CakePHP中文手册【翻译】-Model(三)
时间:2007-09-14 来源:youngpand
定义和查询 hasAndBelongsToMany
现在你已经掌握了比较简单的关联,让我们转移到最后一个关联:hasAndBelongsToMany (或 HABTM).最后一个是最难的,它会让你团团转,但是它也是其中最有用的一个。如果你有2个model,而这2个model又一个join表联系在一起,HABTM关联这个时候是有用的。Join表抓住了互相关联的各自的行。
hasMany 和hasAndBelongsToMany的区别是:对于hasMany,关联的model是不共享的。这对我们接下来的工作是非常有用:关联Post和Tag model。当一个Tag属于一个Post时,我们不会让他使用完,我们会继续将他和其他Post关联。
为了实现它,我们需要为这个关系建立正确的表。当然你需要为你的Tag model转被一个“tags“表,Post准备一个”posts“表,但是你也需要为此关联创建一个join表。HABTM join表的命名规则是[复数的model1名]_[复数的model2名],这里,model名按字母排序。
HABTM Join 表: model样例以及他们的Join表
1. Posts 和Tags: posts_tags
2. Monkeys和IceCubes: ice_cubes_monkeys
3. Categories和Articles: articles_categories
HABTM Join表至少需要包含2个他们连接的model的外键。在我们的例子中,"post_id" 和"tag_id"就是我们所需要的。
下面导出的SQL看起来是为我们的Posts HABTM Tags实例准备的哟:
--
-- Table structure for table `posts`
--
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL auto_increment,
`user_id` int(10) default NULL,
`title` varchar(50) default NULL,
`body` text,
`created` datetime default NULL,
`modified` datetime default NULL,
`status` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=MyISAM;
-- --------------------------------------------------------
--
-- Table structure for table `posts_tags`
--
CREATE TABLE `posts_tags` (
`post_id` int(10) unsigned NOT NULL default '0',
`tag_id` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`post_id`,`tag_id`)
) TYPE=MyISAM;
-- --------------------------------------------------------
--
-- Table structure for table `tags`
--
CREATE TABLE `tags` (
`id` int(10) unsigned NOT NULL auto_increment,
`tag` varchar(100) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;
随着我们的表已创建,让我们在Post model里定义关联:
/app/models/post.php hasAndBelongsToMany
class Post extends AppModel
{
var $name = 'Post';
var $hasAndBelongsToMany = array('Tag' =>
array('className' => 'Tag',
'joinTable' => 'posts_tags',
'foreignKey' => 'post_id',
'associationForeignKey'=> 'tag_id',
'conditions' => '',
'order' => '',
'limit' => '',
'uniq' => true,
'finderQuery' => '',
'deleteQuery' => '',
)
);
}
?>
$hasAndBelongsToMany数组是Cake用来创建Post和Tag model之间的关联的。数组中的每个键允许你更进一步配置关联:
1. className (必须): 你想要关联的model的类名。例如:
在我们的例子中,我们想指定‘Tag’model 类名。
2. joinTable:它是为没有遵循Cake命名规则的数据库而准备的。如果你的表不是【复数的model1】_【复数的model2】的词语顺序,你可以在这里指定你的表名。
3. foreignKey: 指向当前model的JOIN表的外键名。
在这里,你正在操作一个没有遵循Cake命名规则的数据库。
4. associationForeignKey:指向关联model的外键名。
5. conditions:定义关系的SQL条件语句
我们可以使用它告诉Cake仅关联一个通过验证的Tag。为了实现它,将键值设置为" Tag.approved = 1"或者其他类似的。
6. order:关联model的顺序。
例如,如果你想让你的关联model以某顺序排列,为此键设置一个SQL排序谓语:" Tag.tag DESC "。
7. limit: 你想让Cake获取关联model的最大数
用来限制获取关联的Tag数。
8. uniq:如果设置为true,重复的关联对象会被访问者和查询方法忽略。
一般来说,如果关联不同,将他设置为true。"Awesomeness"的Tag仅可以分配到Post "Cake Model Associations"一次,并且在结果数组中仅出现一次。
9. finderQuery:指定一个完整的SQL语句获取关联。
对于复杂的依赖多个表的关联,这是一种优秀的方法。如果Cake的自动关联不工作,在这里,你可以定义它。
10.deleteQuery:一完整的SQL语句,用来删除HABTM model之间的关联。
如果你不愿意按Cake的方法完成删除操作或者你的创建是以某种方法自己定义的,你可以在这里提供你自己的查询来改变本方法完成操作任务。
现在,当执行Post model的find()或findAll()调用时,在这里我们也应该可以看到我们关联的Tag model:
$post = $this->Post->read(null, '2');
print_r($post);
//输出:
Array
(
[Post] => Array
(
[id] => 2
[user_id] => 25
[title] => Cake Model Associations
[body] => Time saving, easy, and powerful.
[created] => 2006-04-15 09:33:24
[modified] => 2006-04-15 09:33:24
[status] => 1
)
[Tag] => Array
(
[0] => Array
(
[id] => 247
[tag] => CakePHP
)
[1] => Array
(
[id] => 256
[tag] => Powerful Software
)
)
)
保存相关的Model数据
当你操作关联的model时,需要记住的一件重要事情是,保存model数据应该一直由相应的Cake model来完成。如果正在保存一个新的Post以及它的Comment(留言)的话,那么在保存的过程中,你需要使用Post和Comment model。
如果在系统中不存在一个关联的model(例如,你想同时保存一个新的Post和一个相关的Comment),你需要首先保存主要的,或者说是父model。为了知道它如何进行,让我们想象一下,在我们的PostsController中有一个动作(action),它保存新的Post和相关Comment。下面的动作实例假设你已经发布一个Post和Comment。
/app/controllers/posts_controller.php (部分)
function add()
{
if (!empty($this->data))
{
//We can save the Post data:
//it should be in $this->data['Post']
$this->Post->save($this->data);
//Now, we'll need to save the Comment data
//But first, we need to know the ID for the
//Post we just saved...
$post_id = $this->Post->getLastInsertId();
//Now we add this information to the save data
//and save the comment.
$this->data['Comment']['post_id'] = $post_id;
//Because our Post hasMany Comments, we can access
//the Comment model through the Post model:
$this->Post->Comment->save($this->data);
}
}
尽管如此,如果父Model在系统中已经存在(例如,加一个Comment到一个已存在的Post),在保存之前你需要知道父Model的ID。你可以将此ID作为URL参数,或者作为表单中的一个隐藏元素。
/app/controllers/posts_controller.php (部分)
//Here's how it would look if the URL param is used...
function addComment($post_id)
{
if (!empty($this->data))
{
//You might want to make the $post_id data more safe,
//but this will suffice for a working example..
$this->data['Comment']['post_id'] = $post_id;
//Because our Post hasMany Comments, we can access
//the Comment model through the Post model:
$this->Post->Comment->save($this->data);
}
}
如果ID作为一个表单的隐藏元素传递的话,你可能想命名这个字段(如果你正在使用HtmlHelper),以至在发出的数据里它会中止,在这里,它应该为:
如果Post的ID是在 $post['Post']['id']...
echo $html->hidden('Comment/post_id', array('value' => $post['Post']['id'])); ?>
按这种方式,父Post model的ID可以在$this->data['Comment']['post_id']中访问,并且都为$this->Post->Comment->save($this->data)所准备。
如果你正保存多个子model,这些相同的基本技术都会工作,仅需要将那些save()调用放在一个循环(记住使用Model::Create()清除model信息哟)中。
总之,如果你正保存关联的数据(belongsTo, hasOne, 及 hasMany relations关系),重要的一点是得到父model的ID,并将他保存到子model。
相关阅读 更多 +
排行榜 更多 +