WHAT'S NEW?
Loading...

フォームヘルパに関して(CakePHP Advent Calendar 12st)

« 11日 msng氏「CakePHPでデバッグレベルが0の時だけ発生するエラー
Datasourceを使い倒す」kaz_29氏 13日 »

スタンド名「スパスティック」
特技「空気読まない」
俺のスタンドだ。距離はゼロメートル。
むしろ俺自身がスタンド。

さて、CakePHP Advent Calendar 12日目の担当だ。
なんで12日目を選んだかというと、12という数字がすきだからだ。
それにyamlよりiniファイル好きな俺としては、12はドメスティックに愉快だとおもわないか?

ま、そんな話は完全に無視して良い。

12ということで、テストしたCakePHPのバージョンは1.2を使用してみた。

ところでテーマだけど、俺はフォームヘルパーに関して、ざっくりとやってみたい。

完全にオレオレフレームワークでプロプライエタリな開発環境だが、いい加減メジャーなフレームワークに移行したいといういろいろな開発者、もしくはその関係者に対して、俺はCakePHPがMVCフレームワークの中でもかなりの勢いがあるし、何しろ俺がやってるから俺が直接教えることができるんだ!的な普及活動をしているんだけど、質問の多くを占める、inputタグに関する滞りを払拭してみたいと思った。



数年前、symfonyの0.8だか0.9くらいでテンプレ担当したとき、ヘルパーというのを初めて使った。そのときの感想が、

「はぁ?ヘルパァ?地獄の低脳?なんだそれ?生臭そうでインチキ臭ぃのう!!!」

とか

「こんなモン使わなくても、フツウにHTML書いたほうが速いじゃねぇかボケ!!」

だった。

しかしそれはそれから数年後、CakePHPを使ってみると、フォームヘルパこそ、Web構築に欠かせない便利機能の一部なのではないかと思った。

文章ではうまく伝えられないかも知れないけど、例えば
  1. 入力画面
  2. 確認画面
  3. 完了画面
の中で、入力エラー(つまりバリデーション結果)をどこで表示するか、だ。

オレオレフレームワークな人は大抵、2の確認画面でエラーを出す人が多い。
しかし、CakePHPでは1の入力画面で入力エラーを表示させる。
実はこのほうがスマートだ。

仮に2の確認画面でエラーを表示した場合、どうやって入力画面へ戻らせるのか?

セッションにデータを保存して入力画面まで持ち越すのか、それとも戻ったらフィールド全部消えているのか、さらに、もともとDBから読み込んだデータをもとにした編集画面であれば、もとのデータを表示させるのか、それとも今入力したデータを表示させるのか、いろいろ悩むと思う。特に初心者だと。

正直フレームワークと使うと、こんな事どうでもよくなる。全く考えないで開発できる。

こう行った独善的開発につきものの悩み事なんぞ、犬に食わせて良い。
そう思えるくらいCakePHPでは簡単スマートで、結果的に寝覚めが良い(これ大事)。

しかし、HTMLでinputタグを使うのはバリバリ楽勝だよ、と思っていても、いざCakePHPのフォームヘルパつかうと、
  • あれはどうやるの?
  • これはできないの?
など、多くの質問を受けるハメになる。
挙句の果てには
  • HTMLで書くからいいや
となる場合があるが、
  • inputタグを直接書いたら自動的に処理してくれる部分が動かない
つまり、
  • かなりの量の処理を手で書くハメになる
という事になる。
これは本来やらなくて良い処理のはずだ。

さらにこれをこのまま勧めてしまうと、
  • 前例が少ないやり方だからハマったときにヒントがない
ということになり、
  • CakePHPダメじゃん!
で終わるという、耳の切ない結末を垣間見ることになってしまう。
それじゃダメだ。

かく言う俺も、CakePHP導入直後の初回の業務で、ある意味自分を追い込むため、複数モデルを複数レコード一度に保存してやるぜ!的な勢いで、よくわからんままinputタグをHTMLタグ直接記述して使いまくり、身長が縮むほど苦労した。

開発期間4ヶ月のうち、2ヶ月間これで頑張ったが、3ヶ月目に全部つくり直すことにした。
今では、この時は2ヶ月を無駄にしたけど、結果的につくり直してよかったと思っている。
それほどフォームヘルパは大事だと思った。

さて、前置きは終わり。
まず結論から言うと、

$form->input()をじゃんじゃん使おう

だ。

しまいには

「俺なんかケツふくときもフォームヘルパだぜ!」

と言えるほど、フォームヘルパを使いまくった方が良い。
さて、早速やって見る。

■フォーム開始

フォームは以下のヘルパーで開始しているものとする。
echo $form->create('User', array('url' => array(
  'controller' => 'dashboards',
  'action' => 'index',
  'id' => null
)))
つまりモデルはUser、ということ。

■入力系

