**模型关联的先后顺序**:先在模型中定义两个表之间的关系,然后在查询的时候使用不同的操作方法进行查询;
=======
**模型关联 的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 | 约束关联查询 |
**在控制器查询时候的使用方法**
- FA的JS调用机制说明
- FA的JS之Fast.api逐个详解
- FA页面渲染时后端传递数据给前端的方式
- FA的ajax查询数据的前后台流程
- FA特有的函数解释
- FA的鉴权Auth类
- extend\fast\Auth.php详解
- application\admin\library\Auth.php详解
- application\common\library\Auth.php详解
- FA的Token机制
- FA管理员(后台)的权限机制
- FA用户(前台和API)的权限机制
- FA在前台模板文件中进行鉴权
- FA的登录页面
- TP类Hook:钩子机制
- TP类Lang:多语言机制
- TP类Config:参数配置机制
- TP类Request:请求类
- TP的模型关联详解
- think-queue队列组件
- Queue.php
- \queue\Connector.php
- \queue\connector\Redis.php
- \queue\Job.php
- queue\job\Redis.php
- PHP规则:正则表达式
- PHP规则:闭包与匿名函数
- 项目架构说明
- 代码架构
- TP数据库where条件的各种写法
