热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CakePHP2.xCookBook中文版第七章模型之关联:将模型连接在一起

使用joins允许你以极为灵活的方式处理CakePHP的关系并获取数据,但是在很多情况下,你能使用其它工具达到同样的目的,例如正确地定义关联,运行时绑定模型或者使用Containable行为。使用这种特性要很小心,因为它在某些情况下可能会带来模式不规范的SQL查询。

关联:将模型连接在一起

CakePHP 的一个非常强劲的特性就是由模型提供关系映射,通过关联来管理多个模型间的连接。

在应用程序的不同对象间定义关系是很自然的。例如:在食谱数据库,一个食谱可能有多个评论,每个评论有一个作者,每个作者可能有多个评论。 以定义这些关系的形式工作,将允许你以一种直观且强大的方式访问你的数据库。

本节的目的是展示如何在 CakePHP 中计划、定义以及利用模型间的关系。

虽然数据可能来自各种源,但在 web 应用程序中最常见的则是存储在关系数据库中。 本节将覆盖这方面的大部分内容。

关于与插件模型一起的关联的信息,请参见 插件模型

关系类型

CakePHP 的关系类型有四种: hasOne、hasMany、belongsTo 和 hasAndBelongsToMany (HABTM)。

关系 关联类型 例子
     
一对多 hasMany 一个用户有多份食谱
多对一 belongsTo 多份食谱属于同一个用户
多对多 hasAndBelongsToMany 多份食谱有且属于多种成分

关联是通过创建一个由你定义的关联命名的类变量来定义的。 此变量有时候可能是简单的字符串,但也可能是用于定义关联细节的复杂的多维数组。

 1 class User extends AppModel {  2 public $hasOne = 'Profile';  3 public $hasMany = array(  4 'Recipe' => array(  5 'className'  => 'Recipe',  6 'conditions' => array('Recipe.approved' => '1'),  7 'order'      => 'Recipe.created DESC'  8  )  9  ); 10 }

在上面的例子中,第一个实例的单词 ‘Recipe’ 是别名。它是关系的唯一标识,它可以是你选择的任何东西。通常你会选择与要引用的类相同的名字。然而,每个模型的别名在应用程序中必须唯一。合适的例子有:

 1 class User extends AppModel {  2 public $hasMany = array(  3 'MyRecipe' => array(  4 'className' => 'Recipe',  5  )  6  );  7 public $hasAndBelongsToMany => array(  8 'MemberOf' => array(  9 'className' => 'Group', 10  ) 11  ); 12 }
 1 class Group extends AppModel {  2 public $hasMany = array(  3 'MyRecipe' => array(  4 'className'  => 'Recipe',  5  )  6  );  7 public $hasAndBelongsToMany => array(  8 'Member' => array(  9 'className' => 'User', 10  ) 11  ); 12 }

但是在所有的情况下,以下代码都不工作:

 1 class User extends AppModel {  2 public $hasMany = array(  3 'MyRecipe' => array(  4 'className' => 'Recipe',  5  )  6  );  7 public $hasAndBelongsToMany => array(  8 'Member' => array(  9 'className' => 'Group', 10  ) 11  ); 12 }
 1 class Group extends AppModel {  2 public $hasMany = array(  3 'MyRecipe' => array(  4 'className'  => 'Recipe',  5  )  6  );  7 public $hasAndBelongsToMany => array(  8 'Member' => array(  9 'className' => 'User', 10  ) 11  ); 12 }

因为在 HABTM 关联中,别名 ‘Member’ 同时指向了 User 模型(在 Group 模型中)和 Group 模型(在 User 模型中)。 在不同的模型为某个模型起不唯一的别名,可能会带来未知的行为。

Cake 能自动在关联模型对象间建立连接。所以你可以在你的 User 模型中以如下方式访问 Recipe 模型:

1 $this->Recipe->someFunction();

同样的,你也能在控制器中循着模型关系访问关联模型:

1 $this->User->Recipe->someFunction();

注解

记住,关系定义是 ‘单向的’。如果你定义了 User hasMany Recipe,对 Recipe 模型是没有影响的。你需要定义 Recipe belongsTo User才能从 Recipe 模型访问 User 模型。

hasOne

让我们设置 User 模型以 hasOne 类型关联到 Profile 模型。

首先,数据库表需要有正确的主键。对于 hasOne 关系,一个表必须包含指向另一个表的记录的外键。在本例中,profiles 表将包含一个叫做 user_id 的列。基本模式是: :

