DEV Community

Cover image for Understanding Laravel Model Observer, and Testing Using the Underlying Model
Bolaji Ajani
Bolaji Ajani

Posted on

Understanding Laravel Model Observer, and Testing Using the Underlying Model

In my journey with Laravel, one feature that consistently proves its value is the Model Observer. These observers are not just a neat organizational tool; they are a robust mechanism for reacting to various Eloquent model events – like creating, updating, or deleting models. Just recently, while working on a model with a conditional nullable column, Laravel Observers again demonstrated their efficiency. Let's delve into what Laravel Model Observers are and how you can leverage them effectively in your projects.

What is a Laravel Model Observer?

In Laravel, a Model Observer is a class where you can group listener methods for various Eloquent model events. This pattern, derived from the Observer design pattern, is particularly useful for keeping your model-related logic clean and manageable. It lets you handle model events such as creating, updating, deleting, etc., in a dedicated class, ensuring that your model classes remain lean and focused on their primary responsibilities.

Creating and Registering Observers:

Creating a Laravel Observer is straightforward. Use the artisan command to generate an observer class, ideally naming it after the model it observes. For instance, to create an observer for an Addon model, you would use:

php artisan make:observer AddonObserver --model=Addon
Enter fullscreen mode Exit fullscreen mode

This command scaffolds an observer class in the app\Observers directory. Next, you need to register this observer with the model. This can be done in the boot method of the App\Providers\EventServiceProvider or any custom service provider like so:

use App\Models\Addon;
use App\Observers\AddonObserver;

public function boot(): void
{
    Addon::observe(AddonObserver::class);
}
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can list your observer within the $observers property of the EventServiceProvider:

protected $observers = [
    Addon::class => [AddonObserver::class],
];
Enter fullscreen mode Exit fullscreen mode

Practical Example: Ensuring Data Integrity

Consider an Addon model with varying types, where the one-time type does not require a usable_count. To ensure data integrity and prevent potential issues, we can implement a check in the observer's creating method:

namespace App\Observers;

use App\Models\Addon;
use Exception;

class AddonObserver
{
    public function creating(Addon $addon): void
    {
        if ($addon->type !== Addon::TYPE_ONE_TIME && is_null($addon->usable_count)) {
            throw new Exception(Addon::ADDON_TYPE_REQUIRES_USABLE_COUNT);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This code throws an exception if an Addon of a certain type is created without the necessary usable_count, thereby safeguarding the application from incorrect data entries.

Testing Observers with Pest PHP

The robustness of Laravel applications often hinges on the effectiveness of our Observers. Testing these Observers thoroughly is crucial, and Pest PHP provides an expressive and elegant framework for this purpose. Let's explore a comprehensive test case that not only tests the Observer but also validates the behavior of the underlying model.

use App\Models\Addon;
use Exception;

test("ensuring addon observer integrity with model testing", function () {
    // Scenario: Creating an Addon without a required usable_count
    $this->expectException(Exception::class);
    $this->expectExceptionMessage(Addon::ADDON_TYPE_REQUIRES_USABLE_COUNT);

    Addon::factory()->create([
        'type' => Addon::TYPE_LIMITED, // This type requires usable_count
        'usable_count' => null         // Omitting usable_count
    ]);

    // Scenario: Successfully creating an Addon without needing a usable_count
    $addon = Addon::factory()->create([
        'type' => Addon::TYPE_ONE_TIME, // This type does not require usable_count
    ]);

    expect($addon)->toBeInstanceOf(Addon::class);
    expect($addon->usable_count)->toBeNull();
});
Enter fullscreen mode Exit fullscreen mode

This test effectively checks the Observer's functionality by interacting directly with the Addon model, ensuring that the Observer enforces the necessary business rules.

Conclusion:

Laravel Model Observers provide a structured way to handle model events and keep your models focused on their core responsibilities. Coupled with rigorous testing, Observers can significantly enhance the reliability and maintainability of your Laravel projects. You might want to visit the official Laravel documentation to read more about what you can do with observers.

Top comments (0)