$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); } public function availability( ?int $availableStock, ?int $remainingUserQuota, ): AvailabilityDecision { $reasons = []; if ($remainingUserQuota !== null && $remainingUserQuota <= 0) { $reasons[] = [ 'code' => 'user_quota_reached', 'message' => self::USER_QUOTA_REACHED_MESSAGE, ]; } if ($availableStock !== null && $availableStock <= 0) { $reasons[] = [ 'code' => 'out_of_stock', 'message' => self::OUT_OF_STOCK_MESSAGE, ]; } return $this->decision( $this->maximumAddableQuantity($availableStock, $remainingUserQuota), $reasons, ); } public function purchaseLimitExceededAvailability( int $maximumQuantity, string $message, ): AvailabilityDecision { $reasons = []; if ($maximumQuantity <= 0) { $reasons[] = [ 'code' => 'user_quota_reached', 'message' => self::USER_QUOTA_REACHED_MESSAGE, ]; } else { $reasons[] = [ 'code' => 'requested_quantity_exceeds_user_quota', 'message' => $message, ]; } return $this->decision($maximumQuantity, $reasons); } /** * @param list $reasons */ private function decision(?int $maximumQuantity, array $reasons): AvailabilityDecision { /** @var list $configuredActions */ $configuredActions = config('catalog.availability.default_actions', []); $allowedActions = collect($configuredActions) ->map(fn (string $action): CatalogAction => CatalogAction::from($action)); foreach ($reasons as $reason) { $policy = $this->policies->resolve($reason['code']); if ($policy['effect'] === AvailabilityEffect::Hide) { return AvailabilityDecision::hidden($reasons); } if ($policy['effect'] === AvailabilityEffect::Restrict) { $deniedActions = $policy['denied_actions']; $allowedActions = $allowedActions->reject( fn (CatalogAction $action): bool => in_array($action, $deniedActions, true), ); } } return AvailabilityDecision::visible( $maximumQuantity, $allowedActions->values()->all(), $reasons, ); } }