$items */ public function __construct( public array $items, ) {} /** * Get all items as a collection. * * @return Collection */ public function collect(): Collection { return collect($this->items); } /** * Get only single items (not bundles). * * @return Collection */ public function singles(): Collection { return $this->collect()->filter( fn ($item) => $item instanceof ParsedItem ); } /** * Get only bundles. * * @return Collection */ public function bundles(): Collection { return $this->collect()->filter( fn ($item) => $item instanceof BundleItem ); } /** * Check if result contains any bundles. */ public function hasBundles(): bool { return $this->bundles()->isNotEmpty(); } /** * Get all base SKUs (flattened from items and bundles). */ public function getAllBaseSkus(): array { $skus = []; foreach ($this->items as $item) { if ($item instanceof BundleItem) { $skus = array_merge($skus, $item->getBaseSkus()); } else { $skus[] = $item->baseSku; } } return $skus; } /** * Get all bundle hashes (for discount lookup). */ public function getBundleHashes(): array { return $this->bundles() ->map(fn (BundleItem $bundle) => $bundle->hash) ->values() ->all(); } /** * Total count of line items (bundles count as 1). */ public function count(): int { return count($this->items); } /** * Total count of individual products (bundle items expanded). */ public function productCount(): int { $count = 0; foreach ($this->items as $item) { if ($item instanceof BundleItem) { $count += $item->count(); } else { $count++; } } return $count; } /** * Rebuild the compound SKU string. */ public function toString(): string { return implode(',', array_map( fn ($item) => $item->toString(), $this->items )); } public function __toString(): string { return $this->toString(); } }