読者です 読者をやめる 読者になる 読者になる

CakePHPでサイトを作るのに気をつかったところ

PookCakePHPで作っています。
Pookに限らずCakePHPでサイトを作るのに個人的に気を使うところを書いてみます。
CakePHPのバージョンは1.3.6前提です。

RequestHandlerコンポーネントを使いこなす

Pookはページネーションは全てajaxで取得するようにしています。そのためページネーションされる可能性があるところはすべてjson出力されるようになっています。例えばユーザーページの場合、どちらもアクセスできます。

http://pook.jp/taizo
http://pook.jp/taizo.json

これは、RequestHandlerコンポーネントで.jsonの場合と、デフォルト(.html)の場合でviewファイルを分けています。ざっくりやり方を書くと

1. config/routes.php に Router::parseExtensions()を追加
Router::parseExtensions('json');
2. APP/views/layouts/json/default.ctp を設置する
<?php
header('Content-type: application/json');
echo $content_for_layout;
?>

*1

3. jsonの出力が必要な箇所にviewファイルを用意する
views/articles
|-- index.ctp // html
`-- json
    `-- index.ctp // json

Paginationの取り扱い

上の項目でajax使っていると書きましたが、PookではAjaxヘルパーは使っていません。
CakePHPで使うPaginatorヘルパーを使ってリンクを出力し、それをjavascriptでイベントを追加するようにしています。

なので2ページ目から、直接見たい場合のリンクもきちんとhtmlで動くようにしています。
http://pook.jp/taizo/index/page:2 (2ページ目からクォートが表示されます)

routes.php のテストは必ず書く

APP/config/routes.php を複雑にしていくと、Paginatorヘルパーのリンク生成と整合性が取れなくなるときがあります。
なので、routes.phpを少しでもいじる場合はテストを書いたほうが良いです。
CakePHPの中でも、routes.php のテストは最も簡単な部類です。
CakePHP式のテストの書き方に慣れるには、ここから入るのがオススメです。

極める routes.php

view内の再利用

複数のページに共通のUI(ブログの場合、サブカラムのメニューなど)があって、それを再利用したい場合、いくつか方法があります。適時使い分けることになります。

  1. エレメント
    • シンプルに済ませたい場合はコレ。変数をキチンと判るようにしておかないと、後でわからなくなる。
  2. コンポーネント + ヘルパー
    • 利用するデータのアクセスをコンポーネントで、表示をコンポーネントが呼び出されていること前提で、ヘルパーで実装。Paginatorみたいな使い方になります。
  3. プラグイン
    • 上のものを別プロジェクトで汎用的に使えるようにしたい場合はプラグイン化しておいたほうが良いかと。

Skinny Controller, Fat Model

Skinny Controller, Fat Modelなんていうコントローラを薄くして、モデルは厚くしろなんていう言葉がありますが
CakePHPでもコントローラは薄いほうが良いかと個人的に思います。
理由は1つで、テストが書きづらいからです。僕の中で、CakePHPでテストの書きやすさを並べるとこんな感じになります。

  1. Models
  2. Behaviors
  3. Components
  4. Controllers
  5. Views
コントローラのテストが書きづらい理由

例えば、ブログで最新の自分の記事10件を取ってくることになった場合

<?php
class ArticlesController extends AppController {
  var $components = array(
    'Auth',
  );
  var $paginate = array(
    'limit' => 10,
    'order' => 'created desc',
  );
  function index() {
    $this->set('result', $this->paginate('Article', array('user_id' => $this->Auth->user('id'))));
  }
}

テストを走らせる時に、ログイン状態を保持するようにしなければいけないです。

Securityコンポーネントを使った場合

<?php
class ArticlesController extends AppController {
  var $components = array(
    'Security',
  );
  function beforeFilter() {
    $this->Security->requirePost('add');
    parent::beforeFilter();
  }
  function add() {
    // 記事の追加
  }
}

この場合も、POSTで無くてはaddコントローラを呼び出せないため、RequestHandlerComponent::isPost() をtrueで返すようにテストケース側に仕込む必要があります。

解決するためには、やはりモデルや、コンポーネントなどに処理を移動させます。
あと、ビューのテストはSeleniumなどを使ったほうが僕は好きです。

まとめ

CakePHPを使いはじめて1年半くらい経ったので
来年こそ、やる気を出してブログにネタを書いてみようかなと思います。

*1:大抵はキャッシュの問題があるので、headerにCache-Controlなどを設定することになります。