View Composers

Automatically injecting data into views with View Composers

graph TD A[View Request] --> B{View Composer?} B -->|Yes| C[Execute Composer] B -->|No| D[Render View] C --> E[Inject Data] E --> D F[Closure Composer] --> C G[Class Composer] --> C

View Composers allow you to automatically inject data into views. This pattern helps you centralize data sharing and avoid code repetition in controllers.


Types of View Composers:


1. Closure-based Composers: Simple composers with closure suitable for simple logic.


2. Class-based Composers: Class-based composers suitable for more complex and reusable logic.


Common Uses:


  • Shared data across multiple views (like categories, menus)
  • Navigation menus
  • User settings
  • Statistics and counters
  • Sidebar content
  • Footer data
  • Global variables

Benefits of View Composers:


  • Reduced code duplication in controllers
  • Centralized data sharing
  • Cleaner and more maintainable code
  • Reusable logic
  • Separation of concerns
  • Easier for testing

Examples

Closure-based Composer

<?php

// In AppServiceProvider boot()
use Illuminate\Support\Facades\View;

View::composer('layouts.sidebar', function ($view) {
    $view->with('categories', Category::all());
});

// Multiple views
View::composer(['layouts.sidebar', 'layouts.nav'], function ($view) {
    $view->with('categories', Category::all());
});

// All views
View::composer('*', function ($view) {
    $view->with('currentUser', auth()->user());
});

A simple composer that injects categories into sidebar.

Class-based Composer

<?php

namespace App\View\Composers;

use App\Models\Category;
use Illuminate\View\View;

class CategoryComposer
{
    public function compose(View $view)
    {
        $view->with('categories', Category::all());
    }
}

// Register in AppServiceProvider
View::composer('layouts.sidebar', CategoryComposer::class);

// Or with dependency injection
class CategoryComposer
{
    public function __construct(
        private CategoryRepository $repository
    ) {}
    
    public function compose(View $view)
    {
        $view->with('categories', $this->repository->all());
    }
}

A class-based composer.

Composer with Caching

<?php

namespace App\View\Composers;

use Illuminate\View\View;
use Illuminate\Support\Facades\Cache;

class CategoryComposer
{
    public function compose(View $view)
    {
        $categories = Cache::remember('categories', 3600, function () {
            return Category::all();
        });
        
        $view->with('categories', $categories);
    }
}

Composer with caching for better performance.

Multiple Composers

<?php

// Multiple composers for same view
View::composer('layouts.app', CategoryComposer::class);
View::composer('layouts.app', MenuComposer::class);
View::composer('layouts.app', function ($view) {
    $view->with('siteSettings', config('site'));
});

// All composers execute and merge data

Multiple composers for one view.

Composer Creators

<?php

// View creator (runs before view is created)
View::creator('layouts.app', function ($view) {
    // This runs before view is created
    // Can modify view data before rendering
    $view->with('preloadData', [
        'user' => auth()->user(),
        'settings' => app('settings')
    ]);
});

// Difference:
// Composer: runs after view is created
// Creator: runs before view is created

View creator that runs before view is created.

Conditional Composer

<?php

namespace App\View\Composers;

use Illuminate\View\View;

class AdminComposer
{
    public function compose(View $view)
    {
        if (auth()->check() && auth()->user()->isAdmin()) {
            $view->with('adminMenu', $this->getAdminMenu());
            $view->with('adminStats', $this->getAdminStats());
        }
    }
    
    private function getAdminMenu()
    {
        return [
            'users' => route('admin.users.index'),
            'posts' => route('admin.posts.index'),
        ];
    }
}

Composer with conditional logic.

Composer with Wildcards

<?php

// All views in users directory
View::composer('users.*', function ($view) {
    $view->with('userCount', User::count());
});

// All views
View::composer('*', function ($view) {
    $view->with('appName', config('app.name'));
});

// Multiple patterns
View::composer(['users.*', 'admin.*'], function ($view) {
    $view->with('isAdminArea', true);
});

Using wildcards to match multiple views.

Testing Composers

<?php

namespace Tests\Unit;

use App\View\Composers\CategoryComposer;
use App\Models\Category;
use Illuminate\View\View;
use Tests\TestCase;

class CategoryComposerTest extends TestCase
{
    public function test_composer_injects_categories()
    {
        Category::factory()->count(3)->create();
        
        $view = $this->view('layouts.sidebar');
        $composer = new CategoryComposer();
        $composer->compose($view);
        
        $this->assertArrayHasKey('categories', $view->getData());
        $this->assertCount(3, $view->getData()['categories']);
    }
}

Testing View Composers.

Use Cases

  • Shared data across multiple views
  • Navigation menus and sidebar content
  • Global variables and settings
  • User-specific data
  • Statistics and counters
  • Footer and header data

Common Mistakes

  • Using composer for data only needed in one view
  • Heavy operations in composer causing slow rendering
  • Not using caching for expensive queries
  • Composer for all views causing overhead
  • Forgetting to register composer
  • N+1 queries in composer

Best Practices

  • Use composer for shared data
  • Use caching for expensive queries
  • Use eager loading for relationships
  • Register composer in Service Provider
  • Use class-based composer for complex logic
  • Test composer
  • Use wildcards with caution

Edge Cases

  • Multiple composers for one view
  • Composer with circular dependencies
  • Composer in API responses
  • Composer with very large datasets
  • Conditional composers
  • Composer with async operations

Performance Notes

  • Composer overhead is usually low
  • Using caching for expensive queries is important
  • Composer for all views can have overhead
  • Use eager loading for relationships
  • Composer caching can improve performance

Security Notes

  • Ensure sensitive data is not exposed in composer
  • Use authorization checks in composer
  • Ensure user input is validated

Interview Points

  • What is the difference between composer and creator?
  • When should you use View Composer?
  • What is the difference between closure-based and class-based composer?
  • How can you test composer?
  • What are the benefits of using View Composers?
  • How can you register composer for multiple views?

Version Notes

  • Laravel 11.x: Improved performance in composer execution
  • Laravel 11.x: Better support for composer caching
  • Laravel 10.x: Improved composer registration
  • Laravel 9.x: Improved composer creators