Get "PHP 8 in a Nuthshell" (Now with PHP 8.4)
Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

Define Observers and Global Scopes using PHP's Attributes in Laravel

PHP 8’s Attributes are a great way to add metadata to your code. They are a form of structured, syntactic metadata that can be added to classes, methods, functions, and properties.

Attributes are great when you want to attach data to your classes that can be seen “at a glance” while still being able to access them programmatically.

You can read more about PHP 8’s Attributes in this article where I explained their inner workings and how you can use them in your code.

There are a couple of new features in Laravel 10.x now make use of this feature.

These are:

  • To define observers for Eloquent models.
  • To define global scopes for Eloquent models.

In this article, we’ll see how we can define these using PHP’s Attributes in Laravel.

Defining Observers using Attributes

Observers in Laravel are classes that listen for specific events on Eloquent models. They are used to group all the event listeners for a model into a single class.

Traditionally, you would define a model observer like so.

namespace App\Observers;

class UserObserver
{
    public function creating(User $user)
    {
        //
    }
}

And then you would attach this observer to the model like so.

use App\Observers\UserObserver;

class User extends Model
{
    public function boot(): void
    {
        User::observe(UserObserver::class);
    }
}

But with PHP 8’s Attributes, you can define the observer like so.

use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use App\Observers\UserObserver;

#[ObservedBy(UserObserver::class)]
class User extends Model
{
    //
}

As you can tell, Laravel provides an ObservedBy attribute that you can use to define the observer for the model. You can pass the observer class to the ObservedBy attribute.

So, now you don’t need to define the boot method in the model to attach the observer. You can do it directly using the ObservedBy attribute.

Defining Global Scopes using Attributes

Global scopes allow you to add constraints to all queries for a given model.

Traditionally, you would define a global scope like so.

namespace App\Scopes;

use Illuminate\Database\Eloquent\Builder;

class AgeScope
{
    public function apply(Builder $builder, $model)
    {
        $builder->where('age', '>', 30);
    }
}

And then you would attach this global scope to the model like so.

use App\Scopes\AgeScope;

class User extends Model
{
    protected static function booted()
    {
        static::addGlobalScope(new AgeScope);
    }
}

PHP’s Attributes simplify this process.

With PHP 8’s Attributes, you can define the global scope like so.

use Illuminate\Database\Eloquent\Attributes\ScopedBy;
use App\Scopes\AgeScope;

#[ScopedBy(AgeScope::class)]
class User extends Model
{
    //
}

As you can tell, Laravel provides a ScopedBy attribute that you can use to define the global scope for the model. You can pass the global scope class to the ScopedBy attribute.

You can even pass multiple global scopes to the ScopedBy attribute like so.

#[ScopedBy([AgeScope::class, CountryScope::class])]

So, now you don’t need to define the booted method in the model to attach the global scope. You can do it directly using the ScopedBy attribute.

In closing

I think Attributes help reduce a lot of boilerplate code and make the code more readable and maintainable. I love how Laravel is embracing PHP 8’s new features and making the development experience even better.

Learn the fundamentals of PHP 8 (including 8.1, 8.2, 8.3, and 8.4), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

Like this article?

Buy me a coffee

👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.

Comments?