CakePHP自学笔记(六):模型的构建和关联

by AquarHEAD

AquarTutorial 第一期
-CakePHP自学笔记

(一):利用Ubuntu搭建开发环境
(二):数据库配置与连接
(三):MVC开发模式简介
(四):CakePHP的命名规范
(五):Cake中的MVC开发
>>(六):模型的构建和关联

<-<-<-<-<-<-<-<-<-<-<-<-<-

“重构”的代价

直到想起来有这玩意才发现自己有多笨。。。所以郁闷了好几天没看,alpha版本里面也没有用到模型的关联,今天实在无聊的要命,就读了一下,然后改了DarkBook的代码,重新组织了数据的结构,麻烦不说,我还是搞不明白为什么都声明了'foreignKey' => 'owner'了,Cake还是返回没有User模型没有id的错误。God Damn It!!看来Cake习惯了每个模型都有个id当主键。>_<||。。。

所以刚才从数据库改起,模型、控制器、视图全有改动,幸亏DarkBook只有两个控制器,要不我肯定疯掉了——这就是不读完教程就乱写的后果。。。

四种关联

CakePHP提供了四种类型的关联:hasOne、hasMany、belongsTo和hasAndBelongsToMany。四者分别对应模型中一个同名的变量。

声明了模型之间的关联之后,我们(在一般情况下)就不需要在控制器中引入$uses变量来接收其他模型的数据了。比如在模型中我们可以用 $this->SomeOtherModel->someFunction() 来控制其他模型,或者在控制器中用 $this->SomeModel->SomeOtherModel->someFunction() 来控制其他模型。

hasOne

hasOne即“有一个”,比如一个User应该有一个Profile。那么我们就简单地这样声明:

<?php
class User extends AppModel {
    var $name = 'User';
    var $hasOne = 'Profile';
}
?>

注意,Cake需要外键(foreign key)来实现关联(跟我的方法差不多么。。。),hasOne需要在对应的模型(本例中的Profile)中包含外键,Cake默认的外键名称是原模型小写外加“_id”,本例中就是user_id。当然了,我们可以更改这个外键的值 ,hasOne还有其他的一些参数可以修改,列举如下:

  • className 即关联到当前模型的模型类名。本例中就是Profile。
  • foreignKey 在被关联模型中外键的名称,默认值就是当前模型类名小写后加“_id”。
  • conditions 一条SQL语句用以过滤返回的关联模型。最好在语句中加上类名,比如"Profile.approved = 1"总是比"approved = 1"好的多。
  • fields 获取关联模型数据时返回的字段列表。默认情况下返回全部。
  • order 一条SQL语句用以定义返回的关联数据的排序。
  • dependent 当这个值设置为true的时候,一旦对当前模型的某个记录调用了delete()函数并且参数cascade也设置成true,那么关联的模型记录也会被删除。本例中就是删除一个User也会删除关联给他的Profile。

可以用如下形式来设置这些参数的值(其他关联都类似):

<?php
class User extends AppModel {
    var $name = 'User';          
    var $hasOne = array(
        'Profile' => array(
            'className'    => 'Profile',
            'conditions'   => array('Profile.published' => '1'),
            'dependent'    => true
        )
    );    
}
?>

在控制器中用类似 $this->set('data', $this->User->find('all')) 之类的函数传递数据给视图后,我们在视图中就可以十分方便的调用各个模型了。
返回的结果类似这样:

//Sample results from a $this->User->find() call.
 
Array
(
    [User] => Array
        (
            [id] => 121
            [name] => Gwoo the Kungwoo
            [created] => 2007-05-01 10:31:01
        )
    [Profile] => Array
        (
            [id] => 12
            [user_id] => 121
            [skill] => Baking Cakes
            [created] => 2007-05-01 10:31:01
        )
)

那么我们在视图中要用User的name字段的值,就可以 $data['User']['name'],要引用Profile的skill字段的值,就可以 $data['Profile']['skill']。

belongsTo

用hasOne我们已经可以从User的模型和控制器中获取Profile的数据了,相反地,我们可以设置belongsTo从Profile中获取所属User的数据。如下声明:

<?php
class Profile extends AppModel {
    var $name = 'Profile';
    var $belongsTo = 'User';
}
?>

与hasOne不同的是,belongsTo要求在当前模型中有外键。这个外键默认是belongsTo关联的模型的类名小写再加“_id”的后缀。这个关联与hasOne有很多相同的参数,包括className、foreignKey、conditions、fields和order,此外还有一下两个参数:

  • counterCache 把这个参数设为true会导致关联模型的一个计数字段自动更改。这个计数字段的默认名是当前模型的类名小写加上“_count”后缀,在调用save()或者delete()时这个计数值就会变化。如果在这里添一个字符串,那么就把它作为计数字段的名称。
  • counterScope 可选的条件用于确认计数字段的更新。

