ここ数ヶ月ほど、週末の空いた時間にPHPのフレームワーク「CakePHP2」を勉強していて、基本的な制作がひと通りできるようになりました。
いくつか小さなアプリを作ってみて、CMSの見積を計算するフォームは自家用で使ってみています。
これまで時間がかかったり、間違いが多かったのがずいぶん楽になりました。
参考にした本と資料、理解するのに時間がかかったものをまとめてみます。
参考にした本と資料
CakePHP2 実践入門
一般的な仕様や機能については「CakePHP2 実践入門」を教科書にしました。
友人のPHPプログラマに「今、CakePHP2の本を買うならこれ一択」と言われて買ったこの本は本当にわかりやすく、今後も長くお世話になりそうです。
ですが、この本に詳しく載ってないことや、イメージ通りのコーディングを実現しようとすると、日本語ドキュメントが少なかったり、前バージョンの情報が出てきてしまったりで、やっぱりずいぶん苦労しました。
英語版の料理本
CakePHPの公式マニュアルは「Cookbook」といいます。
デザインかわいいです。
Cookbookには日本語版もあるのですが、残念ながらすべてのコンテンツの翻訳が終わっていないので、特に気になるHTMLヘルパー・Formヘルパーの詳細な解説がありません。
なので、細かいことはほとんど、本家英語版を読みました。
私は英語がさっぱりなのですが、サンプルコードがたくさんあるので、サイト内検索と機械翻訳で、今のところなんとかなっています。
制作中に詰まったこと
データベースを利用せずに入力チェックを行う
当初、計算結果を保存することも考えていたのですが、このアプリの場合計算だけできればいいことに気付いて、データベーステーブルを削除したらちゃんと動かなくなってしまいました。
CakePHPは、データベースがあることを前提に入力チェックをしているので、モデルに「仮のデータベース構造」を指示してやらなければいけないそうです。
具体的には、利用するモデルに以下のように書きます。
<?php class Hoge extends AppModel { public $useTable = false; var $_schema = array( 'id' => array('type' => 'integer'), 'name' => array('type' => 'string', 'length' => 64), 'body' => array('type' => 'text', 'length' => 9999), 'date' => array('type' => 'datetime') ); ・・・以降に$validateを書く
この例は、「id(整数)」「name(64文字まで)」「body(長文)」「date(日付)」が必要な場合です。
最初に「テーブルを使用しない」という宣言をしてから、$_schemaというローカル変数に仮のデータベース構造を配列で書き込んでいきます。
bodyは長文なのだからlongtextなんじゃないのか、と思いましたが、うまくいかなかったので、大きな数字を入れることでごまかしています…
任意コントローラーをCakePHPのルートにする
CakePHPを設置したばかりの状態では、該当URLにアクセスすると、CakePHPのヘルプが表示されます。
この状態で「hoge」というコントローラーを作ると、URLは以下のようになります。
[CakePHPを設置したパス]hoge/
実際にアプリケーションとして稼働する場合、[CakePHPを設置したパス]にアクセスした時点で、自分が作ったコントローラーが表示されるようにしなければいけません。
この設定を変えるには、
[CakePHPを設置したパス]app/Config/routes.php
に、以下のように書きます。
Router::connect('/', array( 'controller' => 'hoge', 'action' => 'index' )); Router::connect('/:action', array( 'controller' => 'hoge' ));
ほとんどの例では、ひとつめしか載っていないのですが、hogeコントローラーのfugaアクションもルート起点にしたい場合は、ふたつめのメソッドも同時に必要なようです。
CakePHP製のCMS「baser CMS」のroutes.phpを見ると、ページの状態別にRouter::connectがたくさん書かれています。
以下の資料が参考になりました。
Formヘルパーでinput要素を自由に配置する
Formヘルパーで、書籍に載っているシンプルなコードでinput要素を作ろうとすると、上の画像のように一行ずつ独立したコーディングになります。
ですが、私は下の画像とコードのような、Twitter Bootstrap対応のマークアップをしたい個所があったので悩んでしまいました。
(Twitter Bootstrapライブラリがあることはわかっていましたが、今後のために自力で解決しようと思いました)
<div class="control-group"> <label class="control-label" for="HogeCodingL">複雑なページ</label> <div class="controls"> <input name="data[Hoge][coding_l]" class="input-mini" placeholder="000" type="number" id="HogeCodingL"> ページ </div> </div>
結局、以下のようにFormヘルパーを書きました。
HTMLヘルパーを使えばもっとスマートな方法になりそうですが…
オプション部分の、labelとdivをfalseにするとinput要素のみを出力できます。
<div class="control-group"> <label class="control-label" for="HogeCodingL">複雑なページ</label> <div class="controls"> <?php echo $this->Form->input('Hoge.coding_l', array( 'label' => false, 'class' => 'input-mini', 'after' => ' ページ', 'placeholder' => '000', 'div' => false ) ); ?> </div> </div> <?php echo $this->Form->error('coding_l'); ?>
なお、Formヘルパーでぐぐると、CakePHP1.xの頃のコードが多く出てきますが、CakePHP2では、ヘルパーの記述は
$form->…
ではなく
$this->Form->…
と、thisが必要です。
Formヘルパーで初期値が一行目ではないプルダウンリストを作る
いちばん苦労したのがこれです。
Formヘルパーのselectは、バージョン2で大幅に仕様変更となっていますが、ぐぐっても古いのしか出てこないのです。
Cookbookの英語版に載っていることに気付くまで、丸二日くらいうんうん言っていました。
作ろうとしたのは、こんな、最初の空行がなく、ラベルと同じ内容を送信値にして、かつ、初期の選択肢が先頭ではないフォームでした。
<select name="data[Hoge][fuga]" id="HogeFuga"> <option value="ほげほげ">ほげほげ</option> <option value="ふがふが">ふがふが</option> <option value="ぴよぴよ" selected="selected">ぴよぴよ</option> <option value="もげもげ">もげもげ</option> <option value="わらわら">わらわら</option> <option value="うにうに">うにうに</option> </select>
調べた結果、こうなりました。
オプションのemptyをfalseに、defaultに選択状態にしたいvalueを指定します。
echo $this->Form->select('Hoge.fuga', array( 'ほげほげ' => 'ほげほげ', 'ふがふが' => 'ふがふが', 'ぴよぴよ' => 'ぴよぴよ', 'もげもげ' => 'もげもげ', 'わらわら' => 'わらわら', 'うにうに' => 'うにうに' ), array( 'empty' => false, 'label' => 'ほげふがぴよ', 'default' => 'ぴよぴよ' ) );
あとになってから、日本語の値を送るよりは0からはじまる整数の方がいいのかも…と思ったりしましたが…
まとめ
CakePHPのサイトには「Bakery」というユーザーによるスニペット集があります。
私が悩んでいたことは、ここに全部書いてあるような希ガス…
でも、きちんと意味を考えながら調べたことというのは忘れないし応用がきくので、今後ちゃんとアプリケーションを作ったり、CakePHPで書かれた既存アプリをカスタマイズするときにも役に立つだろうと思います。