modelをsetSourceした時はaliasにも気をつけよう
Cakephp1.2の話。
modelで参照するTableを動的に変更するために、cakephpのmodelにはsetSourceというメソッドがあります。
そいつを実行すると確かに$this->find()ってやるとテーブルが変更されているが、戻ってくる配列のKeyが変更されていないことに気づいた!絶望した!
どういうことかを、てけとーなコードで説明する。
<?php class Hoge extends Model { var $name = "hoge"; function getXXX($id) { //Hogeテーブルを見に行く $hoge = $this->read(null,$id); pr($hoge); $this->setSource("Hogehoge"); //Hogehogeテーブルを見に行く。 $hogehoge = $this->findAllByHogeId($id); pr($hogehoge); } }
戻りの配列が泣けてくる。
<?php //$hogeのダンプ Array ( [Hoge] => Array ( [id] => 2 [name] => "ござ" [created] => 2009-03-22 14:03:13 [modified] => 2009-03-22 14:03:13 ) ) //$hogehogeのダンプ Array ( [Hoge] => Array ( [id] => 25 [code] => "CODE-001" [created] => 2009-03-22 14:03:13 [modified] => 2009-03-22 14:03:13 ) )
setSourceしただけじゃ、戻り値のKeyが変更されてない。HogeHogeというKeyで返して欲しい。
別にKey名が違っているぐらいどうってこと・・・、とか思ったりもしたけどやっぱりキモい。むしゃくしゃしたからlibs/models/model.phpのソースを追いかけた。
<?php function setSource($tableName) { $this->setDataSource($this->useDbConfig); $db =& ConnectionManager::getDataSource($this->useDbConfig); $db->cacheSources = ($this->cacheSources && $db->cacheSources); if ($db->isInterfaceSupported('listSources')) { $sources = $db->listSources(); if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) { return $this->cakeError('missingTable', array(array( 'className' => $this->alias, 'table' => $this->tablePrefix . $tableName ))); } $this->_schema = null; } //ここでテーブルを切替えてる。 $this->table = $this->useTable = $tableName; //このtableToModelが変更されているので、戻りのArrayのKeyが変更されると思ってた。 $this->tableToModel[$this->table] = $this->alias; $this->schema(); }
だが、しっかりとKeyは変更されていなかったわけだ。
で、更に追いかけると、findメソッドはdbosource->readを叩いていることがわかった。
そんなかで再帰が無い場合は__filterResults($resultSet, $model);を叩いてreturnしている。ここに犯人がいるに違いない。
<?php //cake/lib/model/datasources/dbo_source.php function __filterResults(&$results, &$model, $filtered = array()) { $filtering = array(); $count = count($results); for ($i = 0; $i < $count; $i++) { if (is_array($results[$i])) { $classNames = array_keys($results[$i]); $count2 = count($classNames); for ($j = 0; $j < $count2; $j++) { $className = $classNames[$j]; //アッー! if ($model->alias != $className && !in_array($className, $filtered)) { if (!in_array($className, $filtering)) { $filtering[] = $className; } //$modelのaliasとclassName(=テーブル名の単数形)が等しい場合はこっち if (isset($model->{$className}) && is_object($model->{$className})) { $data = $model->{$className}->afterFind(array(array($className => $results[$i][$className])), false); } if (isset($data[0][$className])) { $results[$i][$className] = $data[0][$className]; } } } } } return $filtering; }
直接alias参照しているじゃねーか!!!!!!
よって、modelの中でsetSourceした後に$this->alias = "モデル名"ってやると、いつもの配列になります。
setSourceでDB変えた後アソシエーションした場合はどうなるのかは、まだ試してないです><