カテゴリー
cakephp2

Cakephp2 DBの変更がmodelに反映されない

データベーステーブルの構造を変更(カラムの追加や変更など)したのに、追加したカラムに対して$result[‘Model’][‘field_added’]とかでアクセスしたら怒られました。

本番環境でのみ発生するので最初悩みましたが...。

Debugモードが0の場合、Cakephpではモデルのキャッシュが結構長い時間有効になっているようで、モデルにテーブルの変更内容が反映されないことが原因でした。

で、ググって見つけたサイトがCakePHPでテーブル構造を変更しても反映されないでしたが、単純に/app/tmp/cache/modelsにある問題のテーブルに該当するキャッシュを消しても解決しませんでした。

んでもって、次に消したキャッシュファイルが、
/app/tmp/cache/persistent/myapp_cake_core_method_cache

これで変更が反映されました。

めでたし、めでたし。

カテゴリー
cakephp cakephp2 PHP

Cakephp2.xでのBlackHoleまとめ

CakePHPでSecurityコンポーネントを使っていると、時折発生するBlackHole。
発生時はエラーログを確認するしか詳細な原因がわからないのですが、多くの場合は以下のどれかに該当するのではないでしょうか。

  • CSRFチェックのトークンがUseOnceなのにフォーム送信後にブラウザの「戻る」とか押してからフォームを再送した。
  • フォームを生成してから30分過ぎた。(トークンのデフォルト有効期限が30分)
  • JavaScriptでHidden項目を書き換えた。
  • JavaScriptで入力項目を追加したり取り去ったりした。

ブラウザの戻るを想定する場合はトークンを複数回使えるように設定しなければいけません。(ただし、セキュリティー的には若干低下します)

複数回使いまわせるようにするにはControllerのbeforeFilterなどで以下のようにcsrfUseOnceをfalseにします。

$this->Security->csrfUseOnce = false;

また、トークンのデフォルト有効期限が短すぎると判断される場合はbeforFilterで

$this->Security->csrfExpires = '+1 hour';

とするか、または$componentsの設定で以下のように設定すると良いでしょう。

public $components = array(
    'Security' => array(
        'csrfExpires' => '+1 hour'
    )
);

そもそもCSRF対策不要だよ!ってアプリケーションでは、

public $components = array(
    'Security' => array(
        'csrfCheck' => false
    )
);

として、CSRF対策機能をばっさり無くすことも可能ですが、セキュリティー的に問題が残るため、多くの場合はおすすめできません。

あと、JavaScriptでフォームの中身を変更すると、ことごとくBlackHoleに吸い込まれる事がありますが、Securityコンポーネントを有効にするとデフォルトでついてくる機能「FormTamperingPrevention」のおかげです。

Cakeがフォームを生成した時にフォームの内容をまるごとハッシュ化しており、フォーム送信時にフォームの内容に差異が生じていると、フォームを不正に改変されたとみなしてBlackHoleに吸い込んでくれる機能です。

ただし、JavaScriptでHiddenに値をセットしたり、Ajaxを使う場合に項目を動的に追加したりなんかしてもBlackHoleに吸い込んでくれるので、そのような場合は以下の何れかの対策が必要です。

FormTamperingPreventionをまるっと無効にする場合はbeforeFilterで以下のように指定します。

$this->Security->validatePost = false;

また、特定のフィールドだけFormTamperingPreventionの対象外としたい場合(JavaScriptで値をセットするhidden項目など)は、ViewのFormHelperを用いて以下のように指定することで回避できます。

$this->Form->unlockFields('Model.field_name');

 

BlackHoleに吸い込まれた時の処理

BlackHoleに吸い込まれた場合、デフォルトではDebugモードにもよりますが、プロダクションモードでは真っ白なページが表示され、404エラーが返されてしまいます。
では実際に何らかの問題が生じてBlackHoleに吸い込まれるべくして吸い込まれた場合、どのようにハンドリングすればスマートでしょうか。

ここはひとまずCakePHPのCookBookにのっとってblackholeメソッドを用意し、そちらに処理を任せるようにしましょう。

public function beforeFilter() {
    $this->Security->blackHoleCallback = 'blackhole';
}

public function blackhole($type) {
    // handle errors.
}

上記はCookBookからの引用になります。またblackholeメソッドのパラメータ$typeは以下の値をとるようですが、内容はCookBookのほぼ直訳になります。

  • ‘auth’ フォームバリデーションエラーまたはコントローラ/アクションの不一致によるエラー。
  • ‘csrf’ CSRFエラー
  • ‘get’ HTTPメソッド制限違反
  • ‘post’ HTTPメソッド制限違反
  • ‘put’ HTTPメソッド制限違反
  • ‘delete’ HTTPメソッド制限違反
  • ‘secure’ SSLメソッド制限違反

なので、blackholeメソッド側で$typeを参照し、エラー内容に沿ったメッセージを表示してあげると良さそうです。

カテゴリー
cakephp2

Cakephp2.xでモデル内から別のモデルを利用する

Fat modelな書き方をしていると、モデル内から別のモデルを呼び出したい場面が良く出てくる。

モデル内で使いたい別のモデルをimportし、必要な場所でインスタンス化して呼び出せばOK。
下記例はhogeモデル内からfugaモデルを呼び出す準備。

//hoge.phpのクラスに追記
App::import('Model','fuga');
//インスタンス化
$fuga= new Fuga;

//モデルfugaのメソッドを使用できる
$fuga->find('all');

hogeモデル全体で使いたい場合などは先頭でインスタンス化しておくが、その際$this->fugaにモデルインスタンスを格納すれば、hogeモデル内のどのメソッドからも$this->fuga->find(‘all’);できる。
まぁあたりまえっちゃぁ、あたりまえか。