Спостерігачі моделей в Laravel

Спостерігачі моделей в Laravel

Відразу до практики - подивимося де і як можна використовувати спостерігачів.

Отже, припустимо у нас є модель категорій з декількома властивостями:

  • назвою title
  • так званим slug - псевдонімом для url, тобто адреса у браузері буде виглядати як mysite.com/categories/laravel, де laravel - і є той самий псевдонім
  • булевим published, яке вказує, чи опублікована дана категорія

Якщо всі поля прописані у формах створення нової та оновлення існуючої категорій, то відповідні методи в моєму контролері виглядали б приблизно так:

class CategoryController extends Controller
{
    public function store(Request $request)
    {
        $category = Category::create($request->only($this->getEditableColumns()));
        ...
    }

    public function update(Request $request, Category $category)
    {
        $category->update($request->only($this->getEditableColumns()));
        ...
    }

    protected function getEditableColumns()
    {
        return ['title', 'slug', 'published', 'sort_order'];
    }
}

(валідацію в даному випадку опускаю, ну а якщо є бажання, на цю тему є окрема стаття

Та річ в тому, що slug - це всього-навсього приведена до нижнього регістру назву категорії, в якій підчищені "ліві" символи, а пробіли та підкреслення замінені дефісами. Тому не бачу сенсу додавати в форму поле пседовніма, і тим більше, змушувати користувачів думати, як його заповнювати. І все ж якимось чином псевдоніму треба присвоїти значення - ось і проблема, яку допоможуть вирішити спостерігачі.

Для початку за допомогою консолі створимо спостерігача і відразу зазначимо модель, події якої треба прослуховувати:

$ php artisan make:observer CategoryObserver --model=Category

 

Результатом є каталог project/app/Observers, в якому розміщений файл CategoryObserver.php з потрібним класом. Тепер ми хочемо присвоїти значення псевдоніму до того, как модель будет сохранена или обновлена. як модель буде збережена або оновлена. Можна, звичайно, було б відразу тупо прописати методи creating() і updating(). Але ми ж люди розумні, а тому заглянемо в доки, де зможемо прочитати, що як у випадку збереження, так і в разі поновлення будуть ініційовані події saving/saved. Нас цікавить тільки перша. Редагуємо код створеного файлу:

<?php

namespace App\Observers;

use App\Category;

class CategoryObserver
{
    public function saving(Category $category)
    {
        $category->slug = str_slug($category->title);
    }
}

 

Нагадаю, str_slug() - це функція-хелпер в Laravel, яка з рядка робить пседовнім. Причому не проблема, якщо рядок буде українською - кирилиця буде перетворена на латиницю.

Залишилося зареєструвати спостерігача. Для цього можна створити окремого постачальника послуг, але зараз обійдемося без нього - пропишемо необхідний код у AppServiceProvider:

<?php

namespace App\Providers;

use App\Category;
use App\Observers\CategoryObserver;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Category::observe(CategoryObserver::class);
    }

    ...
}

 

Бум! Проблема вирішена.

Однак рано говорити стоп, зачепимо ще поле published. Припустимо, що це чекбокс, а як ми знаємо, якщо чекбокс не зазначено, то і у реквесті його не буде. Іншими словами, якщо категорія опублікована, а ми захочемо зняти її з публікації і під час редагування форми приберемо галочку з чекбокса - поле в базі даних не оновиться. Є різні методи боротьби з цим явищем, як, наприклад, створення в формі прихованого поля і т.д. Але оскільки у нас вже є спостерігач, нехай попрацює і тут. Повернемося до методу saving(), і хоча "кожен такий метод отримує модель в якості єдиного аргументу", нас це не злякає - дістанемо реквест і там:

<?php

namespace App\Observers;

use App\Category;

class CategoryObserver
{
    public function saving(Category $category)
    {
        $category->slug = str_slug($category->title);
        $category->live = request()->published ? true : false;
    }
}

 

Готово.

Так, приклад простий, але все ж - мінімум коду - і все працює, як годинник. Лаконічно і зрозуміло, як і завжди в Laravel.