سلام دوستان. توی هر برنامه‌ای View هایی وجود داره که همگانی هستن. برای مثال هدر و فوتر برنامه که ما اونها رو توی هر صفحه‌ای داریم و معمولاً اطلاعاتی رو برای نمایش دادن به اونها پاس می‌دیم. مثلاً می‌خوایم توی هدر دسته‌بندی‌های مقالات رو نمایش بدیم.

فایل header.blade.php رو در نظر بگیرید که داریم تو اون دسته‌بندی‌ها رو نمایش می‌دیم:

<ul>
  @foreach($categories as $category)
    <li>{{ $category->title }}</li>
  @endforeach
</ul>

فایل Master زیر رو در نظر بگیرید ما توی خط ۵ هدر رو include کردیم:

<!doctype html>
<html>
<body>
    <div id="app">
        @include('shared.header')
        <main>
            @yield('content')
        </main>
    </div>
</body>
</html>

حالا هر زمانی که داریم یک ویو رو نشون می‌دیم، باید دسته‌بندی‌ها رو بصورت زیر به ویو پاس بدیم:

Route::get('/', function() {
    return view('home', ['categories' => Categories::all()] );
});

Route::get('/posts', function() {
    return view('posts', ['categories' => Categories::all()] );
});

Route::get('/contact', function() {
    return view('contact', ['categories' => Categories::all()] );
});

حالا تصور کنید که برنامه بزرگ‌تر میشه و همچنین اطلاعات دیگه‌ای هم باید توی هدر و فوتر نمایش داده بشن. این کار اصلاً جالب نیست که توی هر return view() که داریم اطلاعات همگانی رو پاس بدیم. لاراول فریم‌ورکی هست که معمولاً فکر این چیزا رو کرده و یک ویژگی به اسم View Composer رو در اختیار ما قرار گذاشته 😉

 

View Composer چیه؟ 🤔

ما می‌تونیم یک کلاس یا یک تابع Callback رو به فریم‌ورک شناسایی کنیم و مشخص کنیم زمانی برای ما اجرا بشن که وقتی یک View خاص در حال رندر شدن هست. به این کلاس یا تابع می‌‌گن ویوکامپوزر (View Composer).

 

نوشتن یک ویوکامپوزر

ویوکامپوزرها معمولاً توی Service Provider ها تعریف میشن. بهتره که برای این کار یک پرووایدر (Provider) اختصاصی درست کنیم. دستور زیر رو توی خط فرمان اجرا می‌کنیم:

php artisan make:provider ViewServiceProvider

این پرووایدر توی مسیر app/Providers برامون ساخته میشه.

مرحله بعد باید این پرووایدر رو به فریم‌ورک شناسایی کنیم. توی فایل config/app.php و در قسمت providers خط ۵ کد زیر رو اضافه می‌کنیم:

// ...

'providers' => [
    // ...
    \App\Providers\ViewServiceProvider::class,
],

حالا موقع نوشتن یک ویوکامپوزر هست. کلاس ViewServiceProvider رو باز می‌کنیم و توی متد boot با استفاده از متد composer از کلاس View یک ویوکامپوزر تعریف می‌کنیم:

<?php

namespace App\Providers;

use App\Models\Category;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ViewServiceProvider extends ServiceProvider
{
    // ...

    public function boot()
    {
        View::composer('shared.header', function($view) {
            $categories = Category::all();

            $view->with([
                'categories' => $categories,
            ]);
        });
    }
}

همونطور که می‌بینیم به متد composer دو تا آرگومان پاس دادیم. آرگومان اول برای مشخص کردن Viewـی هست که می‌خوایم بهش اطلاعات پاس بدیم و آرگومان دوم یک تابع که همون ویوکامپوزر ما هست. این تابع یک پارامتر داره به اسم $view. مقدار این پارامتر همون ویو مد نظر ما (shared.header) هست که به اون می‌تونیم مثل خط ۱۸ اطلاعات پاس بدیم.

از حالا به بعد متغیر $categories همیشه توی shared.header در دسترس هست و لازم نیست که توی هر return view() که توی برنامه داریم، اون رو پاس بدیم 👌

این امکان وجود داره که یک ویوکامپوزر رو برای چند View در نظر بگیریم:

public function boot()
{
    View::composer(['shared.header', 'shared.footer'], ... );
}

و یا از علامت * استفاده کنیم تا ویوکامپوزر برای همه View ها در نظر گرفته بشه:

public function boot()
{
    View::composer('*', ... );
}

 

توی کد بالا، ویوکامپوزر یک تابع Callback بود. از معایب استفاده از این توابع این هست که با اضافه شدن چند ویوکامپوزر دیگه، متد boot پرووایدر شلوغ میشه. یک راه بهتر، استفاده از کلاس‌های مجزا برای ویوکامپوزر هست. یعنی به این صورت:

public function boot()
{
    View::composer('shared.header', HeaderComposer::class);
    View::composer('shared.footer', FooterComposer::class);
    View::composer('shared.sidebar', SidebarComposer::class);
}

همونطور که می‌بینیم، کدِ هر قسمت رو منتقل کردیم به یک کلاس جدا. حالا باید این کلاس‌ها رو بسازیم.

دستور آرتیزان برای این کار وجود نداره، پس دستی این کار رو انجام می‌دیم. ابتدا مسیر app/Http/View/Composers رو درست می‌کنیم تا کلاس‌هامون رو اونجا قرار بدیم.

مرحله آخر ساختن کلاس‌هاست. توی همین مسیر یک فایل به اسم HeaderComposer.php درست می‌کنیم با محتویات زیر:

<?php

namespace App\Http\View\Composers;

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

class HeaderComposer
{
    public function compose(View $view)
    {
        $categories = Category::all();

        $view->with([
            'categories' => $categories,
        ]);
    }
}

همین! یادتون باشه که namespace این کلاس رو توی پرووایدر use کنید.

 

این کلاس‌ها توسط Service Container لود میشن و این امکان وجود داره که وابستگی‌ها رو به صورت type-hint به کلاس اضافه کنیم. برای این کار می‌تونیم کلاس رو بصورت زیر باز نویسی کنیم:

<?php

namespace App\Http\View\Composers;

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

class HeaderComposer
{
    protected $categories;

    public function __construct(CategoryRepository $categories)
    {
        // Dependencies are automatically resolved by the service container...
        $this->categories = $categories;
    }

    public function compose(View $view)
    {
        $view->with([
            'categories' => $this->categories->all(),
        ]);
    }
}

 

خب دوستان این پست هم به پایان رسید. خوشحال میشم نظراتتون رو بدونم. مرسی از وقتی که گذاشتید 😉✌️