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

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でセッションが切れてしまう

2010.02.01    CakePHP, IE, 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)

CakePHP paginatorのsortだけを使う

2010.01.28    CakePHP, PHP, 馬場      baba   

CakePHPのpaginatorは、ページングの他にソートもやってくれて大変便利です。

しかし、たまに「ソートはするけどページングしたくない」ということもありますよね?
そんなとき、$this->paginate['limit'] = null のようにやると、Zero Divide でエラーになります。paginatorの中ではそんなことは想定されていません。

ゼロからソート部分を作るのも面倒なので、paginatorのソート部分だけを無理矢理使わせてもらいましょう。
コントローラで以下のように処理します。

//手動でsort
$order = null;
if (isset($this->passedArgs['sort'])) {
	$direction = null;
	if (isset($this->passedArgs['direction'])) {
		$direction = strtolower($this->passedArgs['direction']);
	}
	if ($direction != 'asc' && $direction != 'desc') {
		$direction = 'asc';
	}
	$order = array($this->passedArgs['sort'] => $direction);
}

//paginate処理はするが、そのデータは使わない
$this->paginate();

//全件を表示
$result = $this->Model->find('all', array('order' => $order)));

この方法なら、ビューでは、

$paginator->sort(’なまえ’, ‘User.name’);

のように、普通にpaginatorを使うときの同じようにかけます。

強引であまり褒められた方法ではありませんが、これで目的は達成できました。

CakePHP+PHP5.3でstrtotimeエラー

2009.11.25    CakePHP, PHP, 馬場      baba   

XAMPP最新版1.7.2では、PHPのバージョンが5.3になっています。

この環境でCakePHP(1.2.5)を使うと、以下のようなエラーが出ることがあります

Warning: strtotime() [function.strtotime]: It is not safe to rely on the system’s timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected ‘Asia/Tokyo’ for ‘9.0/no DST’ instead in …

この場合、cakeのapp/config/core.phpで以下の行のコメントアウトを外せばOKです。

date_default_timezone_set(’UTC’)

メッセージそのままで当たり前のことですが・・・

#関係ないですが、なんでCakePHP 1.2.5をインストールすると1.2.4.8284と出るんでしょうね?

JavaScriptって便利ですね

JavaScriptってあまり好きじゃないけど便利なものありますね。
先達者の皆様にいつも感謝です。

中でも普段の受注制作で使えそうで、
個人的に興味がある/好きなもの紹介してみます。

Anything Slider
http://css-tricks.com/examples/AnythingSlider/#panel-5
画像とか動画とかスライドできちゃいます。
ちょっとした“魅せ”にはいいですよね。
以外と感動してくれるお客様もいそうです。

CakePHP×FCK Editor
http://d.hatena.ne.jp/slywalker/20081125/1227621414
CMSで自由に画像を加えたりフォーマットをいじるためには、
HTMLを許容したり自由度を下げたりしないといけないですよね。
WordPress-likeなエディタUIを加えてあげることで
編集しやすいCMSをつくるのもいいですよね。

cakephp paginator 引数を保った状態でsort

2009.06.26    CakePHP, PHP, 渡辺      peter   

小一時間ほどハマったのでメモ程度に。

cakephpでpaginateするために専用helperを使っている方は多いと思います。

適当に他サイトからコピペしても動くことと、コード事態が汚いことが相まって、

細部まであまり見ておりませんでした。

絞り込んだ結果をpaginateやらsortする機会する場合は、

大抵コピペしたコードだは動作しないので

その場合の処置を共有します。

$paginator->options(array(’url’ => $this->passedArgs));

urlできたパラメータを引き継げよ、って指示します。

CakePHP Auth Componentの暗号化を無視する方法

2009.04.16    CakePHP, 渡辺      peter   

WEBシステムを構築する際に必ず構築するログイン処理、CakePHPでは

Auth Componentと呼ばれる便利なControllerコンポーネントが存在します。

これを使うと、特にコードを書くことなく

ログイン処理、ログアウト処理、ユーザ作成・編集時のパスワードハッシュ化を実現できます。

私は説明が下手なので、要点をうまくまとめているサイトをご紹介します。

http://blog.ne2ma2.com/archives/161

暗号化の部分はModelやControllerの部分に戻ってくる時点で行われているため、

CakePHPのバリデーションを使うことができません。

ユーザの編集時にパスワードが勝手に二重でハッシュ化され、以後ログインできなくなったり、

パスワードを必ず入力してもらうことになります。

上記サイトでは異なるモデルでバリデーションを定義することでこれを解決していますが、

もう1つ単純なやりかたとして、暗号化の部分を手動で行う、があります。

Authコンポーネントのファイルのstartup()という関数の中で必ず呼ばれている

$this->data = $controller->data = $this->hashPasswords($controller->data);

をコメントアウトするだけです。

後は、ハッシュ化が必要なときに適時上記と同じ関数をコントローラーで呼ぶだけです。

例: $this->data = $this->Auth->hashPasswords($this->data); //ユーザの入力したパスワードをハッシュ化

あと、ログイン時に入力されたユーザ名とパスワードをハッシュ化して認証する都合上、

Authコンポーネントのログイン関数にも手動で暗号化処理を加えてあげる必要があります。

if (empty($data)) {
$data = $this->data;
}
else{
$data[$this->userModel.".".$this->fields['password']] = $this->password($data[$this->userModel.".".$this->fields['password']]);
}

人様に用意して頂いたものにケチをつけるのは好きではありませんが、

システムの動きとして自然な気がします。

