Help us understand the problem. What is going on with this article?

LaravelのViewに小さなMVCを

こんなことありませんか?

  • ユーザーの権限ごとに違うナビゲーションメニューを表示したい
  • ショッピングカートや通知のアイコン
  • 記事に対するコメント欄
  • パンくずメニュー

このような、どの画面でも使うページ部品を動的に表示したい。メインの処理とは違うデータをControllerで用意してviewに渡さないといけない
そんなときどこに処理を書いていますか?
Viewにロジックをたくさん書く?Controllerに書く?Ajaxでごりごり書く?

:thinking:

RailsやCakePHP3なら...

RailsやCakePHP3にはCellsという、再利用可能なページ部品を作るのに最適な機構があります

Cellはページ部品内で小さなMVCとして動きます
ナビゲーションメニューはControllerとは関係なくひとりでにナビゲーションメニューを構築し、通知のアイコンもひとりでに未読の通知がないか探しに行きます

Laravelは?

Laravelにはそのような機能はありませんが、さすがLaravelです作れます!

  1. Cellに相当するクラスを作り
  2. ServiceProviderでディレクティブを追加 OR @injectでviewに注入する
<?php

namespace App\Cells;

class MenuCell
{
    public function handle($user_id)
    {
        // いい感じにDBからメニューのデータを持ってくる
        $menu_items = [];

        return view('cells.sidemenu')
            ->with(compact('menu_items'))
            ->render();
    }
}
@inject('MenuCell', 'App\Cells\MenuCell');
{!! $MenuCell->handle(auth()->id()) !!}

簡易的ではありますがこんな感じでしょうか

まぁこれでもいいんですが
メニューってキャッシュしたくなりますよね、
メインの記事に対する関連する記事とか非同期にして効率よく描画したい、通知ならポーリングしたり...でもjs書くの面倒:joy:

そんな欲望を満たしてくれるライブラリあります!

laravel-widgets

arrilot/laravel-widgets
同じ名前のライブラリ(rinvex/laravel-widgets)がありますが今回紹介するのはこちら(arrilot/laravel-widgets)

基本的な使い方と非同期、ポーリング、キャッシュがどれだけ簡単にできるかを解説します

ライセンス

ぱっとみ書いてなくて分かりにくいですがcomposer.jsonにMITとあります

インストール

composer require arrilot/laravel-widgets

Widgetクラスを作る

artisanコマンドで生成

php artisan make:widget ExampleWidget

生成されたWidget
run()メソッドで返したviewを呼び出した部分で描画してくれる

app/Widgets/ExampleWidget.php
<?php

namespace App\Widgets;

use Arrilot\Widgets\AbstractWidget;

class ExampleWidget extends AbstractWidget
{
    /**
     * The configuration array.
     *
     * @var array
     */
    protected $config = [];

    /**
     * Treat this method as a controller action.
     * Return view() or other content to display.
     */
    public function run()
    {
        //

        return view('widgets.example_widget', [
            'config' => $this->config,
        ]);
    }
}

一緒に作られるviewファイル

resources/views/widgets/example_widget.blade.php

呼び出す

呼び出し方は3種類あります
どれを選んでもOK

@widget('exampleWidget')
{{ Widget::run('exampleWidget') }}
{{ Widget::exampleWidget() }}

変数を渡す

configプロパティに渡す

@widget('exampleWidget', ['val1' => 'hoge'])
{{ Widget::run('exampleWidget', ['val1' => 'hoge']) }}
{{ Widget::exampleWidget(['val1' => 'hoge']) }}

渡された['val1' => 'hoge']はWidgetから$this->configでアクセスできます

run()メソッドに直接渡す

@widget('exampleWidget', [], 'hoge', 'fuga')
{{ Widget::run('exampleWidget', [], 'hoge', 'fuga') }}
{{ Widget::exampleWidget([], 'hoge', 'fuga') }}
app/Widgets/ExampleWidget.php
    public function run($hoge, $fuga)
    {
        //
    }

依存性の注入

run()メソッドはContainerによる依存性の注入を行えます

ここがおすすめ

ここまでなら最初の簡易Cellと大差ないですね
このライブラリのすごいところはここからです

キャッシュ

データの生成に時間がかかったり変化が少ないものに関しては、パフォーマンスの問題からウィジェットをある程度の時間キャッシュさせたくなります

public $cacheTime = 60;  // 秒単位

方法は$cacheTimeプロパティをつけるだけ:ok_hand:
※キャッシュのキーはウィジェット名とウィジェットパラメータによって作られます

非同期描画

状況によっては、先にメインコンテンツを描画してウィジェット部分はajaxで後から描画させたい場合があると思います

JavaScriptに触れずにそれを実現できます

同期
@widget('exampleWidget')
{{ Widget::run('exampleWidget') }}
{{ Widget::exampleWidget() }}

方法はasyncをつけるだけ:ok_hand:

非同期
@asyncWidget('exampleWidget')
{{ AsyncWidget::run('exampleWidget') }}
{{ AsyncWidget::exampleWidget() }}

※ajaxで通信されるパラメーターは自動で暗号化されます

ajax呼び出しが終わるまでの表示を指定する

    public function placeholder()
    {
        return 'Loading ...'; 
    }

placeholder()メソッドで初期表示の文字列を返すだけ:ok_hand:

ポーリング

数秒ごとに定期的に再読み込みするウィジェットも簡単につくれます

    public $reloadTimeout = 10;

方法は$reloadTimeoutプロパティをつけるだけ:ok_hand:
※リクエストのスパンが短すぎるとサーバへの負荷が膨れ上がるので注意

さいごに

laravel-widgetsを導入することで
1. Controllerがすっきりする
2. Viewもすっきり
3. キャッシュや非同期などパフォーマンス面も良い
4. JavaScriptを触らずに実装できる
などなどとても幸せになれます:innocent:

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away