Laravel Bladeのディレクティブを定義するには?

ショコラ
ショコラ

Laravel Bladeのディレクティブを定義するには?

ディレクティブに渡せるパラメーターは1つ。複数渡すときは配列で渡す。
ディレクティブを定義したら、./artisan view:clear コマンド を実行する。

もっさん先輩
もっさん先輩
./artisan view:clear

手順

Blade のディレクティブを定義する手順

  1. 独自ディレクティブを AppServiceProvider.php の bootメソッド で定義します。

因みに、現在の app/Providers/AppServiceProvider.php です。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
      Blade::directive('exists'     ,fn($var)   => "<?php if (isset($var) && !in_array($var,[null,false,0,'',[]],true)): ?>");
      Blade::directive('set'        ,fn($var)   => "<?php $var ?>");
      Blade::directive('unset'      ,fn($var)   => "<?php unset($var) ?>");
      Blade::directive('extract'    ,fn($var)   => "<?php extract($var) ?>");
      Blade::directive('for_extract',fn($var)   => "<?php \$__LOCALS ??= []; \$__LOCALS[\$loop->depth ?? 0] = get_defined_vars(); \$__currentLoopData = array($var)[0]; \$__env->addLoop(\$__currentLoopData); foreach(\$__currentLoopData as \$__): \$__env->incrementLoopIndices(); \$loop = \$__env->getLastLoop(); extract(\$__,isset(array($var)[1])?EXTR_PREFIX_ALL:EXTR_OVERWRITE,array($var)[1] ?? ''); ?>");
      Blade::directive('endfor_extract',fn()    => "<?php endforeach; \$__env->popLoop(); \$loop = \$__env->getLastLoop(); extract(\$__LOCALS[\$loop->depth ?? 0]); ?>");
      Blade::directive('div'        ,fn($times) => "<?php \$times = $times; if (0 == \$loop->index % $times): ?>");
      Blade::directive('div_close'  ,fn()       => "<?php if (((0 != \$loop->index) && (0 == (\$loop->index + 1) % \$times)) || \$loop->last): ?>");
      Blade::directive('enddiv'     ,fn()       => "<?php endif ?>");
      Blade::directive('checked'    ,fn($var)   => "<?php if ($var) echo 'checked' ?>");
      Blade::directive('selected'   ,fn($var)   => "<?php if ($var) echo 'selected' ?>");
      Blade::directive('last'       ,fn($var)   => "<?php if (\$loop->last) echo $var ?>");
      Blade::directive('script'     ,fn()       => "<?php \$__env->startPush('scripts') ?>");
      Blade::directive('endscript'  ,fn()       => "<?php \$__env->stopPush() ?>");
      Blade::directive('dialog'     ,fn()       => "<?php \$__env->startPush('dialogs') ?>");
      Blade::directive('enddialog'  ,fn()       => "<?php \$__env->stopPush() ?>");
      Blade::directive('parent'     ,fn()       => "window.livewire.all().find(i => -1 != i.__instance.childIds.indexOf('{{ \$_instance->id }}'))");
      Blade::directive('config'     ,fn($var)   => "<?= _config($var) ?>");
      Blade::directive('url'        ,fn($var)   => "<?= _url($var) ?>");
      Blade::directive('action'     ,fn()       => "<?= _url(\Route::currentRouteName()) ?>");
      Blade::directive('hidden'     ,fn($vars)  => "<?php foreach ([$vars] as \$var): ?><input type=\"hidden\" name=\"{{ \$var }}\" value=\"{{ \$\$var }}\"><?php endforeach ?>");
    }
}

配置先:app/Libs/functions.php

@config、@urlディレクティブ を使うには、以下の _config、_url関数が必要です。

<?php
function numf($val):string
{
  return is_numeric($val) ? number_format((float)$val) : '';
}

function formatDate($expr,$format):string
{
  return date($format,strtotime($expr));
}

function headline($str,$width = 20)
{
  $headline = $str;
  if (mb_ereg("(.+?)(。|\n)",$str,$m)) {
    $headline = $m[1];
  }
  return mb_strimwidth($headline,0,$width);
}

function flash($key, $message = null)
{
  if (!is_null($message))
    session()->flash($key,$message);
  return session($key);
}

function render_blade(string $blade)
{
  $tmpl = uniqid();
  $file = resource_path('views/dynamic/'.$tmpl.'.blade.php');
  try {
    file_put_contents($file,$blade);
    return view('dynamic.'.$tmpl)->render();
  }
  finally {
    unlink($file);
  }
}

function _config(string $key, $default = null)
{
  return config($key) ?: env($key) ?: $default;
}

function _url(string $route = '', string $param = '')
{
  if (str_starts_with($route,'/'))
    [$route,$param] = ['',$route.$param];

  static $cache = [];
  if (!isset($cache[$route]))
    if (\Route::has($route))
      $cache[$route] = route($route);
    elseif ('' == $route)
      $cache[$route] = _config('APP_URL','//'.$_SERVER['HTTP_HOST']);
    elseif ('' != ($cache[$route] = _config($route)))
      ;
    else {
      [$route,$param] = ['','/'.$route.$param];
      $cache[$route] = _url($route);
    }

  return $cache[$route].$param;
}

過去に @phpディレクティブ で変数に0を入れていた処理がありましたので、今回 @setディレクティブ を作成してみました。

@set ( $i = 0 )

本来なら↓のように書きます。@php には @endphp が必要で、少し重い感じがします。

@php $i = 0 @endphp
  1. AppServiceProvider.php にディレクティブを定義したら、view:clearコマンド を実行します。
./artisan view:clear

Bladeディレクティブ を更新したら、毎回↑上のコマンドを実行する必要があります。

  1. たったこれだけで、ブレードで独自ディレクティブが使えるようになります。

@set、@unset、@exists の使用例です。

@set ( $i = 0 )
@set ( $i++ )
{{ $i }}

@set ( $j = 100 )
@set ( $j++ )
{{ $i }},{{ $j }}

@set ( $i = 100; $j = 200 )
{{ $i }},{{ $j }}

@exists ($i)
  値があります。
@endif

@unset ($i)
@empty ($i)
  値はありません。
@endif

@div,@div_close,@divend の使用例です。

<table>
@foreach ([0,1,2,3,4,5,6,7,8,9] as $i)
  @div (3)
    {{-- $loop->index が3で割り切れた時に入ります。 --}}
    <tr>
  @enddiv
      <td>{{ $i }} </td>
  @div_close
    {{-- $loop->index + 1 が3で割り切れた時に入ります。 --}}
    </tr>
  @enddiv
@endforeach
</table>

@config,@url の使用例です。

<a href="@url">@config('APP_NAME')</a>
<a href="@url('register')">会員登録(ルートで指定)</a>
<a href="@url('/aaa/bbb/ccc','?a=123')">パスとクエリで指定</a>

以上

Scroll to Top