0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel Blade で同名 php スクリプトを自動で ViewComposer みたいに適用

Posted at

Smarty を使っているとき、views/home.tpl というテンプレートファイルに対して views/home.php のようなファイルを次のような内容で作成しておくと、自動的にテンプレートのレンダリング前にビューにアサインされた値をちょっと加工できる。

<?php
// ビューにアサインされた変数+DIコンテナに入ってるオブジェクトが引数に渡る
return function ($hoge, Fuga $fuga) {
    // 戻り値は元のビューにアサインされた変数とマージされる
    return [
        'hoge' => $hoge * 2,
        'fuga' => $fuga->func($hoge),
    ];
};

というのを prefilterpostfilter を使ってやっていました。

コントローラーに書くにはビューの都合すぎる、テンプレートに書くにはロジックすぎる、というときに、テンプレートに近い位置に PHP のコードを置くのに重宝していました(最近なら ViewModel とか Presenter とか呼ばれるそれ用のクラスを作ったりするのかもしれない)。

それと似たようなことを Laravel Blade でやる方法。

CompilerEngine を差し替える

下記のような CompilerEngine をアプリで実装して、

<?php
namespace App\Exceptions\View;

use Illuminate\Container\Container;
use Illuminate\View\Compilers\CompilerInterface;

class CompilerEngine extends \Illuminate\View\Engines\CompilerEngine
{
    /**
     * @var Container
     */
    private $container;

    public function __construct(Container $container, CompilerInterface $compiler)
    {
        parent::__construct($compiler);
        $this->container = $container;
    }

    protected function evaluatePath($__path, $__data)
    {
        $path = end($this->lastCompiled);
        if (substr($path, -strlen('.blade.php')) === '.blade.php') {
            $path = substr($path, 0, -strlen('.blade.php')) . '.php';
            if (file_exists($path)) {
                $func = require $path;
                $__data = $this->container->call($func, $__data) + $__data;
            }
        }
        return parent::evaluatePath($__path, $__data);
    }
}

これを適当なサービスプロバイダの boot() で元のものから差し替えます。

<?php
namespace App\Providers;

use App\Exceptions\View\CompilerEngine;
use Illuminate\Container\Container;
use Illuminate\Support\ServiceProvider;

class ViewServiceProvider extends ServiceProvider
{
    public function boot(Container $container)
    {
        $container->get('view.engine.resolver')->register('blade', function () use ($container) {
            return new CompilerEngine($container, $this->app['blade.compiler']);
        });
    }
}

views/home.blade.php をレンダリングするときに下記のような views/home.php というファイルがあれば自動で実行されます。

<?php
return function ($hoge) {
    return [
        'hoge' => $hoge * 2,
    ];
};

クロージャーの引数にはビューにアサインされている変数が渡されると同時に Container::call() で実行してるので型宣言を元にコンテナで解決されたオブジェクトが DI されます。

ViewComposer を使う

Laravel Blade にはもともと ViewComposer という似たような機能が既にありますが・・テンプレートとセットで書きたいので、なるべくテンプレートと近い位置に配置できるように ViewComposer で処理します。

適当なサービスプロバイダで次のように全てのViewに適用される composer を設定します(いわゆる composer コマンドとは関係ないものなので紛らわしい・・)。

<?php
namespace App\Providers;

use Illuminate\Container\Container;
use Illuminate\Support\Facades\View as ViewFacade;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\View;

class ViewServiceProvider extends ServiceProvider
{
    public function boot(Container $container)
    {
        ViewFacade::composer('*', function (View $view) use ($container) {
            $path = $view->getPath();
            if (substr($path, -strlen('.blade.php')) === '.blade.php') {
                $path = substr($path, 0, -strlen('.blade.php')) . '.php';
                if (file_exists($path)) {
                    $func = require $path;
                    $container->call($func, ['view' => $view]);
                }
            }
        });
    }
}

すると views/home.blade.php に対して下記のような views/home.php ファイルが実行されます。

<?php
use Illuminate\View\View;

return function (View $view) {
    $view->with('hoge', $view['hoge'] * 2);
};

勿論このクロージャーも Container::call() で実行されるので型宣言を元にコンテナで解決されたオブジェクトが DI されます。

ただ View $view はコンテナから DI されているわけではないので型ではなく引数名で解決されます、なので View $v とかで引数を書くとエラーです。

さいごに

よく考えたら blade 自体がほぼほぼ PHP みたいなもので @php @endphp とかで PHP のコードかけるし PhpStorm もその中身を PHP として解釈してくれるので blade テンプレートの一番上の方でそういうの書くだけで良いのでは?

と思ったのでこの案はお蔵入りかも。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?