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
<?php

namespace App\Cells;

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

        return view('cells.sidemenu')
            ->with(compact('menu_items'))
            ->render();
    }
}
```

```php
@inject('MenuCell', 'App\Cells\MenuCell');
{!! $MenuCell->handle(auth()->id()) !!}
```

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

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

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

# laravel-widgets
[arrilot/laravel-widgets](https://github.com/arrilot/laravel-widgets)
同じ名前のライブラリ([rinvex/laravel-widgets](https://github.com/rinvex/laravel-widgets))がありますが今回紹介するのはこちら([arrilot/laravel-widgets](https://github.com/arrilot/laravel-widgets))

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

## ライセンス
ぱっとみ書いてなくて分かりにくいですがcomposer.jsonにMITとあります

## インストール

```
composer require arrilot/laravel-widgets
```

## Widgetクラスを作る

artisanコマンドで生成

```
php artisan make:widget ExampleWidget
```

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

```php: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ファイル

```php:resources/views/widgets/example_widget.blade.php
```

## 呼び出す

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

```php
@widget('exampleWidget')
```

```php
{{ Widget::run('exampleWidget') }}
```
```php
{{ Widget::exampleWidget() }}
```

## 変数を渡す

**configプロパティに渡す**

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

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

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

```php
@widget('exampleWidget', [], 'hoge', 'fuga')
{{ Widget::run('exampleWidget', [], 'hoge', 'fuga') }}
{{ Widget::exampleWidget([], 'hoge', 'fuga') }}
```

```php:app/Widgets/ExampleWidget.php
    public function run($hoge, $fuga)
    {
        //
    }
```

## 依存性の注入

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

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

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


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


### 非同期描画

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

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

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

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

```php:非同期
@asyncWidget('exampleWidget')
{{ AsyncWidget::run('exampleWidget') }}
{{ AsyncWidget::exampleWidget() }}
```
※ajaxで通信されるパラメーターは自動で暗号化されます

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

```php
    public function placeholder()
    {
        return 'Loading ...'; 
    }
```
`placeholder()`メソッドで初期表示の文字列を返すだけ:ok_hand:

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

```php
    public $reloadTimeout = 10;
```

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


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