淺談Laravel 的 ViewComposer應用

淺談Laravel 的 ViewComposer應用

時機

在Laravel內的ViewComposer可以用來處理不同畫面內用到相同參數的情況。舉例來說,現在有一個購物網站,網站內的某個店家可能會有許多頁面需要重複用到店家的基本資料,像是[關於我], [店家商品], [店家聯絡資料], [店家…], 一般我們會對每一個頁面(Controller)傳送固定的資料。舉例如下

class StoreController extends controller
{
    public function index()
    {
        $info = Store::find(1);
        //...get some data
        return view('index', compact('info', ...));
    }
    
    public function order()
    {
        $info = Store::find(1);
        //...get some order
        return view('order', compact('info',...));
    }
    
    public funtcion products()
    {
        $info = Store::find(1);
        //...get some products
        return view('products', compact('info', ...))
    }
}

因為在三個頁面內都需要用到info內的資料,所以需要重複寫三次,但是若之後要維護的時候就要一次改三個地方,這種重複的Code通常就是重構的第一目標,這時候就非常適合用ViewComposer處理。

方法

  1. 首先要先新增一個你要使用的Composer。你可以新增在app/Http/ViewComposers內。(沒有的話就自行新增吧!)

    <?php
    
    namespace App\Http\ViewComposers;
    
    use Illuminate\View\View
    use App\Repositories\StoreRepository
    
    class StoreComposer
    {
         /**
         * The store repository implementation.
         *
         * @var StoreRepository
         */
        protected $stores;
    
        /**
         * Create a new store composer.
         *
         * @param  StoreRepository  $stores
         * @return void
         */
        public function __construct(StoreRepository $stores)
        {
            // Dependencies automatically resolved by service container...
            $this->stores = $stores;
        }
    
        /**
         * Bind data to the view.
         *
         * @param  View  $view
         * @return void
         */
        public function compose(View $view)
        {
            $view->with('info', $this->stores->find(1));
        }
    }

    若以上述例子為例,我們綁定了一個參數名稱為info的資料,接下來要處理那些頁面要包含這些資料。

  2. 利用 ServiceProvider註冊。我們可以透過新增ViewComposerServiceProvider來統一管理ViewComposer的內容。

    <?php
    
    namespace App\Providers;
    
    use Illuminate\Support\Facades\View;
    use Illuminate\Support\ServiceProvider;
    
    class ViewComposerServiceProvider extends ServiceProvider
    {
        /**
         * Register bindings in the container.
         *
         * @return void
         */
        public function boot()
        {
            // Using class based composers...
            View::composer(
                'index', 'App\Http\ViewComposers\StoreComposer'
            );
    
            // Using Closure based composers...
            View::composer('dashboard', function ($view) {
                //
            });
        }
    
        /**
         * Register the service provider.
         *
         * @return void
         */
        public function register()
        {
            //
        }
    }

    可以透過View::composer的方式綁定。第一個參數是綁定blade的名稱, 第二個是綁定的內容來源,可以透過classorClosure的方式。

    當然也可以綁定到多個view:

    View::composer(['index', 'orders', 'products'], 'App\Http\ViewComposers\StoreComposer')
  3. 最後就是要在config/app.php內加入到providersarray

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

如以一來就可以在多個View內綁定info這個參數了,所以你可以在index.blade.php, orders.blade.php, products.blade.php內使用{{ $info->xxx}}來取得商店資料內容了。

註:其實這樣會有一個壞處,就是在多人合作時如果同事沒有用過這種方式,他會找不到資料的來源到底是哪裡,因為在Controller明明就沒有傳值過去…所以若要在專案內使用此方法,記得要在View內註解一下囉。