View Composers

تزریق خودکار داده به viewها با 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 به شما امکان می‌دهند که به صورت خودکار داده را به viewها تزریق کنید. این الگو به شما کمک می‌کند که data sharing را centralize کنید و از تکرار کد در controllers جلوگیری کنید.


انواع View Composers:


1. Closure-based Composers: Composers ساده با closure که برای logic ساده مناسب هستند.


2. Class-based Composers: Composers مبتنی بر کلاس که برای logic پیچیده‌تر و reusable logic مناسب هستند.


استفاده‌های رایج:


  • داده‌های مشترک در چند view (مثل categories، menus)
  • Navigation menus
  • User settings
  • Statistics و counters
  • Sidebar content
  • Footer data
  • Global variables

مزایای View Composers:


  • کاهش code duplication در controllers
  • Centralized data sharing
  • کد تمیزتر و maintainable‌تر
  • Reusable logic
  • Separation of concerns
  • آسان‌تر برای testing

مثال‌ها

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());
});

یک composer ساده که categories را به 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());
    }
}

یک composer مبتنی بر کلاس.

Composer با 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 با caching برای 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

چند composer برای یک 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 که قبل از ایجاد view اجرا می‌شود.

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 با conditional logic.

Composer با 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);
});

استفاده از wildcards برای matching چند view.

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.

موارد استفاده

  • داده‌های مشترک در چند view
  • Navigation menus و sidebar content
  • Global variables و settings
  • User-specific data
  • Statistics و counters
  • Footer و header data

اشتباهات رایج

  • استفاده از composer برای data که فقط در یک view نیاز است
  • Heavy operations در composer که باعث slow rendering می‌شود
  • عدم استفاده از caching برای expensive queries
  • Composer برای همه views که باعث overhead می‌شود
  • فراموش کردن register کردن composer
  • N+1 queries در composer

بهترین روش‌ها

  • از composer برای shared data استفاده کنید
  • از caching برای expensive queries استفاده کنید
  • از eager loading برای relationships استفاده کنید
  • Composer را در Service Provider register کنید
  • از class-based composer برای complex logic استفاده کنید
  • Composer را test کنید
  • از wildcards با احتیاط استفاده کنید

موارد خاص

  • Multiple composers برای یک view
  • Composer با circular dependencies
  • Composer در API responses
  • Composer با very large datasets
  • Conditional composers
  • Composer با async operations

نکات عملکرد

  • Composer overhead معمولاً کم است
  • استفاده از caching برای expensive queries مهم است
  • Composer برای همه views می‌تواند overhead داشته باشد
  • از eager loading برای relationships استفاده کنید
  • Composer caching می‌تواند performance را بهبود دهد

نکات امنیتی

  • مطمئن شوید که sensitive data در composer expose نمی‌شود
  • از authorization checks در composer استفاده کنید
  • مطمئن شوید که user input validated می‌شود

نکات مصاحبه

  • تفاوت بین composer و creator چیست؟
  • چه زمانی باید از View Composer استفاده کرد؟
  • تفاوت بین closure-based و class-based composer چیست؟
  • چگونه می‌توان composer را test کرد؟
  • مزایای استفاده از View Composers چیست؟
  • چگونه می‌توان composer را برای چند view register کرد؟

یادداشت‌های نسخه

  • Laravel 11.x: بهبود performance در composer execution
  • Laravel 11.x: پشتیبانی بهتر از composer caching
  • Laravel 10.x: بهبود composer registration
  • Laravel 9.x: بهبود composer creators