projectId`), which the enforcer can't see beforehand. The attribute is * then a declared-coverage marker and the method body MUST call * `$this->authorize($perm, $entity->projectId)` to do the precise check. * - none of the above — falls back to the current session project (`session('currentProject')`), * appropriate for session-scoped views. */ #[Attribute(Attribute::TARGET_METHOD)] final class RequiresPermission { /** * @param string $permission The required `domain.action` key (use a domain * permission constant, e.g. `TicketsPermissions::CREATE`). * @param string|null $projectIdParam Name of the request param holding the project id. * @param bool $global Company-wide capability — check the global role, not a project. * @param bool $entityScoped Project is derived from an entity the method loads; the * enforcer defers and the method self-authorizes in its body. */ public function __construct( public readonly string $permission, public readonly ?string $projectIdParam = null, public readonly bool $global = false, public readonly bool $entityScoped = false, ) {} }