hasOne: 另一个 模型包含外键。

关系 结构
Apple hasOne Banana bananas.apple_id
User hasOne Profile profiles.user_id
Doctor hasOne Mentor mentors.doctor_id

注解

关于这一点,并没有强制要求遵循 CakePHP 约定,你能够很容易地在关联定义中使用任何外键来覆盖它。虽然如此,遵守规则将使你的代码更简捷,更易于阅读和维护。

User 模型文件保存为 /app/Model/User.php。为了定义‘User hasOne Profile’ 关联,需要在模型类中添加 $hasOne属性。记得要在 /app/Model/Profile.php 文件中放一个 Profile 模型,否则关联将不工作:

1 class User extends AppModel { 2 public $hasOne = 'Profile'; 3 }

有两种途径在模型文件中描述此关系。简单的方法是设置一个包含要关联的模型的类名的字符串型属性 $hasOne,就像我们上面做的那样。

如果需要更全面的控制,可以使用数组语法定义关联。例如,你可能想要限制关联只包含某些记录。

1 class User extends AppModel { 2 public $hasOne = array( 3 'Profile' => array( 4 'className'    => 'Profile', 5 'conditions'   => array('Profile.published' => '1'), 6 'dependent'    => true 7  ) 8  ); 9 }

hasOne 关联数组可能包含的键有: :

  • className: 被关联到当前模型的模型类名。如果你定义了 ‘User hasOne Profile’关系,类名键将是 ‘Profile.’
  • foreignKey: 另一张表中的外键名。如果需要定义多个 hasOne 关系,这个键非常有用。其默认值为当前模型的单数模型名缀以 ‘_id’。在上面的例子中,就默认为 ‘user_id’。
  • conditions: 一个 find() 兼容条件的数组或者类似 array(‘Profile.approved’ => true) 的 SQL 字符串.
  • fields: 需要在匹配的关联模型数据中获取的列的列表。默认返回所有的列。
  • order: 一个 find() 兼容排序子句或者类似 array(‘Profile.last_name’ => ‘ASC’) 的 SQL 字符串。
  • dependent: 当 dependent 键被设置为 true,并且模型的 delete() 方法调用时的参数 cascade 被设置为 true,关联模型的记录同时被删除。在本例中,我们将其设置为 true 将导致删除一个 User 时同时删除与其相关的 Profile。

一旦定义了关系,User 模型上的 find 操作将匹配存在的关联 Profile 记录:

 1 // 调用 $this->User->find() 的示例结果。  2  3 Array  4 (  5 [User] => Array  6  (  7 [id] => 121  8 [name] => Gwoo the Kungwoo  9 [created] => 2007-05-01 10:31:01 10  ) 11 [Profile] => Array 12  ( 13 [id] => 12 14 [user_id] => 121 15 [skill] => Baking Cakes 16 [created] => 2007-05-01 10:31:01 17  ) 18 )

belongsTo

现在我们有了通过访问 User 模型获取相关 Profile 数据的办法,让我们在 Profile 模型中定义 belongsTo 关联以获取相关的 User 数据。belongsTo 关联是 hasOne 和 hasMany 关联的自然补充:它允许我们从其它途径查看数据。

在为 belongsTo 关系定义数据库表的键时,遵循如下约定:

belongsTo: 当前模型 包含外键。

关系 结构
Banana belongsTo Apple bananas.apple_id
Profile belongsTo User profiles.user_id
Mentor belongsTo Doctor mentors.doctor_id

小技巧

如果一个模型(表)包含一个外键,它 belongsTo 另一个模型(表)。

我们可以使用如下字符串语法,在 /app/Model/Profile.php 文件中的 Profile 模型中定义 belongsTo 关联:

1 class Profile extends AppModel { 2 public $belongsTo = 'User'; 3 }

我们还能使用数组语法定义特定的关系:

1 class Profile extends AppModel { 2 public $belongsTo = array( 3 'User' => array( 4 'className'    => 'User', 5 'foreignKey'   => 'user_id' 6  ) 7  ); 8 }

