$query */ public function apply(Builder $query): Builder { if ($this->policies->resolve('out_of_stock')['effect'] !== AvailabilityEffect::Hide) { return $query; } return $query->where(function (Builder $query): void { $query ->where(fn (Builder $query) => $this->applyStandardItemAvailability($query)) ->orWhere(fn (Builder $query) => $this->applyBundleAvailability($query)); }); } /** @param Builder $query */ private function applyStandardItemAvailability(Builder $query): Builder { return $query ->where('catalog_items.type', CatalogItemType::Standard->value) ->where(function (Builder $query): void { $query ->where('catalog_items.inventory_policy', InventoryPolicy::Unlimited->value) ->orWhereHas( 'inventory', fn (Builder $query): Builder => $query ->whereColumn('inventories.real_stock', '>', 'inventories.reserved_stock'), ) ->orWhereHas( 'variants.inventory', fn (Builder $query): Builder => $query ->whereColumn('inventories.real_stock', '>', 'inventories.reserved_stock'), ); }); } /** @param Builder $query */ private function applyBundleAvailability(Builder $query): Builder { return $query ->where('catalog_items.type', CatalogItemType::Bundle->value) ->whereRaw(<<<'SQL' NOT EXISTS ( SELECT 1 FROM bundle_components AS availability_components INNER JOIN catalog_items AS availability_items ON availability_items.id = availability_components.component_catalog_item_id LEFT JOIN variantes AS availability_variants ON availability_variants.id = availability_components.component_variant_id INNER JOIN inventories AS availability_inventories ON availability_inventories.id = COALESCE( availability_variants.inventory_id, availability_items.inventory_id ) WHERE availability_components.bundle_catalog_item_id = catalog_items.id AND availability_items.inventory_policy = ? GROUP BY availability_inventories.id, availability_inventories.real_stock, availability_inventories.reserved_stock HAVING availability_inventories.real_stock - availability_inventories.reserved_stock < SUM(availability_components.quantity) ) SQL, [InventoryPolicy::Tracked->value]); } }