Techracho

hasAndBelongsToManyで検索する

このエントリーをはてなブックマーク Share
2010.06.06    CakePHP, PHP, 馬場   タグ: , , —    baba   

CakePHPでfindする際、belongsToしているオブジェクトを条件にすることができます。

//例:datas → categories で、dataはcategoryに属している(data belongsto category)
$datas = $this->Data->find('all', array(
    'conditions' => array('Category.id' => 5)
));

しかし、hasManyしているものに関しては、そのまま実行すると unknown column と怒られてしまいます。
hasManyでは、デフォルトでJOINされないためです。

hasAndBelongsToManyでも、同じことが発生します。

これを回避するには、明示的なJOINを実行する必要があります。

たとえば、データが複数のカテゴリに属するように、data hasandbelongstomany category にした場合、以下のように指定します。

//例:datas← categories_datas→ categories で、
//hasAndBelongsToManyしている
$datas = $this->Data->find('all', array(
    'joins' => array(array(
        'type' => 'LEFT',
        'alias' => 'CategoriesData',
        'table' => 'categories_datas',
    )),
    'conditions' => 'CategoriesData.data_id = Data.id'
));

joinsは配列なので、いったん取得して[]演算子で追加するか、このように最初から二重の配列で指定してあげる必要があります。
(もちろん、既存のJOINを消してしまわないかチェックしつつ)

ところで、hasAndBelongsToManyでググると、途中で「破産」がたっぷり出てきた後、↓のようになって若干悲しくなりますね・・・

hasAndBelongsToManyで検索する途中

hasAndBelongsToManyで検索する途中

CakePHP loginRedirect

このエントリーをはてなブックマーク Share
2010.05.15    CakePHP, PHP, 馬場      baba   

CakePHPのAuthコンポーネントで、ログイン後のアクションを指定するには、

//app_controller::beforeFiler()あたり
$this->Auth->loginRedirect(array('controller' => 'users', 'action' => 'index'));

といったことをしますが、セッションの状態によって、動作しないことがあります。

安全にやるなら、

//app_controller::beforeFitler()あたり
$this->Auth->autoRedirect = false;

//ログインアクション
$this->redirect('users', 'index');

のようにやるのが良さそうです。

beforeSaveを使おう

このエントリーをはてなブックマーク Share
2010.05.10    CakePHP, PHP, 馬場      baba   

CakePHPの基本ですが、modelのbeforeSaveは大変便利です。

たとえば、「最終更新ユーザID」を保存するときは、

