Використання View Composers в Laravel

Використання View Composers в Laravel

При розробці сайтів ми стикаємося з ситуаціями, коли деякі дані є загальними для багатьох або навіть всіх сторінок. Найпростіші приклади - навігація в хедері або футері, блок з популярними статтями і т.д. Саме для таких випадків в Laravel передбачено елегантне рішення - View Composers. Давайте подивимося, як з ним працювати.

Припустимо, що пункти меню зберігаються у базі даних. Відповідно, кожного разу, коли генерується будь-яка сторінка, нам треба передати ці дані в вид (view). В першу чергу винесемо html-код навігації в окремий файл. Як варіант, створимо в директорії resources/views/layouts піддиректорію partials, у якій створимо файл nav.blade.php – де і розмістимо наше меню. Включимо цей файл в шаблон в такий спосіб::

@include('layouts.partials.nav')

Що стосується передачі даних - є кілька варіантів. Примітивне рішення "в лоб" - просто-напросто дублювати фетчінг даних в кожному контролері, що абсолютно не кошерно. Другий варіант - винаходити якісь свої велосипеди, щоб уникнути дублювання коду. Але навіщо? Адже третій варіант - готове рішення в Laravel, яким і скористаємося.

Створимо постачальника послуг командою у консолі:

php artisan make:provider ComposerServiceProvider

 

І відразу додамо його в масив providers файла конфігурації config/app.php:

...

'providers' => [
    ...
    /*
     * Application Service Providers...
     */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    App\Providers\EventServiceProvider::class,
    App\Providers\RouteServiceProvider::class,

],

...

 

Далі переходимо в створений нами app/Providers/ComposerServiceProvider.php та вставляємо наступне (виходимо з припущення, що у нас в базі даних є таблиця menu_items і відповідна їй модель MenuItem):

<?php

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use App\MenuItem;

class ComposerServiceProvider extends ServiceProvider
{
    public function boot()
    {
        View::composer('layouts.partials.nav', function($view) {
            $view->with(['menuitems' => MenuItem::get()]);
        });
    }

    public function register()
    {
        //
    }
}

 

Прозоріше нікуди, але все ж: перший аргумент - це вид, в який ми передаємо дані, а другий - колбек, в якому ми, власне, і вказуємо, які дані передаємо. Якщо ж потрібно відправити дані в кілька видів, тоді першим аргументом буде масив. Тобто цей код міг би виглядати, наприклад, так:

View::composer(['layouts.partials.nav', 'layouts.partials.footer'], function($view) {
    $view->with(['menuitems' => MenuItem::get()]);
});

 

Якщо ж дані потрібні у всіх в'ю, то перший аргумент буде таким: '*'.

Здавалося б, тут можна було б сказати that’s it, але пропоную піти трохи далі, адже метод composer() може приймати не тілько колбек, а й клас. Давайте подивимося і на цей варіант. В app/Http створимо директорію ViewComposers, і у ній файл NavigationComposer.php з наступним змістом:

<?php

namespace App\Http\ViewComposers;

use App\MenuItem;
use Illuminate\View\View;

class NavigationComposer
{
    public function compose(View $view)
    {
        return $view->with('mainitems', MenuItem::get());
    }
}

 

Оскільки тепер ми використовуємо клас, слід підкоригувати і файл ComposerServiceProvider.php. Ну і щоб показати, що "можна й так", давайте замінимо фасад виду на функцію-хелпер view():

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Http\ViewComposers\NavigationComposer;
use App\MenuItem;

class ComposerServiceProvider extends ServiceProvider
{
    public function boot()
    {
        view()->composer('layouts.partials.nav', NavigationComposer::class);
    }

    public function register()
    {
        //
    }
}

 

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