belongsTo 关联数组可能包含的键有:

  • className: 被关联到当前模型的模型类名。如果你定义了 ‘Profile belongsTo User’关系,类名键的值将为 ‘User.’

  • foreignKey: 当前模型中需要的外键。用于需要定义多个 belongsTo 关系。其默认值为另一模型的单数模型名缀以 ‘_id’。

  • conditions: 一个 find() 兼容条件的数组或者类似 array('User.active' => true) 的 SQL 字符串。

  • type: SQL 查询的 join 类型,默认为 Left,这不可能在所有情况下都符合你的需求,在你想要从主模型和关联模型获取全部内容或者什么都不要时很有用!(仅在某些条件下有效)。 (注:类型值必须是小写,例如:left, inner)

  • fields: 需要在匹配的关联模型数据中获取的列的列表。默认返回所有的列。

  • order: 一个 find() 兼容排序子句或者类似 array('User.username' => 'ASC') 的 SQL 字符串。

  • counterCache: 如果此键的值设置为 true,当你在做 “save()” 或者 “delete()” 操作时关联模型将自动递增或递减外键关联的表的 “[singular_model_name]_count” 列的值。如果它是一个字符串,则其将是计数用的列名。计数列的值表示关联行的数量。也可以通过使用数组指定多个计数缓存,键为列名,值为条件,例如:

    1 array( 2 'recipes_count' => true, 3 'recipes_published' => array('Recipe.published' => 1) 4 )

     

  • counterScope: 用于更新计数缓存列的可选条件数组。

一旦定义了关联,Profile 模型上的 find 操作将同时获取相关的 User 记录(如果它存在的话):

 1 //调用 $this->Profile->find() 的示例结果。  2  3 Array  4 (  5 [Profile] => Array  6  (  7 [id] => 12  8 [user_id] => 121  9 [skill] => Baking Cakes 10 [created] => 2007-05-01 10:31:01 11  ) 12 [User] => Array 13  ( 14 [id] => 121 15 [name] => Gwoo the Kungwoo 16 [created] => 2007-05-01 10:31:01 17  ) 18 )

hasMany

下一步:定义一个 “User hasMany Comment” 关联。一个 hasMany 关联将允许我们在获取 User 记录的同时获取用户的评论。

在为 hasMany 关系定义数据库表的键时,遵循如下约定:

hasMany: 其它 模型包含外键。

关系 结构
User hasMany Comment Comment.user_id
Cake hasMany Virtue Virtue.cake_id
Product hasMany Option Option.product_id

我们可以使用如下字符串语法,在 /app/Model/User.php 文件中的 User 模型中定义 hasMnay 关联:

1 class User extends AppModel { 2 public $hasMany = 'Comment'; 3 }

我们还能使用数组语法定义特定的关系:

 1 class User extends AppModel {  2 public $hasMany = array(  3 'Comment' => array(  4 'className'     => 'Comment',  5 'foreignKey'    => 'user_id',  6 'conditions'    => array('Comment.status' => '1'),  7 'order'         => 'Comment.created DESC',  8 'limit'         => '5',  9 'dependent'     => true 10  ) 11  ); 12 }

hasMany 关联数组可能包含的键有:

  • className: 被关联到当前模型的模型类名。如果你定义了 ‘User hasMany Comment’关系,类名键的值将为 ‘Comment.’。
  • foreignKey: 另一张表中的外键名。如果需要定义多个 hasMany 关系,这个键非常有用。其默认值为当前模型的单数模型名缀以 ‘_id’。
  • conditions: 一个 find() 兼容条件的数组或者类似 array(‘Comment.visible’ => true) 的 SQL 字符串。
  • order: 一个 find() 兼容排序子句或者类似 array(‘Profile.last_name’ => ‘ASC’) 的 SQL 字符串。
  • limit: 想返回的关联行的最大行数。
  • offset: 获取和关联前要跳过的行数(根据提供的条件 - 多数用于分页时的当前页的偏移量)。
  • dependent: 如果 dependent 设置为 true,就有可能进行模型的递归删除。在本例中,当 User 记录被删除后,关联的 Comment 记录将被删除。
  • exclusive: 当 exclusive 设置为 true,将用 deleteAll() 代替分别删除每个实体来来完成递归模型删除。这大大提高了性能,但可能不是所有情况下的理想选择。
  • finderQuery: CakePHP 中用于获取关联模型的记录的完整 SQL 查询。用在包含许多自定义结果的场合。 如果你建立的一个查询包含关联模型 ID 的引用,在查询中使用 $__cakeID__$} 标记它。例如,如果你的 Apple 模型 hasMany Orange,此查询看上去有点像这样: SELECT Orange.* from oranges as Orange WHEREOrange.apple_id = {$__cakeID__$};

