habtm 関係にある Model のテーブルで、table prefix を異ならせると、モデルの association link に失敗する

52 views
Skip to first unread message

custar

unread,
Aug 11, 2009, 11:05:49 AM8/11/09
to CakePHP-ja
3つのテーブルを用意します。

1. b_bs
2. c_cs
3. b_bs_cs

B (b_bs) habtm C (c_cs) の関係にあるとします。

b_bs, c_cs のように、両テーブルの prefix を別々にします。
joinTable は b_bs に合わせておきます。

b_bs
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+

c_cs
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+

b_bs_cs
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| b_id | int(11) | NO | | NULL | |
| c_id | int(11) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+

DATABASE_CONFIG を以下のように設定します。

// config/database.php
class DATABASE_CONFIG
{
public $default = array(
'driver' => 'mysql',
'persistent' => false,
'host' => 'localhost',
'database' => 'test_habtm',
'prefix' => 'b_', ................................. (1)
'encoding' => 'utf8'
);
public $sub = array(
'driver' => 'mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'admin1',
'password' => 'admin1',
'database' => 'test_habtm',
'prefix' => 'c_', ................................. (2)
'encoding' => 'utf8'
);
}

(1),(2) のように prefix が異ならせておきます。

この設定で、それぞれの index action にアクセスしてみると、

http://localhost/sandbox/test_habtm/bs/index ............... Error
http://localhost/sandbox/test_habtm/cs/index ............... OK

となります。Error 内容は、

| Missing Database Table
| Error: Database table c_bs_cs for model BsC was not found.

joinTable の prefix は 'b_' としているのに、何故 prefix 'c_' でテーブルにアクセスするのか?

custar

unread,
Aug 11, 2009, 11:45:36 AM8/11/09
to CakePHP-ja
joinTable の prefix を付け間違えているのは、以下の部分。

// model.php
function __generateAssociation($type)
{
...
$this->{$joinClass} = new AppModel(array(
'name' => $joinClass,
'table' => $this->{$type}[$assocKey]['joinTable'],
'ds' => $this->useDbConfig ....................................
(3)
));
...
}

B->C->BsC の繋がりを作っているところと思われる。
this は C だから、joinClass の生成には C の useDbConfig が使われることになる (3)。


function setDataSource($dataSource = null)
{
$oldConfig = $this->useDbConfig;

if ($dataSource != null)
{
$this->useDbConfig = $dataSource;
}

$db =& ConnectionManager::getDataSource($this->useDbConfig);

if (!empty($oldConfig) && isset($db->config['prefix'])) ............
(4)
{
$oldDb =& ConnectionManager::getDataSource($oldConfig);

if (!isset($this->tablePrefix) ||
(
!isset($oldDb->config['prefix']) ||
$this->tablePrefix == $oldDb->config['prefix']
))
{
$this->tablePrefix = $db->config['prefix']; ....................
(5)
}
}
elseif (isset($db->config['prefix'])) ..............................
(6)
{
$this->tablePrefix = $db->config['prefix'];
}

...
}

この時 joinClass (BsC) の

oldConfig = sub
dataSource = sub

なので (5) まで進み、ここで BsC の tablePrefix が sub の "c_" と設定されてしまう。


さて、どうやって回避しよう。

custar

unread,
Aug 11, 2009, 11:56:22 AM8/11/09
to CakePHP-ja
そもそも (4),(6) の if はこれでいいのだろうか?

if (!empty($oldConfig) && isset($db->config['prefix'])) ... (4)

elseif (isset($db->config['prefix'])) ..................... (6)

custar

unread,
Aug 11, 2009, 3:20:47 PM8/11/09
to CakePHP-ja
決め打ちだけど、今はこれしか浮かばない。

// bs_c.php
class BsC extends AppModel
{
public $name = 'BsC';
public $useDbConfig = 'default';
}

joinTable を扱う Model を用意。
そこで $useDbConfig を明示的に指定。

// app_model.php
protected function setTablePrefix()
{
if (!class_exists($this->name))
{
if (App::import('Model', $this->name))
{
$obj = new $this->name();

if ($this->tablePrefix != $obj->tablePrefix)
{
$this->tablePrefix = $obj->tablePrefix;
}
}
}
}

joinTable を扱う Model なんて普通生成しないので、
class_exists() でフィルタリングし、
後は tablePrefix の比較。

this を BsC にキャストできたら、ちょっと楽なんだろうけど。

custar

unread,
Aug 11, 2009, 10:54:21 PM8/11/09
to CakePHP-ja
if の階層深いけど、取り敢えず。
これで BsC Model クラス ファイルは不要になる。

仮定していること
- AppModel そのもののインスタンスは、joinTable を扱う Model のみ


// app_model.php
protected function setTablePrefix()
{
if (get_parent_class($this) == 'Model')
{
$mgr = ConnectionManager::getInstance();
$ds = ConnectionManager::getDataSource($this->useDbConfig);
$tables = $ds->listSources();
$useTable = $this->tablePrefix . $this->useTable;

if (!in_array($useTable, $tables))
{
$oldConfig = $this->useDbConfig;

foreach ($mgr->enumConnectionObjects() as $key => $data)
{
if ($key != $oldConfig)
{
$config = $mgr->config->{$key};
$useTable = $config['prefix'] . $this->useTable;

if (in_array($useTable, $tables))
{
$this->useDbConfig = $key;
$this->tablePrefix = $config['prefix'];
break;
}
}
}
}
}
}
Reply all
Reply to author
Forward
0 new messages