Laravel: View::composer
Glossary overview

Laravel: View::composer

View::composer() — механізм Laravel, який автоматично передає дані у певні Blade-шаблони, незалежно від того, який контролер їх рендерить. Він спрацьовує щоразу, коли вказаний view готується до відображення.

Навіщо це потрібно

Типова ситуація: є шаблони layouts.app, layouts.partials.header, layouts.partials.footer — спільний каркас, який використовується на кожній сторінці. Дані для них завжди одні й ті самі (налаштування сайту, пункти меню, кольори, лого), і вони потрібні всюди.

Без View::composer залишається два поганих варіанти:

Варіант 1 — передавати в кожному контролері

class HomeController
{
    public function index() {
        return view('home', [
            'siteName' => SiteSetting::singleton()->site_name,
            'headerMenuItems' => Menu::location('header')?->menuItems ?? collect(),
            'siteColors' => [...],
            'posts' => Post::all(),  // власне дані сторінки
        ]);
    }
}

class AboutController
{
    public function index() {
        return view('about', [
            'siteName' => SiteSetting::singleton()->site_name,  // знову те саме
            'headerMenuItems' => Menu::location('header')?->menuItems ?? collect(),
            // ...знову копіпаста
        ]);
    }
}

Дублювання у кожному контролері; додавання нового поля вимагає правок у десятках місць.

Варіант 2 — глобальні View::share()

View::share('siteName', ...);
View::share('headerMenuItems', ...);

Працює, але дані обчислюються на кожен запит, навіть коли layout не використовується (наприклад, на JSON-API-роутах).

View::composer розвʼязує обидві проблеми

View::composer(
    ['layouts.app', 'layouts.partials.header', 'layouts.partials.footer'],
    function ($view) {
        $view->with('siteName', ...);
        // ...
    }
);

Дані потрапляють у потрібні шаблони автоматично. Контролерам не треба про це думати — вони передають лише сторінкові дані. А якщо запит зовсім не рендерить ці шаблони — composer не виконається, ресурси не витратяться.

Як це працює всередині

Коли викликається view('layouts.app') чи Blade зустрічає @include('layouts.partials.header'):

  1. Laravel створює обʼєкт View для цього шаблону.
  2. Перевіряє, чи є зареєстровані composers для нього.
  3. Виконує колбек composerʼа, передаючи туди $view.
  4. Через $view->with(...) дані прикріплюються до view.
  5. Blade рендерить шаблон, маючи доступ до всіх змінних.

Це відбувається прямо перед рендерингом, на кожен запит, де view використовується.

Розбір реального прикладу

public function boot(): void
{
    View::composer(
        ['layouts.app', 'layouts.partials.header', 'layouts.partials.footer'],
        function ($view): void {
            $siteSettings = SiteSetting::singleton();
            
            $view->with('headerMenuItems', Menu::location('header')?->menuItems ?? collect());
            $view->with('footerMenuItems', Menu::location('footer')?->menuItems ?? collect());
            $view->with('siteName', $siteSettings->site_name ?: config('app.name'));
            $view->with('headerLogoUrl', filled($siteSettings->header_logo_path) 
                ? Storage::disk('public')->url($siteSettings->header_logo_path) 
                : null);
            $view->with('siteColors', [
                'header_bg' => $siteSettings->header_bg_color ?: '#ffffff',
                'footer_bg' => $siteSettings->footer_bg_color ?: '#ffffff',
                'accent' => $siteSettings->accent_color ?: '#18181b',
                // ...
            ]);
        }
    );
}

Composer зареєстрований для трьох шаблонів — основного layoutʼа та двох partials. Усередині дістаються налаштування сайту, підтягуються меню для шапки й футера, формуються URLʼи для лого та фавікона, збирається обʼєкт із кольорами для CSS-стилізації.

Тепер у будь-якому Blade-шаблоні, який extends layouts.app чи включає ці partials, доступні $siteName, $headerMenuItems, $siteColors тощо — без жодних зусиль з боку контролера.

Варіанти синтаксису

1. Closure — найпростіше

View::composer('partials.sidebar', function ($view) {
    $view->with('recentPosts', Post::recent()->take(5)->get());
});

2. Окремий клас — для складнішої логіки

// app/View/Composers/SidebarComposer.php
namespace App\View\Composers;

use App\Repositories\PostRepository;
use Illuminate\View\View;

class SidebarComposer
{
    public function __construct(private PostRepository $posts) {}
    
    public function compose(View $view): void
    {
        $view->with('recentPosts', $this->posts->recent(5));
    }
}

// у ServiceProvider
View::composer('partials.sidebar', SidebarComposer::class);

Перевага класу — інʼєкція залежностей, тестованість, чистіший код, якщо логіки багато.

3. Wildcards — кілька шаблонів за патерном

View::composer('admin.*', AdminMenuComposer::class);
View::composer('*', GlobalDataComposer::class);  // взагалі всі view

Куди реєструвати

Стандартне місце — окремий App\Providers\ViewServiceProvider:

namespace App\Providers;

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

class ViewServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        View::composer(['layouts.app', /* ... */], function ($view) {
            // ...
        });
    }
}

І зареєструвати його в bootstrap/providers.php (Laravel 11+) або config/app.php (старіші версії). Можна писати і в AppServiceProvider, але з ростом проєкту краще виносити в окремий провайдер.

⚠️ Важливі поради

Кешуйте важкі запити

Composer запускається на кожному запиті, де рендериться layout — тобто практично завжди. Якщо налаштування рідко змінюються, кешуйте:

$siteSettings = Cache::rememberForever('site_settings', 
    fn() => SiteSetting::singleton()
);

$headerMenu = Cache::rememberForever('menu.header', 
    fn() => Menu::location('header')?->menuItems ?? collect()
);

І чистіть кеш в observerʼах моделей SiteSetting/Menu при оновленні. Це може суттєво розвантажити БД на трафікових сторінках.

Уникайте composerʼа на «*»

Composer на всі шаблони (View::composer('*', ...)) спрацьовує на кожен @include — а їх у Blade десятки. Завжди вказуйте конкретні шаблони.

Composer ≠ View Component

В Laravel 8+ є Blade Components із власним класом і методом render(). Для невеликих самодостатніх шматків UI (картка користувача, кнопка) краще використовувати компоненти. Composer лишається оптимальним для глобальних даних layoutʼа.

Підсумок

View::composer — це «гачок», який автоматично заливає певні дані у вказані Blade-шаблони, коли вони відображаються. Ідеальний use-case — спільний каркас сайту з меню, лого, кольорами, які не залежать від конкретної сторінки. Це звільняє контролери від рутини і централізує логіку формування layoutʼа в одному місці. Найважливіше — кешувати важкі запити всередині, щоб не перевантажувати БД на кожному запиті.