Yii Framework 开发教程(27) 数据库-关联Active Record示例

来源:互联网 发布:手机常用的网络制式是 编辑:程序博客网 时间:2024/05/21 09:32


我们已经了解了怎样使用 Active Record (AR) 从单个数据表中获取数据。 在本节中,我们讲解怎样使用 AR 连接多个相关数据表并取回关联(join)后的数据集。

为了使用关系型 AR,我们建议在需要关联的表中定义主键-外键约束。这些约束可以帮助保证相关数据的一致性和完整性。

本例通过修改Yii Framework 开发教程(25) 数据库-Query Builder示例来介绍多个有关系的表如何使用Active Record。

在我们使用 AR 执行关联查询之前,我们需要让 AR 知道一个 AR 类是怎样关联到另一个的。

两个 AR 类之间的关系直接通过 AR 类所代表的数据表之间的关系相关联。 从数据库的角度来说,表 A 和 B 之间有三种关系:一对多(one-to-many,例如 tbl_user 和 tbl_post),一对一( one-to-one 例如 tbl_user 和tbl_profile)和 多对多(many-to-many 例如 tbl_category 和 tbl_post)。 在 AR 中,有四种关系:

  • BELONGS_TO(属于): 如果表 A 和 B 之间的关系是一对多,则 表 B 属于 表 A (例如 Post 属于 User);
  • HAS_MANY(有多个): 如果表 A 和 B 之间的关系是一对多,则 A 有多个 B (例如 User 有多个 Post);
  • HAS_ONE(有一个): 这是 HAS_MANY 的一个特例,A 最多有一个 B (例如 User 最多有一个 Profile);
  • MANY_MANY: 这个对应于数据库中的 多对多 关系。 由于多数 DBMS 不直接支持 多对多 关系,因此需要有一个关联表将 多对多 关系分割为 一对多 关系。 在我们的示例数据结构中,tbl_post_category 就是用于此目的的。在 AR 术语中,我们可以解释 MANY_MANY 为 BELONGS_TO 和 HAS_MANY 的组合。 例如,Post 属于多个(belongs to many) Category ,Category 有多个(has many) Post.

AR 中定义关系需要覆盖 CActiveRecord 中的 relations() 方法。此方法返回一个关系配置数组。每个数组元素通过如下格式表示一个单一的关系。

在Query Builder中我们使用了下面SQL查询语句

[sql] view plaincopyprint?
  1. SELECT c.FirstName, c.LastName , c.Address,c.Email  
  2. FROM customer c  
  3. INNER JOIN  
  4. employee e  
  5. ON c.SupportRepId=e.EmployeeId  
  6. WHERE e.EmployeeId=4  

涉及到两个表格Employee 和 Customer,Employee和Customer之间是一对多的关系,也就是说一个员工可以负责多个客户。Employee到Customer的关系为HAS_MANY, Customer到Employee的关系为HAS_ONE。因此可以定义Employee和Customer如下:

[php] view plaincopyprint?
  1. //Customer.php  
  2. class Customer extends CActiveRecord  
  3. {  
  4.     public static function model($className=__CLASS__)  
  5.     {  
  6.         return parent::model($className);  
  7.     }  
  8.   
  9.     public function tableName()  
  10.     {  
  11.         return 'Customer';  
  12.     }  
  13.   
  14. }  
  15.   
  16. //Employee.php  
  17. class Employee extends CActiveRecord  
  18. {  
  19.     public static function model($className=__CLASS__)  
  20.     {  
  21.         return parent::model($className);  
  22.     }  
  23.   
  24.     public function tableName()  
  25.     {  
  26.         return 'Employee';  
  27.     }  
  28.   
  29.     public function relations()  
  30.     {  
  31.         return array(  
  32.             'customers'=>array(self::HAS_MANY, 'Customer''SupportRepId'),  
  33.   
  34.             );  
  35.     }  
  36. }  

因为本例只使用到由Employee查询对应的Customer,因此只为类定义了relations方法。对应的表和外键为Customer和SupportRepId。
然后修改SiteController的indexAction方法:

[php] view plaincopyprint?
  1. public function actionIndex()  
  2. {  
  3.   
  4.     $employee=Employee::model()->findByPk(4);  
  5.   
  6.     $this->render('index'array(  
  7.         'model' => $employee->customers,  
  8.   
  9.         ));  
  10. }  

AR 类中的关系定义为每个关系向类中隐式添加了一个属性。在一个关联查询执行后,相应的属性将将被以关联的 AR 实例填充。因此由$employee->customers可以查询到Employee对应的Customers记录。
执行关联查询最简单的方法是读取一个 AR 实例中的关联属性。如果此属性以前没有被访问过,则一个关联查询将被初始化,它将两个表关联并使用当前 AR 实例的主键过滤。 查询结果将以所关联 AR 类的实例的方式保存到属性中。这就是传说中的 懒惰式加载(lazy loading,也可译为延迟加载) 方式,例如,关联查询只在关联的对象首次被访问时执行。
本例使用的为延迟加载,延迟加载在某些情况下并不高效。如果我们想获取 N 个帖子的作者,使用这种延迟加载将会导致执行 N 个关联查询。 这种情况下,我们应该改为使用 渴求式加载(eager loading)方式。
渴求式加载方式会在获取主 AR 实例的同时获取关联的 AR 实例。 这是通过在使用 AR 中的 find 或 findAll 方法时配合使用 with 方法完成的。例如:

[php] view plaincopyprint?
  1. $employee=Post::model()->with('customers')->findAll();  

最后修改一下显示结果的View的代码:

[php] view plaincopyprint?
  1. <?php foreach($model as $customer)  
  2. {  
  3.   
  4.     echo 'First Name:' . $customer->FirstName . '<br />';  
  5.     echo 'Last Name:' . $customer->LastName . '<br />';  
  6.     echo 'Address:' . $customer->Address . '<br />';  
  7.     echo 'Email:' . $customer->Email . '<br />';  
  8.     echo '---------------------- <br />';  
  9. }  
  10.   
  11.  ?>  

不同的数据对列名大小写处理方式不同,有的数据库区分大小写,保险起见,Customer的属性使用和列定义同样的大小写。

201212127005

本例下载

本例介绍了关联Active Record的最基本的用法,其它功能和属性可以参见Yii中文文档,此外如果借助类似CodeSmith这样的工具,如果能够自动生成数据库定义的ActiveRecord代码,就可以大大减轻程序员的代码手工编写工作量。

此外,使用Active Record的便利是以性能为代价的,通常情况下使用Active Record与使用DAO读写数据库性能相比要差一个级别。下表为一个参考值,查找200个演员和1000部电影。

方法内存使用(M)执行时间(秒)Active Record19.741.14109Query Builder17.980.35732DAO17.740.35038
0 0
原创粉丝点击