ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
**模型关联的先后顺序**:先在模型中定义两个表之间的关系,然后在查询的时候使用不同的操作方法进行查询; ======= **模型关联 的7种关系定义**:先想好谁是主,谁是副,副表一般都是补充内容的表,是从属关系 ===== **hasOne**:(**一对一拥有**:博客有内容,博客表是主,内容表是副,是补充内容)博客blog和内容content是一对一的,属于hasOne关联(以博客模型为参照),一般content表会有一个blog_id字段对应blog表的id; **用法**:hasOne('关联模型','外键','主键'); 除了关联模型外,其它参数都是可选。 * **关联模型** (必须):模型名或者模型类名 * **外键** :默认的外键规则是当前模型名(不含命名空间,下同)+`_id` ,例如 `user_id` * **主键** :当前模型主键,一般会自动获取也可以指定传入 hasOne示例: 在Blog模型类中添加定义函数: ``` public function content() { return $this->hasOne('Content'); 完整写法:return $this->hasOne('Content','blog_id','id'); } ``` 解释: blog拥有content,博客拥有内容,这两个表的连接关系是content表有一个外键叫:blog_id ,blog表有一个主键叫:id,这两个id的值是相等的 blog_id = id.这是默认写法,如果你的表格字段命名不是默认写法,就直接填写数据库表中真实的字段名; ===== **belongsTo**:(**一对一属于**:内容属于博客,博客表还是主,虽然它们是一对一的关系,但也不能违反常理说博客属于内容)内容和博客之间就属于belongsTo关联(以内容模型为参照),一般content表会有一个blog_id字段对应blog表的id; 另外博客一定属于某个分类(这里设计为单个分类),也是belongsTo关联(以博客模型为参照),一般blog表会有一个cate_id字段对应cate表的id; **用法**:belongsTo('关联模型','外键','关联表主键'); 除了关联模型外,其它参数都是可选。 * **关联模型** (必须):模型名或者模型类名 * **外键** :当前模型外键,默认的外键名规则是关联模型名 +`_id` * **关联主键** :关联模型主键,一般会自动获取也可以指定传入 belongsTo示例1:在Content模型类中添加定义函数: ``` public function blog() { return $this->belongsTo('Blog'); 完整写法:return $this->belongsTo('Blog','blog_id','id'); } ``` 解释: 内容属于博客,这两个表的连接关系是content表有一个外键叫:blog_id ,blog表有一个主键叫:id,这两个id的值是相等的blog_id= id.这是默认写法,如果你的表格字段命名不是默认写法,就直接填写数据库表中真实的字段名; 我们可以看到和hasOne是相对的关系,具体就看是站在哪个表的角度来看; belongsTo示例2:在Blog模型类中添加定义函数: ``` public function user() { return $this->belongsTo('User'); 完整写法:return $this->belongsTo('User','user_id','id'); } ``` 解释:博客属于用户,这两个表的连接关系是blog表有一个外键叫:user_id ,user表有一个主键叫:id,这两个id的值是相等的user_id = id.这是默认写法,如果你的表格字段命名不是默认写法,就直接填写数据库表中真实的字段名; ===== **hasMany**:(**一对多拥有**:一个分类有很多篇博客)每个分类下面有多篇博客,因此属于hasMany关联(以分类模型为参照),blog表会有一个cate_id字段对应cate表的id; (一个用户有很多篇博客)每个用户会发布多篇博客,所以用户和博客之间也属于hasMany关联(以用户模型为参照),一般blog表会有一个user_id字段对应user表的id; **用法**:hasMany('关联模型','外键','主键'); 除了关联模型外,其它参数都是可选。 * **关联模型** (必须):模型名或者模型类名 * **外键** :关联模型外键,默认的外键名规则是当前模型表名 +`_id` * **主键** :当前模型主键,一般会自动获取也可以指定传入 hasMany示例:在User模型中添加定义函数: ``` public function blogs() { return $this->hasMany('Blog'); 完整写法:return $this->hasMany('Blog','user_id','id'); } ``` 解释:用户写了很多博客,这两个表的连接关系是blog表有一个外键叫:user_id ,user表有一个主键叫:id,这两个id的值是相等的user_id = id.这是默认写法,如果你的表格字段命名不是默认写法,就直接填写数据库表中真实的字段名;对于结果是多行数据的,我们习惯命名为复数形式,所以叫blogs. hasMany和belongsTo是相对关系; 如果你的模型名和表名不一致,在模型中指定了表名变量 `protected $name` ,那么自动获取外键将是$name+_id,这样很容易查询报错字段不存在.如果你的表名和模型名不一致,请选择指定传递外键名 ===== **belongsToMany**:(**相互多对多属于**:一个用户作为多个角色类型,一个角色类型拥有多个用户)每个用户可以有多个角色,反过来每个角色也会有多个用户, 因此用户和角色属于belongsToMany关联(多对多关联无论以哪个模型为参照关联不变),用户和角色之间有一个中间表就是用户角色表,这个中间表通常会设计有user_id和role_id字段; user表id->(user_id<-用户角色表user_role->role_id) ->role表id ,通过中间表,3个表就连接起来了; **用法**:belongsToMany('关联模型','中间表','外键','关联键'); * **关联模型** (必须):模型名或者模型类名 * **中间表** :默认规则是当前模型名 +`_`+ 关联模型名 * **关联模型关联键** :默认规则是关联模型名 +`_id` * **当前模型关联键** :默认规则是当前模型名 +`_id` belongsToMany示例:在User模型中添加定义函数: ``` public function roles() { return $this->belongsToMany('Role', 'auth'); 完整写法:return $this->belongsToMany('Role', 'auth','role_id','user_id'); } ``` 在Role模型中添加定义函数: ``` public function users() { return $this->belongsToMany('User', 'auth'); 完整写法:return $this->belongsToMany('User', 'auth','user_id','role_id'); } ``` 解释:一个用户可以同时拥有多种角色,一种角色也可以同时赋予多个用户,user表id->(user_id<-用户角色表user_role->role_id) ->role表id ;belongsToMany相对的就是belongsToMany. 在写法上,后面两个关联键的顺序根据从哪个表的角度出发来决定先后顺序.示例的中间表命名没有按默认规则命名,如果按常理命名应该是user_role,那么在users函数中就可以只填一个'User'参数即可, 在roles函数中则需要把'auth'改成'user_role'; 反正两个相对的belongsToMany中最多只能有一个可以省略写中间表参数. ===== **hasManyThrough**:(**远程一对多串联**)每个城市有多个用户,而每个用户有多个博客,城市和博客之间并无直接关系,而是通过中间的用户产生关联,城市和博客之间就属于hasManyThrough关联(以城市模型为参照),这种也是3个表连接起来,但是只能从左往右查,从右往左查就都是一对一属于的关系; **用法**:hasManyThrough('关联模型','中间模型','外键','中间表关联键','主键'); * **关联模型** (必须):模型名或者模型类名 * **中间模型** (必须):模型名或者模型类名 * **外键** :默认的外键名规则是当前模型名 +`_id` * **中间表关联键** :默认的中间表关联键名的规则是中间模型名 +`_id` * **主键** :当前模型主键,一般会自动获取也可以指定传入 hasManyThrough示例:在City模型中添加定函数: ``` public function blogs() { return $this->hasManyThrough('Blog', 'User'); 完整的写法:return $this->hasManyThrough('Blog', 'User','blog_id','user_id','id'); } ``` 解释:要查属于这个城市的 用户写的 博客; 这是一个多表联查的查询.hasManyThrough没有相对关系,hasManyThrough的反向不存在,如果是要查这篇博客属于哪个城市,那么就是两个belongsTo的查询:blog(belongsTo)->user(belongsTo)->city. ===== **morphMany**:(**多态一对多拥有**:多态指的是一行数据可以从属多个不同的主表)如果针对某个用户和某个博客都能发表评论,那么用户、博客和评论之间就形成了一种多态一对多的关联关系,也就是说用户会有多个评论(morphMany关联,以用户模型为参照),博客会有多个评论(morphMany关联,以博客模型为参照),但评论表只有一个,也就是评论有多种形态,具体它是评论博客还是评论用户,由另外一个表示类型的字段type来确定;morphMany 和 hasMany的差别就是不仅有表示主键的字段,同时多了一个表示类型的字段; **用法**:morphMany('关联模型','多态字段','多态类型'); * **关联模型** (必须):模型名或者模型类名 * **多态字段** :多态字段信息定义包含两种方式,字符串的话表示多态字段的前缀,数组则表示实际的多态字段 * **多态类型** :默认是当前模型名 数据表的多态字段一般包含两个字段:多态类型和多态主键。 如果多态字段使用字符串例如 `morph`,那么多态类型和多态主键字段分别对应 `morph_type` 和 `morph_id`,如果用数组方式定义的话,就改为 `['morph_type','morph_id']` 即可。 morphMany示例:在User模型中定义函数: ``` public function comments() { return $this->morphMany('Comment', 'commentable'); 完整写法:return $this->morphMany('Comment', ['commentable_type','commentable_id'],'user'); } ``` 在Blog模型中定义函数: ``` public function comments() { return $this->morphMany('Comment', 'commentable'); } ``` 解释:首先comment表中定义有字段:`commentable_id - integer 和 commentable_type - string;` 当morphMany第二个参数是字符串时例如'x',就当作是字段前缀,自动组合出x_id和x_type的字段名;如果你的字段名不是默认规则的,就需要用数组传递实际名称,这个数组必须是数字索引,并且从0开始,然后必须是type在前面,id 在后面.因为源码使用的是list()来解构数组,比如传的是:['type','type_id']. 如果你不传第二个参数,那么TP框架会自动获取你的关联定义函数名,在这里也就是'comments',但这个表名是错误的,评论表名是'comment',所以如果想省略第二个参数,那么就必须关联定义函数名是关联表名,而且关联表里面的2个关联字段命名前缀必须也是关联表名.想这样一切都刚刚好,这样就限制太多了.而且实际项目中几乎不会这样命名.所以我们使用中还是手动指定第二个参数,不省略. 第三个参数默认是当前模型名,也就是User和Blog.但很多时候我们设计表格会设计成type=1 是博客类型,type=2 是用户类型,这样查询效率会更高,这时我们可以直接指定传数字 1 或者 2,就可以指定要查的是博客类型还是用户类型的评论; ===== **morphTo**:(**多态一对一属于**)评论表对于博客和用户来说,不需要定义两个关联关系,而只需要定义一个morphTo关联(以评论模型为参照)即可,评论表的设计就会被改造以满足多态的设计,普遍的设计是会增加一个表示类型的字段来标识属于某个类型,从来确定哪个表是它的主表; **用法**:morphTo('多态字段','多态类型别名(数组)'); * **多态字段** :定义和 `morphMany` 一致 * **多态类型别名** :用于设置特殊的多态类型(比如用数字标识的多态类型) morphTo示例:在Comment模型中定义函数: ``` public function commentable() { return $this->morphTo(); 完整写法:return $this->morphTo(['commentable_type','commentable_id'],['1'=>'Blog','2'=>'User']); } ``` 解释:第一个参数多态字段跟morphMany的第二个参数用法一样;因为comment表中定义有字段:`commentable_id - integer 和 commentable_type - string;` 如果第一个参数是空值null,那么将自动获取关联定义函数名,也就是'commentable'作为前缀,然后自动组合出'commentable_type' 和'commentable_id',刚好组合出两个关联字段的名字; 第二个参数是针对多态类型字段commentable_type的值,有时候,我们设计type=1 是博客类型,type=2 是用户类型. 但在SQL中我们需要知道关联模型的表名,在TP框架中就是需要知道关联模型名,找到这个模型,自然就知道表名了. 传递别名数组过去,TP框架就可以解析出当commentable_type= 1时,应该去找Blog模型.如果这个关联模型的命名空间和当前模型不一样,就需要传递完整的命名空间路径,例如 '1'=>'app\model\Blog' ; morphTo的相对就是morphMany ,跟belongsTo 和hasMany的相对关系是一样的. commentable()返回的模型可能是Blog或User,看commentable_type对应的值.使用时要先判断是哪个模型; ======= **以上是7种关联关系定义的讲解,下面是5种操作方法的讲解** ======= | 方法名 | 作用 | | ------------- | ---------------- | | `relation` | 关联查询 | | `with` | 关联预载入 | | `withCount` | 关联统计 | | `load` | 关联延迟预载入 | | `together` | 关联自动写入 | | has | 约束关联存在判断 | | hasWhere | 约束关联查询 | **在控制器查询时候的使用方法**