input[type="text"]
ヘルパー
echo $form->input('loginId');
表示
HTML
<div class="input text"><label for="UserLoginId">Login Id</label><input name="data[User][loginId]" type="text" value="" id="UserLoginId" /></div>
フィールド名のみでオプションを何も指定しなければ自動的にテキストフィールドになる。

input[type="password"]
ヘルパー
echo $form->input('loginPs', array(
  'type' => 'password'
));
表示
HTML
<div class="input password"><label for="UserLoginPs">Login Ps</label><input type="password" name="data[User][loginPs]" value="" id="UserLoginPs" /></div>
typeにpasswordを指定すると、パスワードフィールドになってくれる。

textarea
ヘルパー
echo $form->input('comment', array(
  'type' => 'textarea'
));
表示
HTML
<div class="input textarea"><label for="UserComment">Comment</label><textarea name="data[User][comment]" cols="30" rows="6" id="UserComment" ></textarea></div>
こちらもtypeにtextareaを指定するとテキストエリアフィールドになってくれる。

■選択系

select
ヘルパー
echo $form->input('author', array(
  'type' => 'select',
  'options' => array(
    '手塚治虫',
    '弘兼憲史',
    '望月峯太郎',
    '荒木飛呂彦',
  ),
  'empty' => __('----', true)
))
表示
HTML
<div class="input select"><label for="UserAuthor">Author</label><select name="data[User][author]" id="UserAuthor"> 
<option value="">----</option> 
<option value="0">手塚治虫</option> 
<option value="1">弘兼憲史</option> 
<option value="2">望月峯太郎</option> 
<option value="3">荒木飛呂彦</option> 
</select></div>
typeにselectを指定すると、プルダウンフィールドになってくれる。
optionsオプションには選択肢となるopptionタグの中身を配列で指定しておく。
表示のスクリーンショットでは縦に2個表示されているが、下段はオプションのemptyを削除し、オプションにsize => 4を追加して中身を表示させてみたもの。

input[type="checkbox"]
ヘルパー
echo $form->input('agree', array(
  'type' => 'checkbox',
))
表示
HTML
<div class="input checkbox"><input type="hidden" name="data[User][agree]" id="UserAgree_" value="0" /><input type="checkbox" name="data[User][agree]" value="1" id="UserAgree" /><label for="UserAgree">Agree</label></div>
typeにcheckboxを指定すると、チェックボックスになってくれる。
ただしこれは「利用規約に同意する」などで使うように、1個だけ表示したい場合にのみ使かう。

input[type="checkbox"](複数一括指定)
ヘルパー
echo $form->input('artist', array(
  'type' => 'select',
  'multiple' => 'checkbox',
  'options' => array(
    'スパスティック インク',
    'ラスチャイルド アメリカ',
    'ヴァン ヘイレン',
    'スパイラル アーキテクト',
    'AKB48',
  )
))
表示
HTML
<div class="input select"><label for="UserArtist">Artist</label><input type="hidden" name="data[User][artist]" value="" /> 
<div class="checkbox"><input type="checkbox" name="data[User][artist][]" value="0" id="UserArtist0" /><label for="UserArtist0">スパスティック インク</label></div> 
<div class="checkbox"><input type="checkbox" name="data[User][artist][]" value="1" id="UserArtist1" /><label for="UserArtist1">ラスチャイルド アメリカ</label></div> 
<div class="checkbox"><input type="checkbox" name="data[User][artist][]" value="2" id="UserArtist2" /><label for="UserArtist2">ヴァン ヘイレン</label></div> 
<div class="checkbox"><input type="checkbox" name="data[User][artist][]" value="3" id="UserArtist3" /><label for="UserArtist3">スパイラル アーキテクト</label></div> 
<div class="checkbox"><input type="checkbox" name="data[User][artist][]" value="4" id="UserArtist4" /><label for="UserArtist4">AKB48</label></div> 
</div>
もしチェックボックスを複数一括で指定するなら、typeはselectとしmultipleオプションにchekboxとして指定すれば良い。
optionsオプションには連想配列やリスト形式の配列を指定すれば、順番通りに表示してくれる。

input[type="radio"]
ヘルパー
echo $form->input('genre', array(
  'type' => 'radio',
  'options' => array(
    'ロック',
    'ポップス',
    'メタル',
    'ジャズ',
    'クラシック',
  )
))
表示
HTML
<div class="input radio"><fieldset><legend>Genre</legend><input type="hidden" name="data[User][genre]" id="UserGenre_" value="" /><input type="radio" name="data[User][genre]" id="UserGenre0" value="0"  /><label for="UserGenre0">ロック</label><input type="radio" name="data[User][genre]" id="UserGenre1" value="1"  /><label for="UserGenre1">ポップス</label><input type="radio" name="data[User][genre]" id="UserGenre2" value="2"  /><label for="UserGenre2">メタル</label><input type="radio" name="data[User][genre]" id="UserGenre3" value="3"  /><label for="UserGenre3">ジャズ</label><input type="radio" name="data[User][genre]" id="UserGenre4" value="4"  /><label for="UserGenre4">クラシック</label></fieldset></div>
typeにradioを指定するとラジオボタンになってくれる。optionsオプションに配列を指定する。
デフォルトで選択しておきたい項目がある場合、valueオプションにキーを指定すればOK。
他のヘルパーと違い、legendタグで囲まれる。