同hasOne类似,控制器传递给视图的数组类似下面:

//Sample results from a $this->Profile->find() call.
 
Array
(
   [Profile] => Array
        (
            [id] => 12
            [user_id] => 121
            [skill] => Baking Cakes
            [created] => 2007-05-01 10:31:01
        )    
    [User] => Array
        (
            [id] => 121
            [name] => Gwoo the Kungwoo
            [created] => 2007-05-01 10:31:01
        )
)

hasMany

下面来定义一个User有很多Comment的关联,用hasMany就可以做到在获取User数据的同时得到这个用户所写的评论。同hasOne类似的是外键要放在相关联的模型中。声明hasMany的方法不再赘述。

hasMany在hasOne的基础上还多了如下这些参数:

  • limit 用这个设置返回的记录的最大数量。
  • offset 获取关联数据前(在给定的conditions和order下)跳过的记录数量。
  • exclusive 当这个值设置为true的时候,模型的递归删除会调用deleteAll()函数而不是一个一个删除。这样能极大提升效率,但并不一定适合所有情况。
  • finderQuery 一条完整的SQL语句,CakePHP用来获取关联的数据。只在需要自己定义完全不同的功能时才需要。

控制器传递的数组像这样:

//Sample results from a $this->User->find() call.
 
Array
(  
    [User] => Array
        (
            [id] => 121
            [name] => Gwoo the Kungwoo
            [created] => 2007-05-01 10:31:01
        )
    [Comment] => Array
        (
            [0] => Array
                (
                    [id] => 123
                    [user_id] => 121
                    [title] => On Gwoo the Kungwoo
                    [body] => The Kungwooness is not so Gwooish
                    [created] => 2006-05-01 10:31:01
                )
            [1] => Array
                (
                    [id] => 124
                    [user_id] => 121
                    [title] => More on Gwoo
                    [body] => But what of the ‘Nut?
                    [created] => 2006-05-01 10:41:01
                )
        )
)

所以引用Comment时要注意用 $data['Comment'][1]等等,也可以 foreach $data['Comment'] as $single_comment 之类的。

hasAndBelongsToMany

最后这个有点不同,比如一个Post就“有并且属于很多”Tag,而HABTM和hasMany(用上面那个做例子)的区别就在于每个Comment被一个User拥有了之后,就不能再被别的User拥有了,而一个Tag属于一个Post之后还可以给其他的Post添加。

HABTM也不仅仅需要在当前模型或关联模型中的外键,它需要一个单独的包括两个模型名字的数据表,这个例子中相应的表就应该是posts_tags,一般需要3个字段——id、post_id、tags_id(哎,在Cake里写程序就记得每个表都来个id当主键,再设置成自动增长省的闹心。。。),HABTM有很多参数:

  • className 关联的模型类名。
  • joinTable 可以用这个参数设置关联表的名字。
  • with 这里添的是关联表对应的模型名称。默认情况下CakePHP会自动创建一个模型。本例中默认的就叫做PostsTag。这个关联表也可以像普通的模型一样使用。
  • foreignKey 当前模型中的外键值。在定义多个HABTM关联时会用到。默认值是当前模型类名小写加“_id”后缀。
  • associationForeignKey 关联模型中的外键值。在定义多个HABTM关联时会用到。默认值是关联模型的类名小写加“_id”后缀。
  • unique 如果设置为true(默认值),Cake会在添加新关联时删除已有的关联。(暂时不能理解透彻)
  • conditions fields order limit offset 和hasMany一样。
  • finderQuery deleteQuery insertQuery 分别可以添一条完整的SQL语句在获取、删除和添加关联的模型记录时使用。同样只在需要自定义的时候使用。

还是看看怎么声明HABTM关联:

<?php
 
class Post extends AppModel {
    var $name = 'Post';   
    var $hasAndBelongsToMany = array(
        'Tag' =>
            array(
                'className'              => 'Tag',
                'joinTable'              => 'recipes_tags',
                'foreignKey'             => 'recipe_id',
                'associationForeignKey'  => 'tag_id',
                'unique'                 => true,
                'conditions'             => '',
                'fields'                 => '',
                'order'                  => '',
                'limit'                  => '',
                'offset'                 => '',
                'finderQuery'            => '',
                'deleteQuery'            => '',
                'insertQuery'            => ''
            )
    );
}
?>

控制器传递给视图的数组同hasMany类似,不再赘述。