Использование View Composers в Laravel

Использование View Composers в Laravel

При разработке сайтов мы сталкиваемся с ситуациями, когда некие данные являются общими для для многих или вообще всех страниц. Простейшие примеры – навигация в хэдере или футере, блок с популярными статьями и т.д. Именно для таких случаев в Laravel предусмотрено элегантное решение – View Composers. Давайте посмотрим, как с ним работать.

Допустим, пункты меню хранятся в базе данных. Соответственно, каждый раз, когда рендерится какая-либо страница, нам надо передать эти данные в представление. В первую очередь вынесем 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 и добавляем следующее (исходим из предположения, что у нас имеется модель 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()
    {
        //
    }
}

 

Просто и красиво. Напоследок скажу, что лично я редко пользуюсь колбэками, поскольку не всегда мы только "достаём" данные - порой требуются различные проверки. И, если в поставщике мы передаём данные в различные вью, его код быстро разрастается, становясь менее читабельным. Поэтому предпочитаю использовать классы.