//app/app_controller.php
class AppController extends Controller {
    public function beforeFilter() {
        Configure::write('logined_user_id', $this->Auth->user('id');
    }
}

//app/models/hoge_model.php
class HogeModel extends AppModel {
    public function beforeSave() {
        $this->data[$this->alias]['user_id'] = Configure::read('logined_user_id');
        return true;
    }
}

たったこれだけですし、応用も利きますね。

なお、モデルから他のモデルを使う際は、以下のように明示的にnewしておく必要があります。

class HogeModel extends AppModel {
    public function beforeSave() {
        App::import('Model', 'PiyoModel');
        $PiyoModel = new PiyoModel();
        //$PiyoModel->find('all'); などできる
    }
}

hasManyやbelongsToのassociationが設定されている場合は、この手続きは不要なので、設定しておいてContainableで外すというのも楽かもしれません。

CakePHP 1.3 HTMLヘルパー

このエントリーをはてなブックマーク Share
2010.04.24    CakePHP, PHP, 馬場      baba   

HTMLヘルパーでリンクを張る際、

$html->link('りんく!', '/pages/index');

などとやりますが、画像にリンクを張る際は

$html->link($html->image('btn.png'), '/pages/index',
null, null, false);

とやらないと、エスケープされてしまっていました。

CakePHP 1.3では、ルールが変わって、

$html->link($html->image('btn.png'), '/pages/index',
array('escape' => false));

のようにやるようです。

CakePHP OAuthで注意

このエントリーをはてなブックマーク Share
2010.04.17    CakePHP, PHP, 馬場      baba   

CakePHPのAuth/Securityコンポーネントは、できの悪いところも多いですが、簡単なのでとりあえず使うのに便利です。

しかし、最近はやりのOAuthを使う場合、core.php で Security.level が middle 以上に設定されていると、コールバック後にエラーになってしまうことがあります。

middle 以上では、セッションのリファラーチェックが働くため、コールバック時にリファラーが違うためにセッションが切れてしまうようですね。
通常、セッションにキーを保存しておくと思うので、これでは困ります。

色々解決方法がありますが、簡単なのは、

Configure::write('Security.level', 'low');

です。

もちろん、これではセキュリティが下がるという懸念もありますが、
むしろhighに設定して内部でどんなセキュリティ対策をしているか理解しない方が遙かに危険です。

lowに設定することで、セッション時間が延び、リファラチェックやフォームのハッシュ整合性チェックなどが無視されますが、それらを理解した上で、必要に応じてこれらを自前で対策した方が、トータルで手間と安全性が優れるケースもありそうです。

CakePHP afterFind()で注意

このエントリーをはてなブックマーク Share
2010.04.16    CakePHP, PHP, 馬場      baba   

CakePHPで、DBから取得した値を加工したり、Associationだけでは解決できない別データを付加したい時など、afterFind()が便利です。

しかし、findではcountなどをすることもあるので、うっかりするとFatal Errorが出てしまうことがあります。

Cannot use string offset as an array in ~.php

データの形式をきちんと確認しましょう。

public function afterFind($results) {
    foreach ($results as $key => $val) {
        //↓ここのis_array()が重要
        //$this->User->find('count')などをやった場合、2などの数値が入るため
        if (isset($results[$key]['User']) && is_array($results[$key]['User'])) {
            $results[$key]['User']['name'] = $results[$key]['User']['familyname'] . $results[$key]['User']['firstname'];
        }
    }
}

CakePHP Delimiter must not be alphanumeric or backslash

このエントリーをはてなブックマーク Share
2010.04.13    CakePHP, PHP, 馬場      baba   

CakePHP で validateやsetをしたときに、以下のようなエラーが出ることがあります。

Warning (2): preg_match() [function.preg-match]: Delimiter must not be alphanumeric or backslash [CORE\cake\libs\model\model.php, line 2567]

これは、notEmptyをnotEntpyのようにtypoしたときなど、バリデーションルールが存在しないときに出てくるようです。
こんなことで30分も無駄遣いしないように気をつけましょう。

古いCakePHPで、簡易的にPHP5.3対応

このエントリーをはてなブックマーク Share
2010.04.12    CakePHP, PHP, 馬場      baba   

1.2.6より古いCakePHPでは、PHP5.3以上にしたときに大量のE_DEPRECATEDエラーが出ます。

php.iniでerror_reportingを設定しても、debugモードが2の時は上書きされてしまうため、とても邪魔です。
セッションが切れたりするので、とりあえず無視したいですね。

CakePHPをアップデートするなどまともな解決法もありますが、一番簡単なのは、CakePHPのerror_reporing設定している部分を上書きしてしまうことです。

project/cake/libs/configure.php : 293

if (isset($config['debug'])) {
	if ($_this->debug) {
		//この行を編集
		//error_reporting(E_ALL);
		error_reporting(E_ALL & ~E_DEPRECATED);

		if (function_exists('ini_set')) {
			ini_set('display_errors', 1);
		}

		if (!class_exists('Debugger')) {
			require LIBS . 'debugger.php';
		}
		if (!class_exists('CakeLog')) {
			require LIBS . 'cake_log.php';
		}
		Configure::write('log', LOG_NOTICE);
	} else {
		error_reporting(0);
		Configure::write('log', LOG_NOTICE);
	}
}

強引ですが簡単ですね。

CakePHPでDBのエンコーディングを指定

このエントリーをはてなブックマーク Share
2010.02.16    CakePHP, PHP, 馬場   タグ: —    baba   

PHPでMySQL 5をUTF8にして使う場合、文字が全部????になってしまう問題を防ぐため、

default-character-set=utf8
character-set-server=utf8
collation-server=utf8_general_ci
skip-character-set-client-handshake

を指定する、というのが定石になっています(よね?)。

しかし、共用サーバなどでmy.iniを編集できない場合、mysql_set_charset() などを使うと思います。

CakePHPでは、bootstrapでDB設定を読んで手動で設定するのも面倒だな、と思っていたら、さすがはCakePHP。便利な設定が。
app/config/database.php

var $default = array(
	'driver' => 'mysql',
	'persistent' => false,
	'host' => 'localhost',
	'login' => 'baba',
	'password' => 'hoge',
	'database' => 'testdb',
	'prefix' => '',
    'encoding' => 'utf8',
);

このようにencodingを指定するだけで、勝手にSET NAMESやmysql_set_charset()を実行してくれます。便利便利。

古いIEだとAjaxのUserAgentが変わるので、CakePHPでセッションが切れてしまう

このエントリーをはてなブックマーク Share
2010.02.01    CakePHP, javascript, 馬場      baba   

CakePHPでjQueryを使ったAjaxを実装した際、IEの古いバージョンでのみエラーになる現象に見舞われました。
調べてみると、Ajaxリクエストを送った際にログインが切れているようです。

どうやら原因は、CakePHPのユーザエージェント検証機能でした。
app/config/core.phpで

Configure::write(’Session.checkAgent’, false);

を設定することで解決しました。

つまり、古いIEでは、通常時のリクエストとAjaxリクエストで、ユーザエージェントが違う、ということですね。
これはたぶん、歴史的な理由からXMLHttpRequestがActiveXオブジェクトで実装されていることに由来するのでしょう。

以下に、各ブラウザで確認したユーザエージェントを書いておきます。
PHPで、$_SERVER['HTTP_USER_AGENT'] を表示しています。また、Ajax通信は、jQueryの$.ajax()を使っています。

■Firefox 3.5.6
通常: Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)
Ajax: Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)

■Chrome 4 beta
通常: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.64 Safari/532.5
Ajax: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.64 Safari/532.5

■IE8
通常: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
Ajax: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)

■IE7
通常: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
Ajax: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)

■IE6
通常: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
Ajax: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)

« 新しい投稿古い投稿 »

COPYRIGHT [C] 2009 BEYOND PERSPECTIVE SOLUTIONS LTD. ALL RIGHTS RESERVED.