一对一关联

A表单行row关联A表单行row

一对一关联hasOnebelongsTo

hasOnebelongsTo区别

假设当前模型为A,关联模型为B
hasOne 表示B属于A的子数据集,如User表,关联了Porfile个人资料表
belongsTo 表示A属于B的子数据集,如User表,关联了Group用户组表

关联定义hasOnebelongsTo

定义一对一关联,例如,一个用户都有一个个人资料,我们定义User模型如下: hasOne方法的参数包括:

hasOne(‘关联模型类名’, ‘外键’, ‘主键’);

除了关联模型外,其它参数都是可选。

  • 关联模型(必须):关联模型类名
  • 外键:默认的外键规则是当前模型名(不含命名空间,下同)+_id ,例如user_id
  • 主键:当前模型主键,默认会自动获取也可以指定传入
    如: 调用直接采用withJoin: 一对一关联定义的时候还支持额外的方法,包括: 如果使用了JOIN方式的关联查询方式,你可以在额外的查询条件中使用关联对象名(不含命名空间)作为表的别名。*

    关联查询

    定义好关联之后,就可以使用下面的方法获取关联数据: 默认情况下, 我们使用的是user_id作为外键关联,如果不是的话则需要在关联定义的时候指定,例如: 有一点需要注意的是,关联方法的命名规范是驼峰法,而关联属性则一般是小写+下划线的方式,系统在获取的时候会自动转换对应,读取user_profile关联属性则对应的关联方法应该是userProfile

    根据关联数据查询

    可以根据关联条件来查询当前模型对象数据,例如:

    预载入查询

    实际上就是模型和关联模型同时查询,with是用in代码,withJoinjoin代码
    可以使用预载入查询解决典型的N+1查询问题,使用: 上面的代码使用的是IN查询,只会产生2条SQL查询。
    如果要对关联模型进行约束,可以使用闭包的方式。 with方法可以传入数组,表示同时对多个关联模型(支持不同的关联类型)进行预载入查询。 如果需要使用JOIN方式的查询,直接使用withJoin方法,如下: withJoin方法默认使用的是INNER JOIN方式,如果需要使用其它的,可以改成 需要注意的是withJoin方式不支持嵌套关联,通常你可以直接传入多个需要关联的模型。
    如果需要约束关联字段,可以使用下面的简便方法。

    关联保存

    系统会自动把当前模型的主键传入Profile模型。
    和新增一样使用save方法进行更新关联数据。

    绑定属性到父模型bind

    个人理解:将关联查询注入到当前模型
    可以在定义关联的时候使用bind方法绑定属性到父模型,例如: 或者指定绑定属性别名 然后使用关联预载入查询的时候,可以使用 绑定关联模型的属性支持读取器。
    如果不是预载入查询,请使用模型的appendRelationAttr方法追加属性。
    也可以使用动态绑定关联属性,可以使用 同样支持指定属性别名

    关联自动写入together

    我们可以使用together方法更方便的进行关联自动写入操作。
    写入 如果绑定了子模型的属性到当前模型,可以指定子模型的属性 更新 删除

    跨表一对一关联hasOneThrough(A-B-C多次关联)

    应用场景

    远程一对一关联用于定义有跨表的一对一关系,例如:

  • 每个用户有一个档案

  • 每个档案有一个档案卡
  • 用户和档案卡之间并无关联

    关联定义

    就可以直接通过远程一对一关联获取每个用户的档案卡,User模型定义如下: 远程一对一关联,需要同时存在CardProfile模型。
    hasOneThrough方法的参数如下:

    hasOneThrough(‘关联模型’, ‘中间模型’, ‘外键’, ‘中间表关联键’,’当前模型主键’,’中间模型主键’);

  • 关联模型(必须):关联模型类名

  • 中间模型(必须):中间模型类名
  • 外键:默认的外键名规则是当前模型名+_id
  • 中间表关联键:默认的中间表关联键名的规则是中间模型名+_id
  • 当前模型主键:一般会自动获取也可以指定传入
  • 中间模型主键:一般会自动获取也可以指定传入

    关联查询

    我们可以通过下面的方式获取关联数据

    一对多关联

    A表单行数据,关联B表多行数据

    直接一对多关联hasMany

    关联定义

    一对多关联的情况也比较常见,使用hasMany方法定义,参数包括:

    hasMany(‘关联模型’,’外键’,’主键’);

除了关联模型外,其它参数都是可选。

  • 关联模型(必须):关联模型类名
  • 外键:关联模型外键,默认的外键名规则是当前模型名+_id
  • 主键:当前模型主键,一般会自动获取也可以指定传入

例如一篇文章可以有多个评论 同样,也可以定义外键的名称

关联查询

我们可以通过下面的方式获取关联数据

根据关联条件查询

可以根据关联条件来查询当前模型对象数据,例如: 如果需要更复杂的关联条件查询,可以使用

关联新增

定义相对的关联belongsTo

要在Comment模型定义相对应的关联,可使用belongsTo方法:

