仙台の山奥で自転車に乗ったり転んだり

愛車の GIOS でサイクリングしたりポタリングしたり、それをブログに記録してみたり。ロードバイクや自転車や坂のことを書いてみたり。ときたまプログラムのことを忘れないようにメモってみたり。

CakePHPで複合主キーと仲良くやる

CkaePHPのO/Rマッパー(?)は複合主キーに非対応なので、自前でCRUDを実装する前提の方がうまくいくと思います。
DataSourceクラスが便利なので、上手く使えば意外とスムーズに実装できたりできなかったり……。

でも、せっかくだから自動化できる部分は活用したい!と思うので、Fixtureを無理やり複合主キーに対応させてみました。

といっても、Bakeで生成すれば意外とちゃんとFixtureのオブジェクトはできあがります。
でも、主キーが配列なのでユニットテストの実行時にエラーがでテーブルが作成できません。

	var $fields = array(
		'field01' => array('type' => 'integer', 'length' => '22'),
		'field02' => array('type' => 'integer', 'length' => '22'),
		'field03' => array('type' => 'string', 'length' => '1'),
		'field04' => array('type' => 'integer', 'length' => '22'),
		'field05' => array('type' => 'integer', 'length' => '22'),
		'field06' => array('type' => 'integer', 'length' => '22'),
		'field07' => array('type' => 'integer', 'length' => '22'),
		'field08' => array('type' => 'integer', 'length' => '22'),
		'field09' => array('type' => 'integer', 'length' => '22'),
		'indexes' => array('PRIMARY' => array('unique' => true, 'column' => array('field01', 'field02', 'field03'))),
		'tableParameters' => array()
	);

PostgreSQLであれば下記のように実直に文字列に書き換えてやればちゃんと動きます。ダブルクォテーションでエスケープしてやらないと、「"field01, field02, field03"」というフィールドとして処理されちゃいます。(たぶん、MySQLだったらバッククォートで囲めばいいのかと)

	var $fields = array(
		'field01' => array('type' => 'integer', 'length' => '22'),
		'field02' => array('type' => 'integer', 'length' => '22'),
		'field03' => array('type' => 'string', 'length' => '1'),
		'field04' => array('type' => 'integer', 'length' => '22'),
		'field05' => array('type' => 'integer', 'length' => '22'),
		'field06' => array('type' => 'integer', 'length' => '22'),
		'field07' => array('type' => 'integer', 'length' => '22'),
		'field08' => array('type' => 'integer', 'length' => '22'),
		'field09' => array('type' => 'integer', 'length' => '22'),
		'indexes' => array('PRIMARY' => array('unique' => true, 'column' => '"field01", "field02", "field03"')),
		'tableParameters' => array()
	);

Oracleであれば↓でいけました。

	var $fields = array(
		'field01' => array('type' => 'integer', 'length' => '22'),
		'field02' => array('type' => 'integer', 'length' => '22'),
		'field03' => array('type' => 'string', 'length' => '1'),
		'field04' => array('type' => 'integer', 'length' => '22'),
		'field05' => array('type' => 'integer', 'length' => '22'),
		'field06' => array('type' => 'integer', 'length' => '22'),
		'field07' => array('type' => 'integer', 'length' => '22'),
		'field08' => array('type' => 'integer', 'length' => '22'),
		'field09' => array('type' => 'integer', 'length' => '22'),
		'indexes' => array('PRIMARY' => array('unique' => true, 'column' => 'field01, field02, field03')),
		'tableParameters' => array()
	);

あと、実際のデータベースからサンプルデータをコピーするときとか、デフォルトのままの条件だと「WHERE 1=1 LIMIT 10」という条件でSQLが作成されます。これ、もちろんORACLEだと実行できないので、「WHERE ROWNUM <= 10」とかに必要に応じて変更してやる必要あり。
あと、こういう対応が必要なのって、新たにデータ定義するようなケースじゃなく、既存業務のデータ定義をWebと連携する……、とかだと思うのでテーブル名とか規約に則ってないのを無理やるあわせるとか、裏道みたいなコツ次第でけっこう便利な設定がある気がする。