feat: Add #[Scope] and #[ScopedBy] attributes for declarative scope registration
#327
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Ports Laravel's
#[Scope]and#[ScopedBy]attributes to Hypervel, enabling declarative scope registration on models.Summary
This PR ports two PHP attributes from Laravel that allow models to declare their scopes declaratively rather than imperatively:
#[ScopedBy]- Class-level attribute for global scopes (auto-applied to all queries)#[Scope]- Method-level attribute for local scopes (withoutscopeprefix)Features
ScopedBy Attribute (Global Scopes)
Declare global scopes that are automatically registered when the model boots:
Trait support - Scopes declared on traits are inherited by models using them:
Inheritance - Child models inherit scopes from parent classes. Resolution order:
parent class -> traits -> classScope Attribute (Local Scopes)
Mark methods as local scopes without the traditional
scopeprefix:This provides a cleaner alternative to the
scopeVerified()naming convention.Changes
New Files
Attributes/ScopedBy.phpAttributes/Scope.phpConcerns/HasGlobalScopes.phpConcerns/HasLocalScopes.phphasNamedScope()/callNamedScope()Modified Files
Model.php__callStatic()for scope detectionBuilder.php__call()to usehasNamedScope()Relations/Pivot.phpHasGlobalScopestraitRelations/MorphPivot.phpHasGlobalScopestraitImplementation Details
HasGlobalScopes Trait
bootHasGlobalScopes()- Called during model boot, registers attribute-declared scopesresolveGlobalScopeAttributes()- Collects#[ScopedBy]from class, traits, and parentsaddGlobalScopes(array $scopes)- Batch registration helperaddGlobalScope()- Extended to support class-string (Laravel compatibility)HasLocalScopes Trait
hasNamedScope(string $scope)- Checks forscopeprefix OR#[Scope]attributecallNamedScope(string $scope, array $params)- Invokes the scope methodisScopeMethodWithAttribute(string $method)- Reflection check for attributeBuilder Changes
The
__call()method now useshasNamedScope()instead of directly checking forscopeprefix, enabling both traditional and attribute-based scopes to work seamlessly.Testing
HasGlobalScopes(attribute resolution, inheritance, traits, Pivot models)HasLocalScopes(scope detection, invocation, parameters, inheritance)All tests passing with PHPStan and PHP-CS-Fixer checks clean.
Laravel Parity
This implementation follows Laravel's approach with some improvements:
static::$globalScopesGlobalScope::$container(Hyperf)