[CakePHP3] Controller単位でdefault.ctp以外のレイアウトファイルを指定する

CakePHP3の全体的な作りがなんとなく、本当になんとなくわかってきた今日このごろです。日々トライ&エラーで牛歩ながらも作りたいものに近付いているようないないような?うん、たぶん近付いてます。

んで、ビュー(テンプレート)をちまちま作っているときにつまずいたことをメモ。具体的にはレイアウトファイルの切替で、アクション単位で変更する場合はすぐに情報が出てきたんですけど、コントローラ全体で別のレイアウトファイルを使いたい場合はどうするの?ってところです。


スポンサーリンク

1.デフォルトのビュー描画プロセス

CakePHP3ではコントローラからビューを描画するとき、デフォルトだとアクション名と対応するCTPファイル(テンプレートファイル)が呼ばれ、その内容がdefault.ctp(レイアウトファイル)の$this->fetch(‘content’)部分にはめこまれてブラウザに表示されます。

UsersControllerのindexアクションを表示する場合は下記の感じ。

  1. httpリクエストで(root)/users/indexが呼ばれる
  2. UsersControllerのindexアクションに記述した処理を実行
  3. View/Template/Users/index.ctpを描画
  4. ↑の描画結果をView/Template/Layout/default.ctpに渡す
  5. default.ctpの$this->fetch(‘content’)にindex.ctpをはめ込んで描画
  6. 結果がブラウザに表示される

このindexアクションからindex.ctpが呼ばれて、さらにdefault.ctpが呼ばれる流れは別に何も書かなくてもCakePHPが勝手にやってくれます。すごく便利な一方で、もちゃもちゃアプリを作っていると、ちゃうねん、別のレイアウト呼んでほしいねん!ってときがまあ出てきます。

2.自前のレイアウトファイルを作る

default.ctp以外にレイアウトファイルを用意したい場合は、View/Template/Layoutフォルダ内にctpファイルを作ってやればOKです。

中身はdefault.ctpを参考にして、CookBook見ながら作っていけばなんとかなります。


スポンサーリンク

3.default.ctp以外のレイアウトファイルを呼ぶ

じゃあ作ったレイアウトファイルを呼ぶにはどうすればいいの、と。

3-1.個別のアクションで設定する

アクション単位でレイアウトファイルを変えたい場合は簡単です。たとえばmyLayout.ctpというレイアウトファイルを使いたい場合、アクション内に以下を記述します。

//Controller/UsersController.php
public function index()
    {
        $this->viewBuilder()->layout('myLayout'); //←これ
    }

viewBuilderヘルパーのlayout()メソッドで指定すると、そのレイアウトファイルを使ってくれるようになります。ちなみに、レイアウトファイルを指定する際は.ctp拡張子はいりません。

3-2.コントローラ単位で指定する

つまづいたのはこっち。

たとえばユーザに見せるフロントサイトと、管理画面でレイアウトを分けたいってなると、「このコントローラはこっちのレイアウトファイルを使う」ってシチュエーションがあると思います。

で、調べてみると、CakePHP2の頃はControllerクラスのメンバ変数$layoutにレイアウトファイルを指定してやるとできたらしいんですね。

class PrintersController extends AppController
    {
        public $layout = 'myLayout';
    }

でもCakePHP3だと、一応上記は動くのですが、「Property $layout is deprecated. Use $this->viewBuilder()->layout() instead in beforeRender().」といってちょっぴり怒られます。

このやり方は非推奨だよー、beforeRenderの中でviewBuilder使ってねー、と。そういうことらしい。

beforeRenderが何かというと、Controllerクラスに用意されているコールバック関数です。アクションの処理が終わって、描画が始まる前に呼び出されるメソッド。

いろいろ試行錯誤して、結論から言うと下記の書き方が正しいみたいです。

//Eventクラスを読み込む
use Cake\Event\Event;

//Controller内
    public function beforeRender(Event $event) {
        parent::beforeRender($event); //親クラスのbeforeRendorを呼ぶ
        $this->viewBuilder()->layout('admin');
    }

ポイントはふたつ。

  1. 親クラスのコールバック関数を呼ぶ
  2. Eventクラスをuseする

このへんをちゃんとしていないと、「Declaration of App\Controller\Admin\UsersController::beforeRender() should be compatible with App\Controller\AppController::beforeRender(Cake\Event\Event $event)」と言って怒られます。ちゃんと親クラスと互換性を持たせなさいねー、ということ、なのかな?

特にEventクラスを読み込まなきゃいけないところがなかなか情報出てこなくて「うわーーん、わかんないよーーーっ」てなってました。ちゃんと動いてよかったよかった。

この投稿の投稿者は おさみ です。ブックマーク用 パーマリンク

スポンサーリンク