この記事は、FORK Advent Calendar 2018 の1日目の記事です。
もうアドベントカレンダーの季節ですか。
時間の流れが早すぎて困りますね。。
私は普段CakePHPにSmartyを組み込んで開発しているのですが、
ビューのテンプレートとして組み込む前のHTMLは、pugで書いたのをコンパイルしたものだったりするので、
pugをそのまま使えちゃった方が良いよね?と思って色々試した記録です。
ライブラリ探し
ググったら、速攻でCakePHP用のものを見つけました。
https://github.com/elquimista/cakephp-jade
が、GitHubを見ると3年前で止まっている。。
次に見つけたのはコレ。
https://github.com/pug-php/pug
動きも活発そうだし、良さそうですね。
これを利用したLaravelとかSymfonyなどのフレームワーク用のやつもあるし!
あれ、、CakePHPのがない。
ということで、2つ目のライブラリを継承しつつ、
1つ目のコードをパクって参考にして、ちょっと作ってみました。
PugView(簡易版)
出来上がったのがこちらです。
<?php
namespace App\View;
use Cake\View\View;
use Pug\Pug;
class PugView extends View
{
protected $_ext = '.pug';
protected $pug;
public function initialize()
{
$this->pug = new Pug([
'pretty' => true,
'cache' => CACHE . 'views' . DS,
]);
// レイアウトを使わないようにする
$this->setLayout('');
}
protected function _render($viewFile, $data = [])
{
if (empty($data)) {
$data = $this->viewVars;
}
$data = array_merge(
$data,
[
'view' => $this,
]
);
return $this->pug->renderFile($viewFile, $data);
}
}
ほとんど何も書いてないですね!
上記のソースを src/View/PugView.php
として保存し、使いたいコントローラーで、
$this->viewBuilder()->setClassName('Pug');
としてやればOKです!
簡易版の仕様
- pugファイルは通常通り
src/Template/
に配置する - 拡張子は .pug でOK
- .ctp は使わない
- CakePHPのレイアウト、エレメントは使えないので、pug の extends や include を使おう
- テンプレートファイルでは、
$this
の代わりに$view
を使おう - ヘルパーは使える
お試し
簡易版が出来たので、お問い合わせフォームを想定して使ってみましょう。
テンプレートファイルの構成
src/Template/
_parts/ -- extends や include で使うやつ
_header.pug -- ヘッダーのパーツ
_footer.pug -- フッターのパーツ
_layout.pug -- ベースとなるレイアウト
Contact/
complete.pug -- 完了画面
confirm.pug -- 確認画面
index.pug -- 入力画面
_parts/_layout.pug
レイアウトはとりあえずシンプルに。
doctype
html(lang="ja")
head
meta(charset="utf-8")
title!=title
link(rel="stylesheet", href="/common/base.css")
body
include _header
block content
include _footer
Contact/input.pug
Formヘルパー使わずにベタで書いてみます。
extends ../_parts/_layout
block content
form(method="post" action!=$view->Url->build(['action' => 'confirm']))
.form_column
label.form_label(for="company")
|お名前
span.form_required ※必須
.form_control
input.form_input(type="text" placeholder="山田太郎" name="name" value=$input.name)
if $errors.name
p.form_error
= $errors.name
.form_column
label.form_label
|メールアドレス
span.form_required ※必須
.form_control
input.form_input(type="email" placeholder="user@example.com" name="email" value=$input.email)
if $errors.email
p.form_error
= $errors.email
.form_column
label.form_label
| 問い合わせ内容
span.form_required ※必須
.form_control
textarea.form_input(name="content")
= $input.content
if $errors.content
p.form_error
= $errors.content
.formBtnWrap
button(type="submit") 入力内容のご確認
変数は下記をセットしているものとします。
$input
: POSTデータ
$errors
: バリデーションエラー(※デフォルトだと2次元配列になって扱いづらいので、下記のようにフォーマットしています)
$errors = [
'name' => 'お名前を入力してください',
'email' => 'メールアドレスを入力してください',
'content' => 'お問い合わせ内容を入力してください',
];
出力結果
何も入力せずにフォームを送信したときのHTMLソースはこんな感じになりました。
うまく行ってますね!
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="/common/base.css">
</head>
<body>
<header>
<div class="header">ヘッダー</div>
</header>
<form method="post" action="/contact/confirm">
<div class="form_column">
<label class="form_label" for="company">お名前<span class="form_required">※必須</span></label>
<div class="form_control"><input class="form_input" type="text" placeholder="山田太郎" name="name" value=""> <p class="form_error">お名前を入力してください</p>
</div>
</div>
<div class="form_column">
<label class="form_label">メールアドレス<span class="form_required">※必須</span></label>
<div class="form_control"><input class="form_input" type="email" placeholder="user@example.com" name="email" value=""> <p class="form_error">メールアドレスを入力してください</p>
</div>
</div>
<div class="form_column">
<label class="form_label">問い合わせ内容<span class="form_required">※必須</span></label>
<div class="form_control">
<textarea class="form_input" name="content"></textarea>
<p class="form_error">お問い合わせ内容を入力してください</p>
</div>
</div>
<div class="formBtnWrap">
<button type="submit">入力内容のご確認</button>
</div>
</form>
<footer>
<div class="footer">フッター</div>
</footer>
</body>
</html>
その他 Tips など
触ってみて個人的にハマったポイントなどを少し書いておきます。
文法
Pug-php は Phug をベースにしていますので、
まず基本的な文法はこちらを読んでおきましょう。
https://www.phug-lang.com/
レイアウト使えない問題
上述したPugView クラスでは、レイアウトを使わないように設定していますが、
これは、Cakeのレイアウトの機能を使うと doctype が変わってしまったり、意図しないHTMLが出力されてしまったためです。
なので、そこは使わないようにして、 pug の extends を使う方針にしました。
おそらく、レイアウトとビューを2重にコンパイルしようとしているためだと思うのですが、
細かくは追えてません。。
同じ名前の変数を使うとぶつかる
当たり前な話ですが、コントローラーからセットした変数と同じ名前の変数がテンプレート内で宣言・代入されていると、そちらが優先されてしまいます。
そのため、元から pug で使われている変数名と、コントローラーからセットする変数名はぶつからないようにしないと行けません。
これはちょっと命名を工夫しないとマズイですね。。
変数の出力
Phug では$
を付けることになっていますが、Pug-phpでは$
がなくても出力されるようです。
// どっちでもOK
= $a
= a
※オブジェクトのメソッドを呼ぶ場合は、$
が必要です。
!= $obj->method()
エスケープ処理
変数を出力する際にエスケープするかどうかは、=
か !=
のどちらを使うかで切り替えられます。
= $a // エスケープされる
!= $a // エスケープされない
checked の切り替え
ラジオやチェックボックスの checked を付けるときは、下記のように書くと使えます。
$checked の値は、'checked' ではなく、true/false の bool を入れます。
:php
$checked = (isset($input['checked']) && $input['checked'] == 1);
input(type="checkbox" checked=$checked)
Formヘルパーも使えます
上述したサンプルはベタで書いてましたが、こんな感じでFormヘルパー使って書くことも出来ます。
エスケープされないように !=
を使います。
!= $view->Form->create('contact')
!= $view->Form->control('name')
!= $view->Form->control('email')
!= $view->Form->control('content', ['type' => 'textarea'])
感想
手探りな状態なので、まだまだ気づいていない罠とかありそうな気もしてますが、
結構行けるんじゃないか?という感触でした。
なので、引き続き探ってみようかと思います。