Great question! The core of your issue is about how Eloquent dynamic scopes work in Laravel 12.x.
Why does changing public to protected fix the error?
When you use the #[Scope] attribute in your Eloquent model, Laravel registers that method as a dynamic scope. This means you can call it like a query builder method (e.g., Recipe::filter($filters)), but only if the method is protected.
How does it work?
- Protected methods with the
#[Scope]attribute are automatically registered as dynamic scopes. - Public methods are not registered as dynamic scopes, so calling them statically (
Recipe::filter($filters)) will try to call a non-static method statically, causing the error you saw.
Example from your code:
// This will NOT work for dynamic scopes:
public function filter(Builder $builder, QueryFilter $filters): Builder {
return $filters->apply($builder);
}
// This WILL work:
#[Scope]
protected function filter(Builder $builder, QueryFilter $filters): Builder {
return $filters->apply($builder);
}
Summary
- Use
protectedfor methods you want to register as dynamic scopes with#[Scope]. - Laravel scans for
protectedmethods with the#[Scope]attribute and makes them available as query builder methods. - If you use
public, Laravel doesn't register them as dynamic scopes, so static calls fail.
References
In short:
Change your method to protected and keep the #[Scope] attribute. That will register it as a dynamic scope and allow Recipe::filter($filters) to work as expected!