hasAndBelongsToManyのバグ
Akelosでn対mのモデルを扱うときに新しくjoin_tableにレコードを追加したい場合多分以下のようにするのだと思う。
<?php Ak::import('User,Group'); $user_model = new User(); $group_model = new Group(); $user = $userModel->find(1); $user->group->add($group_model->find(array(1,2,3))); $user->save(); ?>
結果join_tableには以下のように追加されるはず。
SELECT * FROM groups_users; +----+---------+----------+ | id | user_id | group_id | +----+---------+----------+ | 1 | 1 | 1 | | 2 | 1 | 2 | | 3 | 1 | 3 | +----+---------+----------+
実際試してみると期待通りにうまくいく。ちなみにUserモデルとGroupモデルはそれぞれ以下のような感じで定義されている。
models/user.php
<?php class User extends AkActiveRecord { var $habtm = 'groups'; } ?>
models/group.php
<?php class Group extends AkActiveRecord { var $habtm = 'users'; } ?>
ここで、Userモデルにもう一つn:mのroleモデルを定義すると
models/user.php
<?php class User extends AkActiveRecord { var $habtm = array('groups', 'role'); } ?>
models/tag.php
<?php class Role extends AkActiveRecord { var $habtm = array('users'); } ?>
となる。ここまではいい。
ここで、Userモデルからgroupとroleをそれぞれjoin_tableに追加してみる。
<?php Ak::import('User,Group,Role'); $user_model = new User(); $group_model = new Group(); $role_model = new Role(); $user = $userModel->find(1); $user->group->add($group_model->find(array(1,2,3))); $user->role->add($role_model->find(array(1,2,3))); $user->save(); ?>
期待された結果は以下のような感じになるはず
SELECT * FROM groups_users; SELECT * FROM roles_users; +----+---------+----------+ +----+---------+---------+ | id | user_id | group_id | | id | user_id | role_id | +----+---------+----------+ +----+---------+---------+ | 1 | 1 | 1 | | 1 | 1 | 1 | | 2 | 1 | 2 | | 2 | 1 | 2 | | 3 | 1 | 3 | | 3 | 1 | 3 | +----+---------+----------+ +----+---------+---------+
ところが実際にやってみると、groupにはちゃんと追加されたがroleには何も追加されない。ソース追いかけて調べた結果akelosのバグっぽい。問題の箇所は以下のとおり
lib/AkActiveRecord/AkHasAndBelongsToMany.php
<?php // 849行目付近 function _afterCallback(&$object) { static $joined_items = array(); $success = true; $object_id = $object->getId(); foreach (array_keys($object->hasAndBelongsToMany->models) as $association_id){ $CollectionHandler =& $object->hasAndBelongsToMany->models[$association_id]; $options = $CollectionHandler->getOptions($association_id); $class_name = strtolower($CollectionHandler->getOption($association_id, 'class_name')); if(!empty($object->$association_id) && is_array($object->$association_id)){ $this->_removeDuplicates($object, $association_id); foreach (array_keys($object->$association_id) as $k){ if(!empty($object->{$association_id}[$k]) && strtolower(get_class($object->{$association_id}[$k])) == $class_name){ $AssociatedItem =& $object->{$association_id}[$k]; // This helps avoiding double realation on first time savings if(!in_array($AssociatedItem->__hasAndBelongsToManyMemberId, $joined_items)){ $joined_items[] = $AssociatedItem->__hasAndBelongsToManyMemberId; if(empty($AssociatedItem->hasAndBelongsToMany->__joined) && $AssociatedItem->isNewRecord()? $AssociatedItem->save() : true){ $AssociatedItem->hasAndBelongsToMany->__joined = true; $CollectionHandler->JoinObject =& $CollectionHandler->JoinObject->create(array($options['foreign_key'] => $object_id ,$options['association_foreign_key'] => $AssociatedItem->getId())); $success = !$CollectionHandler->JoinObject->isNewRecord() ? $success : false; }else{ $success = false; } } } } } return $success; // ←多分こいつが原因。 } return $success; // ←正しいのはここ。 }
上記はsaveやupdate時に自動実行されるメソッドですが、最後のreturnの位置がおかしい。foreach内でreturnしちゃってるから、2個目以降のhabtmの実行がキャンセルされてしまっている。問題の箇所を削除して正しい位置に書き換えたらすんなりと動いた。
trac見たけどチケット上がってないなー。ユーザが少ないのかやっぱ。