Nauczymy się tworzyć observer w praktyce, wykorzystamy istniejący projekt i ostatnią migrację, warto przypomnieć sobie lekcję teoretyczną o observerach. Do dzieła.

Ok, przypomnijmy sobie te komendy. Ta sprawdza, czy notatka jest usunięta:

Artisan::command('note-is-trashed {id}', function (int $id) {

    $note = Note::withTrashed()->findOrFail($id);
    $this->comment($note->trashed() ? "true" : "false");
    
});

Ta usuwa notatkę:

Artisan::command('note-delete {id}', function (int $id) {

    $note = Note::withoutTrashed()->findOrFail($id);
    $note->delete();
    $this->comment($note->trashed() ? "true" : "false");
    
});

Ta przywraca notatkę:

Artisan::command('note-restore {id}', function (int $id) {

    $note = Note::withTrashed()->findOrFail($id);
    $note->restore();
    $this->comment($note->trashed() ? "true" : "false");
    
});

Mamy też komendy z fasadą DB. Usuwanie:

Artisan::command('note-delete-db {id}', function (int $id) {

    $affected = DB::table("notes")
    ->where("id", $id)
    ->update(["deleted_at" => \Carbon\Carbon::now()]);

    $this->comment("updated - deleted");
    $this->comment($affected);
    
});

Przywracanie z fasadą DB:

Artisan::command('note-restore-db {id}', function (int $id) {

    $affected = DB::table("notes")
    ->where("id", $id)
    ->update(["deleted_at" => null]);

    $this->comment("updated - restored");
    $this->comment($affected);
    
});

Metody z fasadą DB są o tyle dobre, że nie odpalają eventów observera, to warto pamiętać. Ok, zróbmy observer:

php artisan make:observer NoteObserver --model=Note

Teraz go sobie napiszemy:

<?php

namespace App\Observers;

use App\Models\Note;

class NoteObserver
{
    /**
     * Handle the Note "created" event.
     */
    public function created(Note $note): void
    {
        //
    }

    /**
     * Handle the Note "updated" event.
     */
    public function updated(Note $note): void
    {
  
    }

    /**
     * Handle the Note "deleted" event.
     */
    public function deleted(Note $note): void
    {
        $note->delete_count += 1;
        $note->save();
    }

    /**
     * Handle the Note "restored" event.
     */
    public function restored(Note $note): void
    {
        $note->restore_count += 1;
        $note->save();
    }

    /**
     * Handle the Note "force deleted" event.
     */
    public function forceDeleted(Note $note): void
    {
        //
    }
}

Update count zostawiamy na następną lekcję, na razie tego NIE ruszajmy. Ok, podpinamy observer do modelu przez atrybut:

//(...)
use App\Observers\NoteObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
 
#[ObservedBy([NoteObserver::class])]
#[ScopedBy([HiddenScope::class])]
class Note extends Model
{
    use HasFactory;
    use SoftDeletes;

    protected $fillable = ['title', 'content', ];

    public function author(){
            return $this->belongsTo(Person::class, "person_id");
    }

    public function scopeHiddenOnly(Builder $query): void
    {
        $query->withoutGlobalScope(HiddenScope::class)->where('hidden', true);
    }

}

Zawsze pamiętamy o importach. Ok, teraz możemy usunąć i przywrócić notatkę. Zobaczymy w bazie danych, że delete count i restore count automatycznie nam się ustawia.

Jeżeli użyjemy metod z fasadą DB, nie będzie nam się ustawiać.

Oczywiście możemy napisać komendę, która po cichu usunie notatkę, wtedy observer też się nie odpali:

Artisan::command('note-delete-quiet {id}', function (int $id) {

    $ret_val = Note::withoutEvents(function() use($id){

        $note = Note::withoutTrashed()->findOrFail($id);

        $note->delete();
        
        return "deleted quietly";
    });
    $this->comment($ret_val);
    
});

Teraz notatka zostanie usunięta, ale delete count nie będzie zmieniony, bo eventy observera się nie odpalą.

Update count to temat następnej lekcji, niech to się w głowie ułoży.