$catalogItems */ public function attach(Collection $catalogItems, ?int $userId): void { $remaining = $this->purchaseLimits->remainingByCatalogItem($catalogItems, $userId); foreach ($catalogItems as $catalogItem) { $catalogItem->setAttribute( 'remaining_user_quota', $remaining->get($catalogItem->getKey()), ); } } public function maximumAddableQuantity(?int $availableStock, ?int $remainingUserQuota): ?int { if ($availableStock === null) { return $remainingUserQuota; } if ($remainingUserQuota === null) { return $availableStock; } return min($availableStock, $remainingUserQuota); } /** @param Builder $query */ public function applyProductVisibilityPolicy(Builder $query): Builder { return $this->isVisible( 0, null, AvailabilitySubject::Product, ) ? $query : $query->whereAvailableInCatalog(); } public function isVisible( ?int $availableStock, ?int $remainingUserQuota, AvailabilitySubject $subject, ): bool { return $this->availability( $availableStock, $remainingUserQuota, $subject, )['capabilities']['display']; } /** * @return array{ * subject: string, * maximum_quantity: int|null, * restrictions: list, * capabilities: array{display: bool, select_variant: bool, change_quantity: bool, add_to_cart: bool, buy_now: bool} * } */ public function availability( ?int $availableStock, ?int $remainingUserQuota, AvailabilitySubject $subject = AvailabilitySubject::Product, ): array { $restrictions = []; if ($remainingUserQuota !== null && $remainingUserQuota <= 0) { $restrictions[] = [ 'code' => 'user_quota_reached', 'message' => self::USER_QUOTA_REACHED_MESSAGE, ]; } if ($availableStock !== null && $availableStock <= 0) { $restrictions[] = [ 'code' => 'out_of_stock', 'message' => self::OUT_OF_STOCK_MESSAGE, ]; } $capabilities = [ 'display' => true, 'select_variant' => true, 'change_quantity' => true, 'add_to_cart' => true, 'buy_now' => true, ]; foreach ($restrictions as $restriction) { /** @var array $policy */ $policy = config( "catalog.availability_policies.{$restriction['code']}.{$subject->value}", [], ); foreach ($capabilities as $capability => $allowed) { $capabilities[$capability] = $allowed && ($policy[$capability] ?? true); } } return [ 'subject' => $subject->value, 'maximum_quantity' => $this->maximumAddableQuantity( $availableStock, $remainingUserQuota, ), 'restrictions' => $restrictions, 'capabilities' => $capabilities, ]; } /** @return array */ public function purchaseLimitExceededAvailability(int $maximumQuantity, string $message): array { $availability = $this->availability(null, $maximumQuantity); if ($maximumQuantity > 0) { $availability['restrictions'][] = [ 'code' => 'requested_quantity_exceeds_user_quota', 'message' => $message, ]; } return $availability; } }