CakePHPのfind, findAll, findCount, delete, deleteAll

2009.01.21    CakePHP, PHP, 伊藤   タグ: , , —    tomotaka   

伊藤です。

開発ブログには初投稿です。
よろしくお願いします。

今回は最近話題のCakePHPでモデル(データベーステーブル)からデータを取り出したり、カウントしたりするためのfind, およびfindCountの使い方をまとめてみたいと思います。
そんなの本でもWebにでもどこでも載ってるよ!と言われるかもしれませんが、実際に使ってみて1.1に沿ったの内容の解説が多かったり、内容が不正確であったりと困惑したためCakePHP1.2で実際に動くコードということで改めてまとめてみたいと思います。
また、データを削除する際に便利なdeleteやdeleteAllについても触れてみます。
本稿で想定しているCakePHPのバージョンは1.2です。
最近1.2の安定版も出ましたので、これからCakePHPの開発というと1.2が主流になってくるのではないかと思います。

findおよびfindAll
データベースからデータを取得するにはモデルオブジェクトのfindメソッドやfindAllメソッドを使うのがCakePHPの一般的なやり方です。
findAllは以下のようにして使います。タイプ数が少なくて便利なのですが、今後find(”all”, …)形式の呼び出しに統一されやがてfindAllという名前のメソッドはなくなる予定だそうです。

findAllの使い方:

// 取得するフィールドや取り出す並び順が決まっている場合
function someaction() {
    $condition = array("hoge" => 1);
    $fields = array("id", "hoge", "hogege", "moge", "mogege");
    $order = "moge";
    $records = $this->Model->findAll($condition, $fields, $order);
}

// 並び順デフォルト、取得するフィールド全ての場合
function someaction2() {
    $condition = array("hoge" => 1);
    $records = $this->Model->findAll($condition);
}

findで条件に合致する最初の1件のレコードを取得する:

// 取得するフィールドや取り出す並び順が決まっている場合
function someaction() {
    $condition = array("hoge" => 1);
    $fields = array("id", "hoge", "hogege", "moge", "mogege");
    $order = "moge";
    $record = $this->Model->find(
        "first",
        array("conditions" => $condition, "fields" => $fields, "order" => $order)
    );
}

// 並び順デフォルト、取得するフィールド全ての場合
function someaction2() {
	$condition = array("hoge" => 1);
	$record = $this->Model->find("first", array("conditions" => $condition));

	// $record= $this->Model->findByHoge(1);
	// という書き方もできます
}

findで条件に合致する全てのレコードを取得する:

// 取得するフィールドや取り出す並び順が決まっている場合
function someaction() {
    $condition = array("hoge" => 1);
    $fields = array("id", "hoge", "hogege", "moge", "mogege");
    $order = "moge";
    $records = $this->Model->find(
        "all",
        array("conditions" => $condition, "field" => $fields, "order" => $order)
    );
}

// 並び順デフォルト、取得するフィールド全ての場合
function someaction2() {
	$condition = array("hoge" => 1);
	$records = $this->Model->find("all", array("conditions" => $condition));
}

findByIdでフィールド’idが特定の値の1件のレコードを取得する:

function someaction() {
	$record = $this->Model->findById(1); // idが1のレコードを取り出す
}

帰ってくるデータ:

// findAllやfind("all", ...)で帰ってくる形式(複数レコード)
array(
	[0] => array(
		"ModelName" => array(
			"field1" => "value1",
			"field2" => "value2"
		)
	),
	[1] => array(
		"ModelName" => array(
			"field1" => "value1",
			"field2" => "value2"
		)
	),
	...
)

// findByXxxやfind("first", ...)で帰ってくる形式(単一レコード
array(
	"ModelName" => array(
		"field1" => "value1",
		"field2" => "value2"
	)
)

findCountで条件にマッチする行数を数える:

function someaction() {
	$condition = array("year" => 2009);
	$count = $this->Model->findCount($condition);
	// array("conditions" => $conditition)のような形式のオプション配列ではない,
	// findAllに与えるようなarray("field" => "value")形式の配列でよい
}

findAllやfind(”all”, …)で取り出してcount()してもいいのですが、マッチする件数が多い場合はメモリを食いつぶしてしまいますので、
RDBMSに数えてもらうようにするのがスマートな実装でしょう。

deleteメソッドやdeleteAllメソッドでレコードを削除する

// deleteメソッド: 単一のレコードをIDで指定して削除
function someaction() {
	$deleteTargetID = 1;
	if ($this->Model->delete($deleteTargetID)) {
		// 成功
	} else {
		// 失敗
	}
}

// deleteAllメソッド: 条件にマッチする複数のレコードを一括して削除する
function someaction2() {
	$deleteCondition = array("year" => 2009);
	if ($this->Model->deleteAll($deleteCondition)) {
		// 成功
	} else {
		// 失敗
	}
}

deleteメソッドもdeleteAllメソッドも成功すると真を返します。
ちなみにdeleteAllにarray(”conditions” => $condition)のようなfindのオプション形式の配列を渡すと必ず真が帰ってくるので気を付けましょう!

CakePHPはまだ日本語のドキュメントも不十分でWeb上にも1.1と1.2の情報が完全に整理されないまま混在しているため、
開発も手探り感が強いですが基礎の部分をしっかり理解して使いこなせば役に立つツールのひとつだと思います。
今後もCakePHPに関する情報を掲載していきたいと思いますのでよろしくお願いします。
コメントやトラックバックでのツッコミも歓迎致します!

参考:

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