关联删除

在删除文章的同时删除下面的评论

跨表一对多关联(远程一对多)

应用场景

远程一对多关联用于定义有跨表的一对多关系,例如:

  • 每个城市有多个用户
  • 每个用户有多个话题
  • 城市和话题之间并无关联

    关联定义hasManyThrough

    就可以直接通过远程一对多关联获取每个城市的多个话题,City模型定义如下: 远程一对多关联,需要同时存在TopicUser模型,当前模型和中间模型的关联关系可以是一对一或者一对多。
    hasManyThrough方法的参数如下:

    hasManyThrough(‘关联模型’, ‘中间模型’, ‘外键’, ‘中间表关联键’,’当前模型主键’,’中间模型主键’);

  • 关联模型(必须):关联模型类名

  • 中间模型(必须):中间模型类名
  • 外键:默认的外键名规则是当前模型名+_id
  • 中间表关联键:默认的中间表关联键名的规则是中间模型名+_id
  • 当前模型主键:一般会自动获取也可以指定传入
  • 中间模型主键:一般会自动获取也可以指定传入

    关联查询

    我们可以通过下面的方式获取关联数据

    条件搜索的时候,需要带上模型名作为前缀

根据关联条件查询

如果需要根据关联条件来查询当前模型,可以使用 更复杂的查询条件可以使用

多对多关联

关联定义belongsToMany

例如,我们的用户和角色就是一种多对多的关系,我们在User模型定义如下: belongsToMany方法的参数如下:

belongsToMany(‘关联模型’,’中间表’,’外键’,’关联键’);

  • 关联模型(必须):关联模型类名
  • 中间表:默认规则是当前模型名+_+关联模型名 (可以指定模型名)
  • 外键:中间表的当前模型外键,默认的外键名规则是关联模型名+_id
  • 关联键:中间表的当前模型关联键名,默认规则是当前模型名+_id

中间表名无需添加表前缀,并支持定义中间表模型,例如: 中间表模型类必须继承think\model\Pivot,例如: 中间表模型的基类Pivot默认关闭了时间戳自动写入,上面的中间表模型则开启了时间戳字段自动写入。

关联查询

我们可以通过下面的方式获取关联数据

关联新增

只新增中间表数据(角色已经提前创建完成),可以使用 单独更新中间表数据,可以使用: attach方法的返回值是一个Pivot对象实例,如果是附加多个关联数据,则返回Pivot对象实例的数组。

定义相对的关联

我们可以在Role模型中定义一个相对的关联关系,例如:

多态关联

多态关联允许一个模型在单个关联定义方法中从属一个以上其它模型,例如用户可以评论书和文章,但评论表通常都是同一个数据表的设计。多态一对多关联关系,就是为了满足类似的使用场景而设计。
下面是关联表的数据表结构: 有两个需要注意的字段是comment表中的commentable_idcommentable_type我们称之为多态字段。其中,commentable_id用于存放书或者文章的id(主键) ,而commentable_type用于存放所属模型的类型。通常的设计是多态字段有一个公共的前缀(例如这里用的commentable),当然,也支持设置完全不同的字段名(例如使用data_idtype)。

多态关联定义morphMany

接着,让我们来查看创建这种关联所需的模型定义:
文章模型: morphMany方法的参数如下:

morphMany(‘关联模型’,’多态字段’,’多态类型’);

关联模型(必须):关联的模型类名
多态字段(可选):支持两种方式定义 如果是字符串表示多态字段的前缀,多态字段使用多态前缀_type多态前缀_id,如果是数组,表示使用[‘多态类型字段名’,’多态ID字段名’],默认为当前的关联方法名作为字段前缀。
多态类型(可选):当前模型对应的多态类型,默认为当前模型名,可以使用模型名(如Article)或者完整的命名空间模型名(如app\index\model\Article)。
书籍模型: 书籍模型的设置方法同文章模型一致,区别在于多态类型不同,但由于多态类型默认会取当前模型名,因此不需要单独设置。
下面是评论模型的关联定义: morphTo方法的参数如下:
morphTo(‘多态字段’,[‘多态类型别名’]);
多态字段(可选):支持两种方式定义 如果是字符串表示多态字段的前缀,多态字段使用 多态前缀_type和多态前缀_id,如果是数组,表示使用[‘多态类型字段名’,’多态ID字段名’],默认为当前的关联方法名作为字段前缀
多态类型别名(可选):数组方式定义
获取多态关联
一旦你的数据表及模型被定义,则可以通过模型来访问关联。例如,若要访问某篇文章的所有评论,则可以简单的使用comments动态属性: 你也可以从多态模型的多态关联中,通过访问调用morphTo的方法名称来获取拥有者,也就是此例子中Comment模型的commentable方法。所以,我们可以使用动态属性来访问这个方法: Comment模型的commentable关联会返回ArticleBook模型的对象实例,这取决于评论所属模型的类型。

自定义多态关联的类型字段

