読者です 読者をやめる 読者になる 読者になる

Life is Really Short, Have Your Life!!

ござ先輩の主に技術的なメモ

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変えた後アソシエーションした場合はどうなるのかは、まだ試してないです><