この記事はLaravel 5.7.19
をもとに書かれています。
要約
- 親テンプレートの中では
@show
を使う - 子の中では
@endsection
を使う
事の始まり
Laravelの公式ドキュメントの日本語訳としてよくお世話になるreadoubleですが、そのBladeテンプレートの項にこんな記述があります。
Tip!! 直前の例とは異なり、このsidebarセクションは@showの代わりに@endsectionで終わっています。@endsectionディレクティブはセクションを定義するだけに対し、@showは定義しつつ、そのセクションを即時にその場所に取り込みます。
なんとなく言っていることが分かるような分からないような…
曖昧なままコードを書くのは火傷の元、ということで今一度ちゃんとこのあたりの整理をしようと思いました。
実験
普通に書いてみる
まずはreadoubleにあるのと同じようなコードを書いてみます。
<html>
<head>
<title>Bladeテスト</title>
</head>
<body>
@section('content')
<p>親の内容</p>
@Show
</body>
</html>
@extends('layouts.app')
@section('content')
@parent
<p>子の内容</p>
@endsection
app.blade.php
でcontent
というセクションを定義し、そこに「親の内容」という文を埋め込んでいます。
一方、child.blade.php
はapp.blade.php
を継承しており、やはりcontent
というセクションに「子の内容」という文を埋め込んでいます。ただし、継承元であるapp.blade.php
にもcontent
セクションが存在するため、その内容に追加する形になります。1
注目すべきなのはセクションの終わりを表すディレクティブです。app.blade.php
では@show
でセクションを閉じていますが、child.blade.php
では@endsection
でセクションを閉じています。
このコードをコンパイルブラウザで表示すると次のようなHTMLが生成されます。
<html>
<head>
<title>Bladeテスト</title>
</head>
<body>
<p>親の内容</p>
<p>子の内容</p>
</body>
</html>
想定通りの出力がされました。
親でも@endsection
を使う
親テンプレートであるapp.blade.php
において、@show
の代わりに@endsection
を使ってみます。
<html>
<head>
<title>Bladeテスト</title>
</head>
<body>
@section('content')
<p>親の内容</p>
@endsection {{--@show->@endsection--}}
</body>
</html>
ページをブラウザで開くと、こんなHTMLが生成されています。
<html>
<head>
<title>Bladeテスト</title>
</head>
<body>
</body>
</html>
セクションの内容が丸々消えてしまいました!
子でも@endsection
を使う
最後に、親テンプレートであるchild.blade.php
において、@endsection
の代わりに@show
を使ってみます。
@extends('layouts.app')
@section('content')
@parent
<p>子の内容</p>
@show {{--@endsection -> @show--}}
ブラウザで見てみましょう。
<html>
<head>
</head>
<body>
<p>子の内容</p>
<title>Bladeテスト</title>
<p>親の内容</p>
<p>子の内容</p>
</body>
</html>
子の内容が2度表示されてしまいます。しかも変なところに<title>
タグが表れてカオス。
実験結果からわかること
どうも@show
がないとそもそもセクションの内容が埋め込まれないようです。これがreadoubleで言うところの「@show
は定義しつつ、そのセクションを即時にその場所に取り込みます」ということなのでしょう。逆に、@show
があるとそこで埋め込みが即座に行われてしまうため、複数回@show
が記述されるような場合はやはり埋め込みも複数回発生してしまいます。
コードを読む
実際のところ@show
や@endsection
がどんな処理をするのか、Bladeテンプレートのコンパイル結果を見てみます。
今一度ソースコードを最初のまともに表示されていた状態に戻し、コンパイル結果を確認します。
<html>
<head>
<title>Bladeテスト</title>
</head>
<body>
<?php $__env->startSection('content'); ?>
<p>親の内容</p>
<?php echo $__env->yieldSection(); ?>
</body>
</html>
<?php $__env->startSection('content'); ?>
##parent-placeholder-040f06fd774092478d450774f5ba30c5da78acc8##
<p>子の内容</p>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), array('__data', '__path')))->render(); ?>
このように、@show
は<?php echo $__env->yieldSection(); ?>
に、@endsection
は<?php $__env->stopSection(); ?>
に展開されます。前者はecho
されるため表示が行われるが、後者はecho
がないのでそのままでは表示されないということになります。
ちなみに、これらのメソッドの実態はIlluminate\View\Concerns\ManagesLayouts
クラスに記述されています。
public function yieldSection()
{
if (empty($this->sectionStack)) {
return '';
}
return $this->yieldContent($this->stopSection());
}
public function stopSection($overwrite = false)
{
if (empty($this->sectionStack)) {
throw new InvalidArgumentException('Cannot end a section without first starting one.');
}
$last = array_pop($this->sectionStack);
if ($overwrite) {
$this->sections[$last] = ob_get_clean();
} else {
$this->extendSection($last, ob_get_clean());
}
return $last;
}
yieldSection()
は内部的にはstopSection()
を呼び出すため、どちらもセクションを終わらせることがわかります。
おわり
Bladeテンプレートの表示処理は結構面白そうなので、もっといろいろ見てみたいですね。
参考
-
@parent
ディレクティブを使用していない場合、親の内容に追記するのではなく、親の内容に上書きします。 ↩