2011年12月5日月曜日

[CakePHP]Model名’Datum’、テーブル名'data'の罠[Web系一人AC2011]

土日は、YAuth的な理由(僕の場合はDですが。)により、blogを書けないことが判明しました。
と言うわけで、平日のみの更新とさせていただきます…。すいません。

さて。
今回は、前回話をした、CakePHPにてDatumというモデル名(テーブル名はdata)を使うとハマる、と言う件についてです。

■まえがき。
実はこの問題、「CakePHP Datum」というキーワードで検索すると、@uechocoさんのblogがクリティカルヒットします。
http://labs.uechoco.com/blog/2010/12/cakephp-model-lovers.html
こちらの記事です。
しかし、当時の自分は
「CakePHP data テーブル名」
なんて、ノイズが多そうな検索ばっかり試していて、自力で気づくまで延々と悩むと言う恐ろしい失態をしてしまいました。OMG....

せっかく自分でもソースを追ったので、記事にしておきます。

べ、別にネタが無いわけじゃないんだからねっ

■きっかけ

  • 最初に作った、ただのPHP版のサンプルでは、テーブル名を「data」にしていた。
  • CakePHPは、モデル名とテーブル名の命名に「モデル名は単数形、テーブル名は複数形の英単語」というルールがある。
    このルールを守ることにより、コーディング時に設定ファイルを書かなくても良い。
    モデル名/テーブル名とした場合の例):Sample/samples, User/users, Woman/women
  • 例を見て解るとおり、こちら単純に英単語に「s」をつければ良いわけではなく、ちゃんと英語のルールに沿っている。
    具体的には、以下のソースで判別しているので、詳細が気になるなら参考にするとおk
    https://github.com/cakephp/cakephp/blob/master/lib/Cake/Utility/Inflector.php
  • 「じゃあ、テーブル名を'datas'に変えないといけないのかな?」と思ったが、「'data'って実は複数形だったらしいよ」と最近話題になっていたのを思い出し、調べる。
  • 調べた結果、dataの単数形はdatumであることが判明。
    CakePHPでも問題なく利用できるルールのようだ。
    ちなみに、このサイトで、規約に沿ったワードを調べられます。
    http://www.cpa-lab.com/tech2/inflects/
  • よーし、じゃあ、「Datum/data」の組み合わせでやろう!
  • 一覧表示、データ登録、削除はうまく動いたぞ、さて後は更新…あれ?FatalErrorが出るよ?あれ?
    Fatal error: Call to a member function getColumnType() on a non-object in /var/www/php_samples/cakephp/lib/Cake/Model/Model.php on line 1258

■原因調査
エラーが発生したコードはこちら。
https://github.com/cakephp/cakephp/blob/master/lib/Cake/Model/Model.php

getColumnType呼び出し時の引数$columnに渡される値は、多くの場合
「モデル名.カラム名」
となっています。
例えばDatum/dataの場合なら
Datum.id
Datum.title
Datum.body
等となります。

しかし、こちら更新時には、更新対象列の絞込みのため
「テーブル名.カラム名」
という値で呼ばれる事があります。
(おそらく、更新以外にも複雑なことをやろうとした場合、同じ呼ばれ方をします。今回発生はしませんでしたが。)
具体的には、プライマリキーのカラムのみそのような呼ばれ方をするので、
data.id
となります。

さて、問題のコードを見てみましょう。
Datum.idDatum.title にて呼ばれた場合、問題の1257行目のIF文はFalseとなり、
その先の$colsよりカラムの型を取得し、返します。
しかし data.id にて呼ばれた場合、Modelクラスは$dataというメンバ変数を保持しているため、問題のIF文がTRUEとなり、
$this->data->getColumnType($column);
という呼び出しを行おうとします。
しかし、実際にModelが持っている$dataは配列なので、メソッドを呼び出せずFatal Errorとなるわけです。

■解決策
モデル名をSample, テーブル名をsamplesに変更

■まとめ/教訓

  • フレームワーク利用時に一般的過ぎる単語を使うときは、注意する。
    というか、実際にサイトを作るときに、dataなんて一般的過ぎる単語は誤用が発生する可能性が高いので、NG。
  • 予約語リストがあれば、真っ先に参照するべき。
    CakePHPの場合、僕が探した限りでは見つかりませんでした。
    あったら是非教えてください。
  • 予約語リスト等が見つからない場合、関係するクラスのメンバ変数名はチェックしておいたほうが良い、かも。
    頻度的には、問題が起きたら疑うレベルでも良いけど、一度見ておくとそのフレームワークの命名のルールやよく使われる単語が見えてくるので。
  • Google先生に聞くときは、なるべくノイズが混じらない検索方法を行う。
  • 迷ったら、机上で考える前に一通りソースを舐めろ。デバッガ使え。
    (実は「あれー?おかしいなぁ、あってるはずなのになー。」と、コードと公式ドキュメントを見比べる時間が30分以上あったり…orz)


今回は以上になります。
次回は、予告どおりSymfony2にてサンプルの実装を行ってみます。

1 件のコメント:

  1. A wonderful article you posted. That is so informatory and creative.Please keep these excellent posts coming.
    php application development

    返信削除