thinkphp-关联模型初步、一对一关联查询、一对多关联查询
关联模型
关联模型,顾名思义,就是将表与表之间进行关联和对象化,即合并两张表的数据,然后进行增删改查的操作。
比如以下两张表,tp_user 表,主键为:id;附属表:tp_profile,建立两个字段:user_id 和hobby,外键是user_id;
tp_user
id | username | password |
---|---|---|
12 | 科比 | 123 |
23 | 杜兰特 | 321 |
tp_profile
id | user_id | hobby |
---|---|---|
1 | 12 | 孙悟天 |
2 | 13 | 科比 |
一般主表存放一些经常存取的信息,附表存放一些不常用的额外信息,从而节约资源。
现在我们需要根据主表查找到附表的一些信息。
第一步就是要建立表之间的关联关系:tp_profile的user_id与tp_user的id必须是对应的
第二步就是创建User 模型和Profile 模型,均为空模型;
model/User.php
<?php
namespace app\model;
use think\Model;
class User extends Model {
}
model/Profile.php
<?php
namespace app\model;
use think\Model;
class Profile extends Model {
}
关联有正向关联(主表关联附表),也有反向关联(附表关联主表)。
正向关联:User 模型端(主),需要关联Profile(附),具体方式如下:
<?php
namespace app\model;
use think\Model;
class User extends Model {
// 与表名一一对应
public function profile()
{
//hasOne 表示一对一关联,参数一表示附表,参数二外键,默认user_id
return $this->hasOne(Profile::class);
}
}
Profile类依然保持空即可。
然后新建控制器controller/Grade.php
<?php
namespace app\controller;
use app\model\User as UserModel;
class Grade {
public function index()
{
$user = UserModel::find(12);
return json($user->profile);
// 找到附表中的某一条属性的值
echo $user->profile->hobby;
}
}
这里如果直接json($user)是看不到profile表中字段内容的,如果要获取profile表中对应的内容,就需要$user-profile。
当然主类中也可以改名,但是相应的控制器中获取的也要改(建议名称都保持一致):
class User extends Model {
// 与表名一一对应
public function profileAAA()
{
//hasOne 表示一对一关联,参数一表示附表,参数二外键,默认user_id
return $this->hasOne(Profile::class);
}
}
echo $user->profileAAA->hobby;
关于外键:
namespace app\model;
use think\Model;
class User extends Model {
// 与表名一一对应
public function profile()
{
//hasOne 表示一对一关联,参数一表示附表,参数二外键,默认user_id
return $this->hasOne(Profile::class, 'user_id');
}
}
外键就是附表中与主表相关联的属性。如果默认是User_id那么就可以省略不写,当不是user_id的时候,就需要手动指定。
对于关联方式,系统提供了9 种方案,具体如下:
hasOne | 一对一 |
---|---|
belongTo | 一对一 |
hasMany | 一对多 |
hasOneThrough | 远程一对一 |
belongsToMany | 多对多 |
morphMany | 多态一对多 |
morphOne | 多态一对一 |
morphTo | 多态 |
hasManyThrough | 远程一对多 |
常用的也就是上面标红的三种,一对多:一篇文章对应它的多条评论。多对多:一个用户对应很多种角色(审核人员、主管)然而审核人员又对应了科比,杜兰特等人;
一对一关联反向操作
在model/profile.php中
namespace app\model;
use think\Model;
class Profile extends Model {
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
在控制器中
namespace app\controller;
use app\model\User as UserModel;
use app\model\Profile as ProfileModel;
class Grade {
public function index()
{
$profile = ProfileModel::find(1);
echo $profile->user->username;
}
}
正反向关联也就是关联关系和相对的关联关系
一对一 | hasOne | belongsTo |
---|---|---|
一对多 | hasMany | belongsTo |
多对多 | belongsToMany | belongsToMany |
一对一关联查询
hasOne模式
hasOne 模式,适合主表关联附表
hasOne('关联模型',['外键','主键']);
return $this->hasOne(Profile::class,'user_id', 'id');
关联模型(必须):关联的模型名或者类名
外键:默认的外键规则是当前模型名(不含命名空间,下同)+_id ,例如user_id
主键:当前模型主键,默认会自动获取也可以指定传入
关联修改
比如现在需要通过主表user中的id找到关联的附表对应的行 并修改其内容
在控制器中:
$user = UserModel::find(19);
$user->profile->save([
'hobby' => '酷爱科比'
]);
关联新增
通过主表id找到关联附表中对应行 如果没有关联行就新增
$user = UserModel::find(19);
$user->profile()->save([
'hobby' => '酷爱橘艾莱娜'
]);
这时候我们会发现,如果附表中原本存在关联行的情况下,这样执行依然会在附表中新增一条,这就会变成一对多了
所以就需要在附表中先判断,user_id存在不存在,否则会直接新增一条。
->profile 属性方式可以修改数据,->profile()方法方式可以新增数据;
belongsTo模式
belongsTo 模式,适合附表关联主表,具体设置方式如下(反向操作):
belongsTo('关联模型',['外键','关联主键']);
return $this->belongsTo(Profile::class,'user_id', 'id');
关联模型(必须):模型名或者模型类名
外键:当前模型外键,默认的外键名规则是关联模型名+_id关联
主键:关联模型主键,一般会自动获取也可以指定传入
反向关联
使用hasOne()也能模拟belongsTo()来进行查询,即hasOne也能实现反向查询,这样就不需要belongs方法了:
参数一表示的是User 模型类的profile 方法,而非Profile 模型类
// id=1代表的是z找到附表的id=1的行
$user = UserModel::hasWhere('profile', ['id' => 1])->find();
return $user;
通过profile模型 找到附表id=1的行 找出来 然后找到对应的主表的id=19的值
采用闭包,这里是两张表操作,会导致id 识别模糊,需要指明表
使用闭包好处就是可以自定义SQL查询语句,实现更多的操作。
$user = UserModel::hasWhere('profile', function($query) {
$query->where('id', '=', 1);
})->find();
return $user;
一对多关联查询
hasMany
hasMany 模式,适合主表关联附表,实现一对多查询
hasMany('关联模型',['外键','主键']);
return $this->hasMany(Profile::class,'user_id', 'id');
关联模型(必须):模型名或者模型类名
外键:关联模型外键,默认的外键名规则是当前模型名+_id
主键:当前模型主键,一般会自动获取也可以指定传入
比如在模型中
namespace app\model;
use think\Model;
class User extends Model {
// 一对多
public function profile()
{
return $this->hasMany(Profile::class, 'user_id');
}
}
控制器中
$user = UserModel::find(19);
return json($user->profile);
最终会返回如下形式:
[
{
id: 1,
user_id: 19,
hobby: "酷爱科比",
status: 1
},
{
id: 37,
user_id: 19,
hobby: "不喜欢吃青椒",
status: 1
},
{
id: 46,
user_id: 19,
hobby: "酷爱橘艾莱娜",
status: 0
}
]
所以一对一跟一对多在查询方面的区别就是,一对多最终返回的是一个数组,返回的是多条;而一对一只会返回一条;
使用->profile()方法模式,可以进一步进行数据的筛选
$user = UserModel::find(19);
return json($user->profile()->select());
注意,这里如果仅仅使用$user->profile()是不会有任何结果返回的,必须配合->select()
$user->profile()->select()
效果等价于
$user->profile
使用方法可以对结果进行链式查询,增加条件
return json($user->profile()->where('id', '>=', 10)->select());
使用has()方法,查询关联附表的主表内容(从附表查询主表的内容),比如大于等于2 条的主表记录;
UserModel::has('profile', '>=', 2)->select();
意思是:要在附表中查找,有两条以上主表关联的数据,一条的就不搜出来
附表:
id | user_id | grade |
---|---|---|
1 | 19 | xxx |
2 | 4 | xxx |
3 | 19 | xx |
主表
id(user_id) | content |
---|---|
19 | uu |
$user = UserModel::find(19);
return json($user->has('profile', '>=', 2)->select());
最终返回的是查询到的主表信息,就是主表中id=19的行在附表中有两条关联数据。
[
{
id: 19,
username: "蜡笔小新",
password: "123"
}
]
使用hasWhere()方法,查询关联附表筛选后记录,比如兴趣审核通过的主表记录
UserModel::hasWhere('profile', ['status'=>1])->select();
使用save()和saveAll()进行关联新增和批量关联新增
给附表中,新增一条/多条,并指定附表中user_id=19
新增一条
$user->profile()->save(['hobby'=>'测试喜好', 'status'=>1]);
新增多条
$user = UserModel::find(19);
$user->profile()->saveAll([
[
'hobby' => 'ceshi1',
'status' => 1
],
[
'hobby' => 'ceshi2',
'status' => 1
]
]);
关联删除
比如要在删除主表中的信息时候,也要链带将附表中的关联信息也一起删掉
使用together()方法,可以删除主表内容时,将附表关联的内容全部删除;
// 关联删除
$user = UserModel::with('profile')->find(331);
// 注意together参数是一个数组,是可以关联多个附表,就是将多个附表中user_id=xx的都删除
$user->together(['profile'])->delete();
注意,这里如果->find(331),而id=331在主表中不存在的话,系统会报错。
一对多,是没有修改的。