input[type="file"]
ヘルパー
echo $form->input('photo', array(
  'type' => 'file'
))
表示
HTML
<div class="input file"><label for="UserPhoto">Photo</label><input type="file" name="data[User][photo]" value="" id="UserPhoto" /></div>
typeにfileを指定するとファイル選択フィールドになってくれる。
ファイルアップロードなどをする場合、form開始タグに「multipart/form-data」が必要になるが、$form->create()ヘルパーのオプションtypeでfileと指定すればOK。

■ボタン系


input[type="submit"]
ヘルパー
echo $form->submit('submit')
表示
HTML
<div class="submit"><input type="submit" value="submit" /></div>
submitボタンは専用のメソッドを使う。
もし「»」などがエスケープされてしまう場合、オプションで escape => falseを指定すればOK。

input[type="reset"]
ヘルパー
echo $form->button('reset', array(
  'type' => 'reset',
))
表示
HTML
<input type="reset" value="reset" />
buttonメソッドのtypeにresetを指定するとリセットボタンになる。

input[type="button"]
ヘルパー
echo $form->button('button')
表示
HTML
<input type="button" value="button" />
ボタンの場合、buttonタグで囲まれるのではなく、inputタグのtypeがbutotnになったものが使用される。

■フォーム終了

echo $form->end()
これでフォームの終了タグが出力される。


■その他

全体でほぼ共通しているオプションが幾つかある。
もし表示のされ方が気に入らなかったら、オプションにいくつか足してみると良い。
例を以下にあげておく。
フィールドの前後で改行してしまう
'div' => false を指定
勝手にフィールド名が表示されてしまう
'label' => false
ラジオボタンのlegendを消したい
'legend' => false
CSSのクラス名を指定したい
'class' => 'strong clearfix'など
インラインスタイルを追加したい
'style' => 'margin:1em'など
その他
基本的には、勝手に追加したオプションはHTMLタグのアトリビュートになってくれると考えておいて問題なし。

■個人的に注意していること

色々いじっていくうちに、以下のような疑問が出てくるかも知れないが、正直言ってそこまでCakePHPでHTMLを加工しようとするのはどうかと思っている。
  • labelタグの中にinputタグを入れたい(ボタンの様にする場合)
  • ラジオボタンを多段表示させたい
  • チェックボックスを多段表示させたい
  • ラジオボタンのラベルを前に持ってきたい
  • その他
そこまでやる必要はない。

はっきり言ってこれらはphpで対応するような内容ではない。

CakePHPのヘルパーはあくまで挙動のヘルプをしてくれるもので、HTMLの構造を設定するようなヘルプなんかほとんどしてくれない。当然スタイルもヘルプしてくれない。むしろそこまで細かいヘルプをCakePHPがしちゃダメだ。

ちなみにそう言った理由で、CakePHPのbakeで作ったビューファイルにテーブルをストラピングで背景色を変更するためphpのロジックが入っているのを見たときに唖然とした。

これはやっちゃダメだろう。Vがロジックに支配されてしまっている。Vを支配していいのはクライアントのブラウザだけだ。よく考えれば分かるはず。

サーバに知らせるな!という言い方でもいい。サーバはレンダリングしない。

こう言うのはcss単体でできるものはcssのみで対応させ、それでもできない場合はphpではなく、javascriptでやれば良い。例えば、jQueryなどだ。

サーバサイドでやるんじゃなく、クライアントサイドでやってしまえということだ。
Webやってるんだったら理想は、phpだけじゃなくJavaScriptも(X)HTMLもCSSもひと通りできないとダメだと思っている。

XAMPPではなく本物のLinuxなどのOSで、サーバサイドのApache、MySQL、Postfix、BINDなんかのデーモン、xinetdなどのスーパーデーモン、iptablesで特定範囲のIPを遮断など、それからソフトウェアだけじゃくハードも勉強しないといけない。

ドメイン取得してサーバ借りてOSセットアップしてWebサーバ構築、システムをデプロイして運用させ、メンテナンスしながらサポートも行う、といった一連の流れだ。

そういったいろいろな技術と知識を得ていくと、例えばなんでもかんでもPerlで!とか、何でもかんでもphpで!とか、そういう考え方はなくなってくる。

ここはこれ、あそこはこれだ!的な、適材適所にいろいろな知識と情報を使ったほうが、ひとつに固執するよりはるかに気軽だし手離れも良くなる。

そして何が良いかというと、仕事が早く終るので飲みに行けるということだ。

というわけで、個人的な意見を交えてフォームヘルパーをざっくり説明してみた。

唐突に終わる。