一旦关联被建立,User 模型上的 find 操作也将获取相关的 Comment 数据(如果它存在的话):

 1 //调用 $this->User->find() 获得的结果示例。  2  3 Array  4 (  5 [User] => Array  6  (  7 [id] => 121  8 [name] => Gwoo the Kungwoo  9 [created] => 2007-05-01 10:31:01 10  ) 11 [Comment] => Array 12  ( 13 [0] => Array 14  ( 15 [id] => 123 16 [user_id] => 121 17 [title] => On Gwoo the Kungwoo 18 [body] => The Kungwooness is not so Gwooish 19 [created] => 2006-05-01 10:31:01 20  ) 21 [1] => Array 22  ( 23 [id] => 124 24 [user_id] => 121 25 [title] => More on Gwoo 26 [body] => But what of the ‘Nut? 27 [created] => 2006-05-01 10:41:01 28  ) 29  ) 30 )

有件事需要记住:你还需要定义 Comment belongsTo User 关联,用于从两个方向获取数据。 我们在这一节概述了能够使你从 User 模型获取 Comment 数据的方法。在 Comment 模型中添加 Comment belongsTo User 关系将使你能够从 Comment 模型中获取 User 数据 - 这样的链接关系才是完整的且允许从两个模型的角度获取信息流。

counterCache - 缓存你的 count()

这个功能帮助你缓存相关数据的计数。模型通过自己追踪指向关联 $hasMany 模型的所有的添加/删除并递增/递减父模型表的专用整数列,替代手工调用 find('count') 计算记录的计数。

这个列的名称由列的单数名后缀以下划线和单词 “count” 构成:

my_model_count 

如果你有一个叫 ImageComment 的模型和一个叫 Image 的模型,你需要添加一个指向 images 表的新的整数列并命名为image_comment_count

下面是更多的示例:

模型 关联模型 示例
User Image users.image_count
Image ImageComment images.image_comment_count
BlogEntry BlogEntryComment blog_entries.blog_entry_comment_count

一旦你添加了计数列,就可以使用它了。通过在你的关联中添加 counterCache 键并将其值设置为 true,可以激活 counter-cache:

1 class ImageComment extends AppModel { 2 public $belongsTo = array( 3 'Image' => array( 4 'counterCache' => true, 5  ) 6  ); 7 }

自此,你每次添加或删除一个关联到 Image 的 ImageCommentimage_comment_count 字段的数字都会自动调整。

你还可以指定 counterScope。它允许你指定一个简单的条件,通知模型什么时候更新(不更新)计数值,这依赖于你如何查看。

在我们的 Image 模型示例中,我们可以象下面这样指定:

1 class ImageComment extends AppModel { 2 
            var cpro_id = "u6885494";

        
        
    
推荐阅读
  • PHP 编程疑难解析与知识点汇总
    本文详细解答了 PHP 编程中的常见问题,并提供了丰富的代码示例和解决方案,帮助开发者更好地理解和应用 PHP 知识。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 本文详细介绍了如何通过多种编程语言(如PHP、JSP)实现网站与MySQL数据库的连接,包括创建数据库、表的基本操作,以及数据的读取和写入方法。 ... [详细]
  • OPPO黄页服务即将停止
    OPPO黄页服务因业务调整即将停止,用户需了解具体卸载路径及受影响的机型。 ... [详细]
  • SQL中UPDATE SET FROM语句的使用方法及应用场景
    本文详细介绍了SQL中UPDATE SET FROM语句的使用方法,通过具体示例展示了如何利用该语句高效地更新多表关联数据。适合数据库管理员和开发人员参考。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 本文详细介绍了HTML中标签的使用方法和作用。通过具体示例,解释了如何利用标签为网页中的缩写和简称提供完整解释,并探讨了其在提高可读性和搜索引擎优化方面的优势。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 使用C#开发SQL Server存储过程的指南
    本文介绍如何利用C#在SQL Server中创建存储过程,涵盖背景、步骤和应用场景,旨在帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 本文探讨了适用于Spring Boot应用程序的Web版SQL管理工具,这些工具不仅支持H2数据库,还能够处理MySQL和Oracle等主流数据库的表结构修改。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 在使用 DataGridView 时,如果在当前单元格中输入内容但光标未移开,点击保存按钮后,输入的内容可能无法保存。只有当光标离开单元格后,才能成功保存数据。本文将探讨如何通过调用 DataGridView 的内置方法解决此问题。 ... [详细]
author-avatar
相思和怀恋_811_372
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有