php-tenant/Exceptions/EntitlementException.php
Claude 2601392b8d
refactor: create EntitlementException hierarchy with subtypes
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>
2026-03-24 13:30:48 +00:00

62 lines
1.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Tenant\Exceptions;
use Exception;
/**
* Base exception for entitlement-related errors.
*
* This is the root of the entitlement exception hierarchy. Consumers can catch
* this class to handle all entitlement errors, or catch specific subtypes for
* fine-grained error handling.
*
* Exception hierarchy:
* - EntitlementException (base)
* - LimitExceededException -- feature usage limit has been exceeded
* - PackageNotFoundException -- referenced package code does not exist
* - FeatureNotFoundException -- referenced feature code does not exist
* - PackageSuspendedException -- workspace packages are suspended
*
* @see LimitExceededException
* @see PackageNotFoundException
* @see FeatureNotFoundException
* @see PackageSuspendedException
*/
class EntitlementException extends Exception
{
public function __construct(
string $message = 'You have reached your limit for this feature.',
public readonly ?string $featureCode = null,
int $code = 403,
?\Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
/**
* Get the feature code that was denied.
*/
public function getFeatureCode(): ?string
{
return $this->featureCode;
}
/**
* Render the exception as an HTTP response.
*/
public function render($request)
{
if ($request->expectsJson()) {
return response()->json([
'message' => $this->getMessage(),
'feature_code' => $this->featureCode,
], $this->getCode());
}
return redirect()->back()
->with('error', $this->getMessage());
}
}