Create exception subclasses for fine-grained error handling: - LimitExceededException: feature usage limit exceeded - PackageNotFoundException: package code not found during provisioning - FeatureNotFoundException: feature code not found during checks - PackageSuspendedException: workspace packages suspended Update EntitlementService: - Add canOrFail() and canForNamespaceOrFail() throwing variants - Replace firstOrFail() with explicit PackageNotFoundException in provisioning - Import new exception types, remove unused ModelNotFoundException Update docs/entitlements.md with Exception Hierarchy section, API reference entries for new methods, and updated Best Practices examples. Fixes #19 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
77 lines
2 KiB
PHP
77 lines
2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Core\Tenant\Exceptions;
|
|
|
|
use Core\Tenant\Services\EntitlementResult;
|
|
|
|
/**
|
|
* Exception thrown when a feature usage limit has been exceeded.
|
|
*
|
|
* This exception indicates that the workspace or namespace has consumed
|
|
* all available capacity for a limit-based feature (e.g., pages, API calls).
|
|
*
|
|
* @see EntitlementException Base exception class
|
|
*/
|
|
class LimitExceededException extends EntitlementException
|
|
{
|
|
public function __construct(
|
|
string $message = 'You have reached your limit for this feature.',
|
|
?string $featureCode = null,
|
|
public readonly ?int $limit = null,
|
|
public readonly ?int $used = null,
|
|
int $code = 403,
|
|
?\Throwable $previous = null
|
|
) {
|
|
parent::__construct($message, $featureCode, $code, $previous);
|
|
}
|
|
|
|
/**
|
|
* Create from an EntitlementResult.
|
|
*/
|
|
public static function fromResult(EntitlementResult $result): self
|
|
{
|
|
return new self(
|
|
message: $result->getMessage() ?? 'You have reached your limit for this feature.',
|
|
featureCode: $result->featureCode,
|
|
limit: $result->limit,
|
|
used: $result->used,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the feature limit that was exceeded.
|
|
*/
|
|
public function getLimit(): ?int
|
|
{
|
|
return $this->limit;
|
|
}
|
|
|
|
/**
|
|
* Get the current usage count.
|
|
*/
|
|
public function getUsed(): ?int
|
|
{
|
|
return $this->used;
|
|
}
|
|
|
|
/**
|
|
* Render the exception as an HTTP response.
|
|
*/
|
|
public function render($request)
|
|
{
|
|
if ($request->expectsJson()) {
|
|
return response()->json([
|
|
'message' => $this->getMessage(),
|
|
'error' => 'limit_exceeded',
|
|
'feature_code' => $this->featureCode,
|
|
'limit' => $this->limit,
|
|
'used' => $this->used,
|
|
], $this->getCode());
|
|
}
|
|
|
|
return redirect()->back()
|
|
->with('error', $this->getMessage());
|
|
}
|
|
}
|