Poznajemy teorię Observers w Laravelu. Lekcja łatwa i przyjemna a observers przydadzą nam się w następnych lekcjach. Do dzieła.

Ok, model observer tworzymy komendą:

php artisan make:observer UserObserver --model=User

Wynik to coś takiego:

<?php
 
namespace App\Observers;
 
use App\Models\User;
 
class UserObserver
{
    /**
     * Handle the User "created" event.
     */
    public function created(User $user): void
    {
        // ...
    }
 
    /**
     * Handle the User "updated" event.
     */
    public function updated(User $user): void
    {
        // ...
    }
 
    /**
     * Handle the User "deleted" event.
     */
    public function deleted(User $user): void
    {
        // ...
    }
 
    /**
     * Handle the User "restored" event.
     */
    public function restored(User $user): void
    {
        // ...
    }
 
    /**
     * Handle the User "forceDeleted" event.
     */
    public function forceDeleted(User $user): void
    {
        // ...
    }
}

Nazwy metod powinny nam mówić, kiedy będą one wywoływane. Natomiast wewnątrz nich piszemy to, co chcemy uzyskać przy danym evencie.

Mała uwaga – fasada DB może nie triggerować tych eventów. One działają, gdy wykonujemy na modelu akcje Eloquentem.

Oczywiście jeżeli robimy coś koniecznie fasadą DB i chcemy jednocześnie odpalić event observera, to wiedząc co nieco o eventach możemy użyć takiego kodu:

$u = App\User::find(1);
event('eloquent.creating: App\User', $u);

Nie sprawdzałem, ale piszą że działa. Czym są eventy też jeszcze sobie powiemy. Na razie wiemy tyle, że observer tworzy nam eventy i dispatch tych eventów wywoływany przez odpowiadające im metody eloquenta, a co ma na te eventy się dziać, to określamy w metodach observera.

Wiemy też, że jeżeli używamy fasady DB zamiast Eloquenta, to musimy pogodzić się z tym, że event observera nie ruszy, ewentualnie samemu zrobić dispatch eventu będąc świadomym użytkownikiem frameworka.

Oczywiście tak jak global scope tak i Observer musimy podpiąć do modelu:

use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
 
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
    //
}

I tak jak w global scope istnieje też starsza metoda:

use App\Models\User;
use App\Observers\UserObserver;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    User::observe(UserObserver::class);
}

No w global scope po staremu robiliśmy w booted, zaś Observer po staremu to podpinaliśmy w AppServiceProviderze. Tak czy inaczej warto pisać kod „po nowemu” bo jest bardziej czytelny i mniej nam kod ucieka, mniej „obiektowego goto” jak to mówię.

Eventów jeszcze nie umiemy tworzyć sami, ale wiemy, że observer korzysta z nich pod spodem i jeżeli programowaliśmy 3 dni w JavaScript to powinniśmy znać temat konceptualnie.

I wiedząc, że observer korzysta z eventów w dokumentacji możemy dokopać się do tego jak korzystać z modelu bez wywoływania eventów (w tym tych observera):

use App\Models\User;
 
$user = User::withoutEvents(function () {
    User::findOrFail(1)->delete();
 
    return User::find(2);
});

Mamy też kilka „cichych” metod:

$user->saveQuietly();
$user->deleteQuietly();
$user->forceDeleteQuietly();
$user->restoreQuietly();

Inny sposób na to, aby ominąć observer to użyć fasady DB zamiast Eloquenta. Choć chęć ominięcia observera nie powinna być powodem, dla którego to robimy, od tego mamy withoutEvents, ale powinniśmy mieć świadomość że tak to działa.