默认情况下,ThinkPHP 会使用模型名作为多态表的类型区分,例如,Comment属于Article或者Book , commentable_type的默认值可以分别是Article或者Book。我们可以通过定义多态的时候传入参数来对数据库进行解耦。

多态一对一关联morphOne

应用场景

多态一对一相比多态一对多关联的区别是动态的一对一关联,举个例子说有一个个人和团队表,而无论个人还是团队都有一个头像需要保存但都会对应同一个头像表 会员模型: 团队模型: morphOne方法的参数如下:

morphOne(‘关联模型’,’多态字段’,’多态类型’);

  • 关联模型(必须):关联的模型类名。
  • 多态字段(可选):支持两种方式定义 如果是字符串表示多态字段的前缀,多态字段使用 多态前缀_type和多态前缀_id,如果是数组,表示使用[‘多态类型字段名’,’多态ID字段名’],默认为当前的关联方法名作为字段前缀。
  • 多态类型(可选):当前模型对应的多态类型,默认为当前模型名,可以使用模型名(如Member)或者完整的命名空间模型名(如app\index\model\Member)。

下面是头像模型的关联定义: 理解了多态一对多关联后,多态一对一关联其实就很容易理解了,区别就是当前模型和动态关联的模型之间的关联属于一对一关系。

绑定属性到父模型

可以在定义关联的时候使用bind方法绑定属性到父模型,例如: 或者指定绑定属性别名 然后使用关联预载入查询的时候,可以使用 绑定关联模型的属性支持读取器。

其他操作

关联预载入withwithJoin

关联查询的预查询载入功能,主要解决了N+1次查询的问题,例如下面的查询如果有3个记录,会执行4次查询: 如果使用关联预查询功能,就可以变成2次查询(对于一对一关联来说,如果使用withJoin方式只有一次查询),有效提高性能。 支持预载入多个关联,例如:

with方法只能调用一次,请不要多次调用,如果需要对多个关联模型预载入使用数组即可。

也可以支持嵌套预载入,例如: 支持使用数组方式定义嵌套预载入,例如下面的预载入要同时获取用户的Profile关联模型的PhoneJobImg子关联模型数据: 如果要指定属性查询,可以使用:

记得指定属性的时候一定要包含关联键。

对于一对多关联来说,如果需要设置返回的关联数据数量,可以使用withLimit方法。 注意这里的类型约束必须使用think\model\Relation,因为withLimit方法是关联类才有的方法
关联预载入名称是关联方法名,支持传入方法名的小写和下划线定义方式,例如如果关联方法名是userProfile和userBook的话: 和下面的方法是等效的: 区别在于你获取关联数据的时候必须和传入的关联名称保持一致。 一对一关联预载入支持两种方式:JOIN方式(一次查询)和IN方式(两次查询,默认方式),如果要使用JOIN方式关联预载入,可以使用withJoin方法。

延迟预载入

有些情况下,需要根据查询出来的数据来决定是否需要使用关联预载入,当然关联查询本身就能解决这个问题,因为关联查询是惰性的,不过用预载入的理由也很明显,性能具有优势。
延迟预载入仅针对多个数据的查询,因为单个数据的查询用延迟预载入和关联惰性查询没有任何区别,所以不需要使用延迟预载入。
如果你的数据集查询返回的是数据集对象,可以使用调用数据集对象的load实现延迟预载入:

关联预载入缓存

关联预载入可以支持查询缓存,例如: 表示对关联数据缓存30秒。
如果你有多个关联数据,也可以仅仅缓存部分关联 对于延迟预载入查询的话,可以在第二个参数传入缓存参数。

关联统计withCount

有些时候,并不需要获取关联数据,而只是希望获取关联数据的统计,这个时候可以使用withCount方法进行指定关联的统计。

你必须给User模型定义一个名称是cards的关联方法。

关联统计功能会在模型的对象属性中自动添加一个以“关联方法名+_count”为名称的动态属性来保存相关的关联统计数据。
可以通过数组的方式同时查询多个统计字段。 支持给关联统计指定统计属性名,例如:

关联统计暂不支持多态关联

如果需要对关联统计进行条件过滤,可以使用闭包方式。 使用闭包的方式,如果需要自定义统计字段名称,可以使用 和withCount类似的方法,还包括: 除了withCount之外的统计方法需要在第二个字段传入统计字段名,用法如下: 同样,也可以指定统计字段名 所有的关联统计方法可以多次调用,每次查询不同的关联统计数据。

关联输出

关联数据的输出也可以使用hiddenvisibleappend方法进行控制,下面举例说明。

隐藏关联属性

如果要隐藏关联模型的属性,可以使用 输出的结果中就不会包含Profile模型的email属性,如果需要隐藏多个属性可以使用

显示关联属性

同样,可以使用visible方法来显示关联属性:

追加关联属性

追加一个Profile模型的额外属性(非实际数据,可能是定义了获取器方法) 也可以追加一个额外关联对象的属性

最佳应用建议

查询推荐

定义Admin模型并关联AdminGroup模型 定义AdminGroup模型 调用查询