Laravel advanced: Tappable scopes
Laravel has lesser known features such as Tappable scopes, which basically defines Eloquent scopes in a separate PHP-class.
What I am going to demonstrate isn’t defined anywhere at the official documentation website.
Typical example of defining a local scope
While we normally define scopes by adding a method under a specific model.
<?php
class GuineaPig {
pubic function scopeIsHungry(Builder $builder) {
$builder->where("last_meal_at", '>', now()->subMinutes());
}
}
$query = GuineaPig::query()->isHungry()->get(); // OR
$query = GuineaPig::query()->scopes(['isHungry'])->get();
We can see the presence of a publicly accessible method called scopeIsHungry
. To utilize this scope, we would write code in the following manner.
This feature proves to be helpful; however, it can lead to subpar code suggestions from the editor. It’s important to keep that in mind, because without code suggestions it’s up to the user to review and determine breaking changes.
- Editors like PhpStorm won’t detect breaking name changes, when renaming the method
scopeIsHungry
. - Some editors may express frustration about the unused method
scopeIsHungry()
. Caution is advised when removing that method, as it may affect a large amount of code. - Once you use the
scopeIsHungry()
method, it returns zero for auto-completion. This means that there are no suggestions or options available for further completion.
Alternative solution: tappable scopes
The better solution is moving the code to its own invokable PHP-class.
<?php
class IsHungryTap {
public function __invoke(Builder $builder): void
{
// https://ownyourpet.com/why-are-guinea-pigs-always-hungry/
$builder->where("last_meal_at", '>', now()->subMinutes(15));
}
}
And it can easily be implemented by using the method tap when using a builder.
<?php
$query = GuineaPig->query()->tap(new IsHungryTap())->get();
Compared to the original example, you get decent code-hinting by your editor when you search for usages.
Summary:
- Find usages of
IsHungryTap
- Better code-hinting inside your code editor
- Isolate the local scope to its own PHP-class, to make the model less bloated