CakePHP TIPS

目次

Model::findBy{フィールド名}() の落とし穴

CakePHP の Model には、任意のフィールドからレコードを検索する Model::findBy{フィールド名}() というマジックメソッドがありますが、 PHP4 では関数名の大文字小文字を区別しないため、フィールド名に単語を2つ以上用いている場合に注意が必要です。

たとえば user_id というフィールドから目的のレコードを検索する際、 PHP5 では…

<?php
$model->findByUserID();
?>

…と、書きますが、 PHP4 では…

<?php
$model->findByUser_ID();
?>

…と、アンダーバーを残して書く必要があります。

チェックボックスにチェックがない場合でもデータを送信する

通常、チェックボックスにチェックがない状態でフォームを送信すると、リクエストからはそのチェックボックスの名前すら取得できません。

たとえば次のような HTML でチェックボックスにチェックをしなかった場合、リクエストに含まれるパラメータは user_name=hoge のみとなります。

<form action="some_controller" method="post">
  <input type="text" name="user_name" />
  <input type="checkbox" name="is_admin" value="0" />
</form>

しかしながら PHP スクリプト中でチェックボックスのチェックが外れたかを確認するために…

<?php
$is_admin = false;
if (in_array("is_admin", $_POST)) {
  $is_admin = true;
}
?>

…と書くのも面倒です。 CakePHP ではこの問題にどう取り組んでいるかと思い、ソースを調べてみました。どうやら CakePHP では、同じ名前の hidden 要素でチェックが外れた場合の value を送信しているようです。

<form action="some_controller" method="post">
  <input type="text" name="user_name" />
  <?php  // チェックが外れた場合はこの値を送る ?>
  <input type="hidden" name="is_admin" value="0" />
  <?php  // チェックされた場合は hidden を上書きしてこの値を送る ?>
  <input type="checkbox" name="is_admin"  value="1" />
</form>

HTTP パラメータに同じ名前があった場合は、一番最後のパラメータのみを送るというブラウザの仕様でしょうか。 IE と Firefox では動作しました。

関連テーブルを作る際は ASCII コード順にテーブル名を並べる

関連テーブルを作る際、アルファベット順にテーブルを並べるとは理解しているものの、次のようなテーブルはどうやって連結するか悩んでしまうことがあります。

  • items
  • item_lists

この場合は item_lists_items になります。 s より _ の方が ASCII コードで早く現れるためです。細かいことを気にしたくない場合はアソシエーションの設定で関連テーブル名を明記した方が良いと思います。

Object::requestAction() を使う際のメモ

Object::requestAction() で別のアクションを実行する際、パラメータを GET 通信のようにパス上で渡す場合は URL エンコードする必要はなく、受け側のスクリプトで正しく受け取ることができます。

たとえば次のコードは問題なく動作します。

<?php
$results = $this->requestAction("/some/api/ほげ/ばー");
?>

ソースを見れば一目瞭然なのですが、 HTTP リクエストを送っているわけではないんですね。直接起動しています。

required と allowEmpty の違い

CakePHP 1.2 からは新しい入力値の検証機構が搭載されていますが、 required と allowEmpty という二律背反的な設定項目があります。

required は「その項目の存在を必須とするか?」の設定で、 allowEmpty は「空の値を許容するか?」の設定です。これを組み合わせると「保存に際して必要ではないけど、保存するなら空の値は許さないよ」というフィールドを作ることができます。たとえばパスワードなんかはこの例に当てはまると思います。これを実現するためには次のように書きます。

<?php
class User extends AppModel
{
  var $name = "User";
  var $validate = array("user_id" => array(array("rule" => array("minLength", 4),
                                                 "required" => true,
                                                 "allowEmpty" => false)),
                        "password" => array(array("rule" => array("minLength", 8)),
                                                  "required" => false,
                                                  "allowEmpty" => false));
}
?>

この場合、 user_id は required が true なので、いかなる場合も user_id のフィールドが存在している必要がありますが、 password のフィールドはなくても保存することができるようになります。ただし、 password のフィールドを用意した場合は値を入力しなければなりません。

ちなみに password の on に create と設定しても同じようなことができます。

Model::find() や Model::findAll() の条件として BETWEEN を使う

Model::find() や Model::findAll() の条件として BETWEEN を使うためには、条件を指定する配列に BETWEEN 句を書きます。

<?php
$conditions = array("Model.field" => "BETWEEN ". $start. " AND ". $end);
$results = $this->Model->findAll($conditinos);
?>

本当は不等式をふたつ書いて start < field AND field < end とすれば済むのですが、配列で指定する都合上、同じフィールド名がふたつ書けないため BETWEEN を採用する必要があります。

なんとかならないものか思案中です。

CakePHP 1.1 で webroot を変更する際の注意点

CakePHP 1.1 で /app/webroot の場所を変更する際は注意点があります。

少しでもセキュリティを向上させるために、ドキュメントルート以下には webroot のみを配置しようと考えることは多いと思いますが、 CakePHP 1.1 では実際にアプリケーションにアクセスする際の URL を webroot が配置されているパスから算出するため、もし webroot をシンボリックリンクにしている場合に問題に遭遇します。

たとえば、ドキュメントルートが /var/www で webroot のディレクトリが /usr/local/app 以下にあるとして、シンボリックリンクを張るために次のコマンドを実行したとします。

ln -s /use/local/app/webroot /var/www/webapp

このアプリケーションへは http://www.domain.tld/webapp/ という URL でアクセスできるはずですが、環境によってはスタイルシートなどが読み込めなくなることがあります。これは webroot が配置されているパスとドキュメントルートのパスから /webrpp/ というベース URL を決定しており、シンボリックリンクを張ると次の式が成り立たなくなる可能性があるためです。

/var/www/webapp/ - /var/www/ = /webapp/

多くの環境では、シンボリックリンクを張ると、さもその場所にディレクトリがコピーされたかのような扱いになるため問題はないのですが、環境によってはそうならない場合もあります(つまり /var/www/webapp の中で pwd すると /var/www/webapp と返ってくるような環境がある)。

この問題に対応するためには次の手段を講じます。

  • 本当にその場所に webroot をコピーする
  • $Controller::webroot と $Controller::base にベース URL (前述の例の場合 “/webapp/” )をセットする

お好みにあわせてどうぞ。

プラグインコントローラからプラグインモデルを使う方法

プラグインコントローラからプラグインモデルを使うためには、プラグイン名でモデル名を修飾します(ドット区切り)。

通常ですと $Controller::uses 配列にモデル名を書くだけでコントローラ内から $this->Model と参照することができますが、プラグインの中では次のように書く必要があります。

<?php
class PizzaOrdersController extends PizzaAppController
{
  var $name = "PizzaOrders";
  var $uses = array("Pizza.ModelA");
}
?>

ModelA を使うために Pizza.ModelA としている点に注目してください。

プラグイン同士なんだから近くのオブジェクトを呼んでくれても良いのに…、と思います。

トップページへ戻る / 前のページへ戻る

back to top