ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# **定义:** >[info] >### **hasOne**('关联模型(从表)类名', ' 从表外键', '当前模型(主表)主键') >### **belongsTo**('关联模(主表)型类名','当前模型(从表)外键', '关联(主表)主键'); > * 第二个参数外键形如user_id时 可省略 > * 第三个参数可省略 >[danger]一对一关系,存在主从关系(主表和从表 ),主表**不包含**外键**,**从表**包含**外键。 **hasOne** 和 **belongsTo** 都是一对一关系,区别: 在 **主表** (不包含外键)的模型中建立关联关系,用 **hasOne**  在 **从表**(包含外键)模型中建立关联关系,用 **belongsTo** 用法: >[info]在当前模型下 申明一个**自定义函数**存放关联定义(用于调用hasOne或者belongsTo并返回) >然后使用with或者hasWhere获取最终关联数据,上面定义的函数名作为这两个方法的第一个参数传入 例子: user(主表:关联从表用hasOne): | id | name | status | --- | --- | --- | | 1 | thinkphp6.0 | 1 | | 2 | tom |1 | profile(从表:关联主表用belongsTo): | id | uid | nickname|email| | --- | --- | --- |--- | | 1 | 1 | tp6.0 |123@123.com| # **在用户表中关联用户详情表(从)表** >[danger]## **hasOne('关联模型类名', '外键', '主键');** ~~~ <?php namespace app\admin\model; use think\Model; class User extends Model { public function userProfile() { return $this->hasOne(Profile::class, 'uid');//如果第二个参数外键是user_id时框架能自动获取到,就可以省略了 } } ~~~ 定义好关联之后,就可以使用下面的**关联查询**方法获取关联数据: ~~~ $user = User::find(1); // 输出Profile关联模型的nickname属性 echo $user->user_profile->nickname; ~~~ >[danger]**注意:** 关联方法的命名规范是驼峰法(如上面的userProfile), 而关联属性则一般是小写+下划线的方式,系统在获取的时候会自动转换对应, 所以读取`user_profile`关联属性则对应的关联方法应该是`userProfile`。 ### **根据关联数据查询** 上面**关联查询**使用的是id,如果有其他条件呢? 可以根据关联表的条件来查询当前模型对象数据,例如: ~~~ <?php namespace \app\admin\model; use think\Model; class User extends Model { public function userProfile() { return $this->hasOne(Profile::class, 'uid'); } } ~~~ 上面在User里关联profile数据表后我们就可以使用profile里的条件来查询数据 ~~~ // 查询用户昵称是think的用户且是profile表的字段 // 注意第一个参数是上面关联方法名(不是关联模型名),hasWhere返回Query对象 $users = \app\admin\User::hasWhere('userProfile', ['nickname'=>'tom'])->select(); // 可以使用闭包查询 $users = \app\admin\User::hasWhere('userProfile', function($query) { $query->where('nickname', 'like', 'think%'); })->select(); ~~~ ### **预载入查询** 可以使用预载入查询解决典型的[`N+1`]()查询问题,使用: ~~~ $users = \app\admin\User::with('userProfile')->select(); foreach ($users as $user) { echo $user->user_profile->nickname; } ~~~ 怎么实现`$user->nickname;`也能获取到profile表的nickname的值呢,可以通过下面的绑定属性到父模型 ### **绑定属性到父模型:bind()** ~~~ <?php namespace app\admin\model; use think\Model; class User extends Model { public function userProfile() { return $this->hasOne(Profile::class, 'uid')->bind(['nickname', 'email']); } } ~~~ >[info]或者指定绑定属性别名 ~~~ <?php namespace app\admin\model; use think\Model; class User extends Model { public function userProfile() { return $this->hasOne(Profile::class, 'uid')->bind([ 'email', 'truename' => 'nickname', ]); } } ~~~ 然后使用关联预载入查询的时候,可以使用 ~~~ $user = \app\admin\User::with('userProfile')->find(1); // 直接输出Profile关联模型的绑定属性 echo $user->email; echo $user->truename; ~~~ 绑定关联模型的属性支持读取器。 > 如果不是预载入查询,请使用模型的`appendRelationAttr`方法追加属性。 也可以使用动态绑定关联属性,可以使用 ~~~ $user = User::find(1)->bindAttr('profile',['email','nickname']); // 输出Profile关联模型的email属性 echo $user->email; echo $user->nickname; ~~~ 同样支持指定属性别名 ~~~ $user = User::find(1)->bindAttr('profile',[ 'email', 'truename' => 'nickname', ]); // 输出Profile关联模型的email属性 echo $user->email; echo $user->truename; ~~~ 上面的代码使用的是`IN`查询,只会产生2条SQL查询。如下: ``` SELECT * FROM `wy_user SELECT * FROM `wy_profile` WHERE `uid` IN (1,2,3,4,...) ``` >[danger] 由上可知wy_user表中的id必须存在于wy_profile表的uid中否则抛出致命错误,所以在这注意做好数据同步 >这里只针对一对一的情况,不管profileb表中存在多少uid为1的数据,通通只返回第一条uid为1的数据 如果要对关联模型进行约束,可以使用闭包的方式。 ~~~ $users = \app\admin\User::with(['userProfile' => function($query) { $query->field('id,user_id,name,email'); }])->select(); foreach ($users as $user) { echo $user->profile->nickname; } ~~~ `with`方法可以传入数组,表示同时对多个关联模型(支持不同的关联类型)进行预载入查询。 ~~~ $users = \app\admin\User::with(['userProfile','book'])->select(); foreach ($users as $user) { echo $user->user_profile->name; foreach($user->book as $book) { echo $book->name; } } ~~~ with支持**嵌套查询**:如 ~~~ //model/Banner.php namespace app\api\model; use think\Model; use think\Db; class Banner extends Model{ public function items(){ //banner关联 Banner_item表 return $this->hasMany('BannerItem','banner_id','id'); } } //model/BannerItem.php namespace app\api\model; use think\Model; class BannerItem extends Model{ public function img(){ //bannner_item 关联image表 return $this->hasOne('Image','img_id','id'); } } //model/Image.php namespace app\api\model; use think\Model; class Image extends Model{ } //在控制器方法中调用: $banner=\app\api\Model\Banner::with(['items','items.img'])->find($id); //多表关联(参数是定义的方法名 ) //items 参数表明Banner类里有一个封装关联关系items方法 //items.img 表示items方法里关联的表模型又关联了一个封装关联关系的img方法 ~~~ 如果需要使用`JOIN`方式的查询,直接使用`withJoin`方法,如下: ~~~ $users = \app\admin\User::withJoin('userProfile')->select(); foreach ($users as $user) { echo $user->user_profile->name; } ~~~ `withJoin`方法默认使用的是`INNER JOIN`方式,如果需要使用其它的,可以改成 ~~~ $users = \app\admin\User::withJoin('userProfile', 'LEFT')->select(); foreach ($users as $user) { echo $user->user_profile->name; } ~~~ >[danger]需要注意的是`withJoin`方式不支持嵌套关联,通常你可以直接传入多个需要关联的模型。 如果需要约束关联字段,可以使用下面的简便方法。 ~~~ $users = \app\admin\User::withJoin([ 'userProfile' => ['user_id', 'nickname', 'email'] ])->select(); foreach ($users as $user) { echo $user->user_profile->nickname; } ~~~ ### **关联保存** ~~~ $user = User::find(1); // 如果还没有关联数据 则进行新增 $user->userProfile()->save(['email' => 'thinkphp']); ~~~ 系统会自动把当前模型的主键传入`Profile`模型。 和新增一样使用`save`方法进行更新关联数据。 ~~~ $user = User::find(1); $user->user_profile->email = 'thinkphp'; $user->user_profile->save(); // 或者 $user->user_profile->save(['email' => 'thinkphp']); ~~~ ## **关联自动写入** 我们可以使用`together`方法更方便的进行关联自动写入操作。 写入 ~~~ $blog = new Blog; $blog->name = 'thinkphp'; $blog->title = 'ThinkPHP5关联实例'; $content = new Content; $content->data = '实例内容'; $blog->content = $content; $blog->together(['content'])->save(); ~~~ 如果绑定了子模型的属性到当前模型,可以指定子模型的属性 ~~~ $blog = new Blog; $blog->name = 'thinkphp'; $blog->title = 'ThinkPHP5关联实例'; $blog->content = '实例内容'; // title和content是子模型的属性 $blog->together(['content'=>['title','content']])->save(); ~~~ 更新 ~~~ // 查询 $blog = Blog::find(1); $blog->title = '更改标题'; $blog->content->data = '更新内容'; // 更新当前模型及关联模型 $blog->together(['content'])->save(); ~~~ 删除 ~~~ // 查询 $blog = Blog::find(1,'content'); // 删除当前及关联模型 $blog->together(['content'])->delete(); ~~~ # **在用户详情表中关联用户(主)表 (定义相对关联)** >[danger]## **hasOne('关联模型类名', '外键', '主键');** ~~~ <?php namespace app\admin\model; use think\Model; class Profile extends Model { public function user() { return $this->belongsTo(User::class, 'uid');//外键如果是user_id格式则可以省略 } } ~~~ 我们就可以根据档案资料来获取用户模型的信息 ~~~ $profile = \app\admin\Profile::find(1); // 输出User关联模型的属性 echo $profile->user->name; ~~~