laravel中的多对多关系详解
数据表之间是纵横交叉、相互关联的,laravel的一对一,一对多比较好理解,官网介绍滴很详细了,在此我就不赘述啦,重点我记下多对多的关系
一种常见的关联关系是多对多,即表A的某条记录通过中间表C与表B的多条记录关联,反之亦然。比如一个用户有多种角色,反之一个角色对应多个用户。
为了测试该关联关系,我们沿用官网的用户角色示例:
需要三张数据表:users
、roles
和 role_user
,role_user
表按照关联模型名的字母顺序命名(这里role_user是中间表),并且包含 user_id
和 role_id
两个列。
多对多关联通过编写返回 belongsToMany
方法返回结果的方法来定义。废话不说多,直接上数据结构:
1:创建一个角色表roles
,并添加一些初始化数据:
- SET FOREIGN_KEY_CHECKS=0;
- -- ----------------------------
- -- Table structure for users
- -- ----------------------------
- DROP TABLE IF EXISTS `users`;
- CREATE TABLE `users` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
- `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
- `password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
- `remember_token` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
- `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`id`),
- UNIQUE KEY `users_email_unique` (`email`) USING BTREE
- ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
- -- ----------------------------
- -- Records of users
- -- ----------------------------
- INSERT INTO `users` VALUES ('1', 'admin', 'admin@163.com', '$2y$10$J/yXqscucanrHAGZp9G6..Tu1Md.SOljX3M8WrHsUdrgat4zeSuhC', 'ilocXtjZJwhrmIdLG1cKOYegeCwQCkuyx1pYAOLuzY2PpScQFT5Ss7lBCi7i', '2016-04-21 16:26:23', '2016-12-14 09:29:59');
- INSERT INTO `users` VALUES ('2', 'baidu', '10940370@qq.com', '$2y$10$2A5zJ4pnJ5uCp1DN3NX.5uj/Ap7P6O4nP2BaA55aFra8/rti1K6I2', null, '2016-04-22 06:48:10', '2016-04-22 06:48:10');
- INSERT INTO `users` VALUES ('3', 'fantasy', '1009@qq.com', '', null, '2017-06-14 10:38:57', '2017-06-15 10:39:01');
2:创建一个角色表roles
,并添加一些初始化数据:
- SET FOREIGN_KEY_CHECKS=0;
- -- ----------------------------
- -- Table structure for roles
- -- ----------------------------
- DROP TABLE IF EXISTS `roles`;
- CREATE TABLE `roles` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
- `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
- -- ----------------------------
- -- Records of roles
- -- ----------------------------
- INSERT INTO `roles` VALUES ('1', '超级版主', '2016-04-21 16:26:23', '2016-12-14 09:29:59');
- INSERT INTO `roles` VALUES ('2', '司令', '2016-04-22 06:48:10', '2016-04-22 06:48:10');
- INSERT INTO `roles` VALUES ('3', '军长', '2017-06-14 10:38:57', '2017-06-15 10:39:01');
- INSERT INTO `roles` VALUES ('4', '司长', '2017-06-07 10:41:41', '2017-06-15 10:41:51');
- INSERT INTO `roles` VALUES ('5', '团战', '2017-06-22 10:41:44', '2017-06-28 10:41:54');
- INSERT INTO `roles` VALUES ('6', '小兵', '2017-06-22 10:41:47', '2017-06-22 10:41:56');
3:创建一个中间表role_user
用于记录users
表与roles
表的对应关系,并添加一些初始化数据:
- SET FOREIGN_KEY_CHECKS=0;
- -- ----------------------------
- -- Table structure for role_user
- -- ----------------------------
- DROP TABLE IF EXISTS `role_user`;
- CREATE TABLE `role_user` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `user_id` int(11) DEFAULT NULL,
- `role_id` int(11) DEFAULT NULL,
- `created_at` datetime DEFAULT NULL,
- `updated_at` datetime DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;
- -- ----------------------------
- -- Records of role_user
- -- ----------------------------
- INSERT INTO `role_user` VALUES ('', '', '', '2017-06-07 11:42:13', '2017-06-21 11:32:16');
- INSERT INTO `role_user` VALUES ('', '', '', '2017-06-07 11:32:13', '2017-06-07 11:22:13');
- INSERT INTO `role_user` VALUES ('', '', '', '2017-06-07 11:32:13', '2017-06-07 11:12:13');
- INSERT INTO `role_user` VALUES ('', '', '', '2017-06-07 11:32:13', '2017-06-07 11:22:13');
- INSERT INTO `role_user` VALUES ('', '', '', '2017-06-07 11:32:13', '2017-06-07 11:52:13');
- INSERT INTO `role_user` VALUES ('', '', '', '2017-06-07 11:32:13', '2017-06-07 11:42:13');
- INSERT INTO `role_user` VALUES ('', '', '', '2017-06-07 11:42:13', '2017-06-07 11:52:13');
注意我们定义中间表的时候没有在结尾加s并且命名规则是按照字母表顺序,将role放在前面,user放在后面,并且用_分隔,这一切都是为了适应Eloquent模型关联的默认设置:在定义多对多关联的时候如果没有指定中间表,Eloquent默认的中间表使用这种规则拼接出来。
创建一个Role
模型:
- <?php
- namespace App\Models;
- use Illuminate\Database\Eloquent\Model;
- /**
- * Class Role
- * @package App\Models
- * @mixin \Eloquent
- */
- class Role extends Model
- {
- }
然后我们在 User
模型上定义 roles
方法:
- <?php
- namespace App\Models;
- use Illuminate\Database\Eloquent\Model;
- /**
- * Class User
- * @package App\Models
- * @mixin \Eloquent
- */
- class User extends Model
- {
- /**
- * 用户角色
- */
- public function roles()
- {
- return $this->belongsToMany('App\Models\Role');
- }
- }
注:正如我们上面提到的,如果中间表不是role_user
,那么需要将中间表作为第二个参数传入belongsToMany
方法,如果中间表中的字段不是user_id
和role_id
,这里我们姑且将其命名为$user_id
和$role_id
,那么需要将$user_id
作为第三个参数传入该方法,$role_id
作为第四个参数传入该方法,如果关联方法名不是roles
还可以将对应的关联方法名作为第五个参数传入该方法。
接下来我们在控制器中编写测试代码:
- <?php
- $user = User::find(1);
- $roles = $user->roles;
- echo '用户'.$user->name.'所拥有的角色:';
- foreach($roles as $role)
- echo $role->name.' '; //对应输出为:用户admin所拥有的角色:司令 军长 团战
当然,和所有其它关联关系类型一样,你可以调用roles
方法来添加条件约束到关联查询上:
- User::find(1)->roles()->orderBy('name')->get();
正如前面所提到的,为了确定关联关系连接表的表名,Eloquent 以字母顺序连接两个关联模型的名字。不过,你可以重写这种约定 —— 通过传递第二个参数到 belongsToMany
方法:
- return $this->belongsToMany('App\Models\Role', 'user_roles');
除了自定义连接表的表名,你还可以通过传递额外参数到 belongsToMany
方法来自定义该表中字段的列名。第三个参数是你定义关联关系模型的外键名称,第四个参数你要连接到的模型的外键名称:
- return $this->belongsToMany('App\Models\Role', 'user_roles', 'user_id', 'role_id');
定义相对的关联关系
要定义与多对多关联相对的关联关系,只需在关联模型中调用一下 belongsToMany
方法即可。我们在 Role
模型中定义 users
方法:
- <?php
- namespace App\Models;
- use Illuminate\Database\Eloquent\Model;
- /**
- * Class Role
- * @package App\Models
- * @mixin \Eloquent
- */
- class Role extends Model
- {
- /**
- * 角色用户
- */
- public function users()
- {
- return $this->belongsToMany('App\Models\User');
- }
- }
正如你所看到的,定义的关联关系和与其对应的User
中定义的一模一样,只是前者引用 App\Models\Role
,后者引用App\Models\User
,由于我们再次使用了 belongsToMany
方法,所有的常用表和键自定义选项在定义与多对多相对的关联关系时都是可用的。
测试代码如下:
- $role = Role::find(2);
- $users = $role->users;
- echo '角色#'.$role->name.'下面的用户:';
- foreach ($users as $user)
- echo $user->name.' ';//对应输出为:角色#司令下面的用户:admin fantasy baidu
正如你看到的,处理多对多关联要求一个中间表。Eloquent 提供了一些有用的方法来与这个中间表进行交互,例如,我们假设 User
对象有很多与之关联的 Role
对象,访问这些关联关系之后,我们可以使用这些模型上的pivot
属性访问中间表字段:
- $roles = User::find(1)->roles;
- foreach ($roles as $role)
- echo $role->pivot->role_id.'<br>';//对应输出为:2 3 5
注意我们获取到的每一个 Role
模型都被自动赋上了 pivot
属性。该属性包含一个代表中间表的模型,并且可以像其它 Eloquent 模型一样使用。
默认情况下,只有模型主键才能用在 pivot
对象上,如果你的 pivot
表包含额外的属性,必须在定义关联关系时进行指定:
- return $this->belongsToMany('App\Models\Role')->withPivot('column1', 'column2');
比如我们修改role_user表增加一个字段 username 数据如下:
修改模型User:
- <?php
- namespace App\Models;
- use Illuminate\Database\Eloquent\Model;
- /**
- * Class User
- * @package App\Models
- * @mixin \Eloquent
- */
- class User extends Model
- {
- /**
- * 用户角色
- */
- public function roles()
- {
- //return $this->belongsToMany('App\Models\Role');
- return $this->belongsToMany('App\Models\Role')->withPivot('username');
- }
- }
测试代码如下:
- $user = User::find(1);
- foreach ($user->roles as $role)
- echo $role->pivot->username;//对应输出为:马特马特2马特3
如果你想要你的 pivot
表自动包含created_at
和 updated_at
时间戳,在关联关系定义时使用 withTimestamps
方法:
- return $this->belongsToMany('App\Models\Role')->withTimestamps();
通过中间表字段过滤关联关系
你还可以在定义关联关系的时候使用 wherePivot
和 wherePivotIn
方法过滤belongsToMany
返回的结果集:
- return $this->belongsToMany('App\Models\Role')->withPivot('username')->wherePivot('username', '马特2');
- //return $this->belongsToMany('App\Models\Role')->wherePivotIn('role_id', [1, 2]);
测试代码如下:
- $user = User::find(1);
- print_r($user->roles->toArray());
以上对应输出:
- Array
- (
- [] => Array
- (
- [id] =>
- [name] => 军长
- [created_at] => -- ::
- [updated_at] => -- ::
- [pivot] => Array
- (
- [user_id] =>
- [role_id] =>
- [username] => 马特2
- )
- )
- )
如果你想要你的pivot
表自动包含created_at
和updated_at
时间戳,在关联关系定义时使用withTimestamps
方法:
- return $this->belongsToMany('App\Models\Role')->withTimestamps();
这一节我先讲到这里啦,下一节我将讨论更复杂的三种关联关系及其在模型中的定义及使用。未完待续...
laravel中的多对多关系详解的更多相关文章
- Hibernate中的多对多关系详解(3)
前面两节我们讲到了一对一的关系,一对多,多对一的关系,相对来说,是比较简单的,但有时,我们也会遇到多对多的关系,比如说:角色与权限的关系,就是典型的多对多的关系,因此,我有必要对这种关系详解,以便大家 ...
- Hibernate一对多和多对一关系详解 (转载)
:双向一对多关系,一是关系维护端(owner side),多是关系被维护端(inverse side).在关系被维护端需要通过@JoinColumn建立外键列指向关系维护端的主键列. publ ...
- Laravel中Trait的用法实例详解
本文实例讲述了Laravel中Trait的用法.分享给大家供大家参考,具体如下: 看看PHP官方手册对Trait的定义: 自 PHP 5.4.0 起,PHP 实现了代码复用的一个方法,称为 trait ...
- Hibernate中的一对多关系详解(2)
一对多的关系:例如,部门对员工,一个部门可以有多个员工 多对一的关系:例如,员工对部门,多个员工属于一个部门,并且每个员工只能属于一个部门 那么一对多.多对一在数据库中的是怎样表示的呢?好多话都不说了 ...
- Qt中QGraphics类坐标映射关系详解
1.Item(图元)坐标:属于局部坐标,通常以图元中心为原点(中心对称),非中心对称类,比如dialog类,一般以左上角为原点,正方向x朝右,y朝下. 2.setPos的坐标是父类坐标系的坐标,一般对 ...
- slf4j log4j logback关系详解和相关用法
slf4j log4j logback关系详解和相关用法 写java也有一段时间了,一直都有用slf4j log4j输出日志的习惯.但是始终都是抱着"拿来主义"的态度,复制粘贴下配 ...
- 【转】UML类图与类的关系详解
UML类图与类的关系详解 2011-04-21 来源:网络 在画类图的时候,理清类和类之间的关系是重点.类的关系有泛化(Generalization).实现(Realization).依赖(D ...
- C#中的Linq to Xml详解
这篇文章主要介绍了C#中的Linq to Xml详解,本文给出转换步骤以及大量实例,讲解了生成xml.查询并修改xml.监听xml事件.处理xml流等内容,需要的朋友可以参考下 一.生成Xml 为了能 ...
- UML类图与类的关系详解
摘自:http://www.uml.org.cn/oobject/201104212.asp UML类图与类的关系详解 2011-04-21 来源:网络 在画类图的时候,理清类和类之间的关系是重点.类 ...
随机推荐
- JavaEE开发之SpringMVC中的路由配置及参数传递详解
在之前我们使用Swift的Perfect框架来开发服务端程序时,聊到了Perfect中的路由配置.而在SpringMVC中的路由配置与其也是大同小异的.说到路由,其实就是将URL映射到Java的具体类 ...
- 【zzulioj 2127】 tmk射气球
比较简单的题,直接求空间中一个点到直线的距离而已,这道题说了直线和水平的平面 平行,我们可以先求投影到直线的距离,然后再算当前点到直线的距离. Description 有一天TMK在做一个飞艇环游世界 ...
- EZChart - 在线图表生成器
朋友写材料时,需要用到一些分析图表,嫌Excel的太丑,就为他写了一个在线图表生成器. 纯静态实现,基于:H5 + Bootstrap + FusionCharts 本地存储使用H5的localSto ...
- css3实现checkbox变按钮
css3实现checkbox变按钮 .search_checkbox { margin: 0; padding: 0; margin-left: 15px; display: inline-block ...
- 如何设置文本不换行省略号显示等CSS常用文本属性
如何让多余的文本省略号显示首先要说几个属性的作用: whitespace:nowrap 中文行末不断行显示 overflow: 控制超出文本的显示方式:hidden 超出范围文本隐藏:scroll 始 ...
- CSS垂直和水平居中
在css中,居中使用十分频繁. 居中分为水平和垂直居中 水平居中十分简单: body{ background:#f90; } body统一为这个颜色 div { margin:0 auto; back ...
- JS面向对象,创建,继承
很开心,最近收获了很多知识,而且发现很多东西,以前理解的都是错的,或者是肤浅的,还以为自己真的就get到了精髓,也很抱歉会影响一些人往错误的道路上走,不过这也告诉了我们,看任何一篇文章都不能盲目的去相 ...
- hdu4185二分图匹配
Thanks to a certain "green" resources company, there is a new profitable industry of oil s ...
- shopping_cart
#!/usr/bin/env python # -*- coding: utf-8 -*- print('欢迎土豪光临随心所欲旗舰店') user_money = int(input('老板,请输入你 ...
- javascript 函数和作用域(闭包、作用域)(七)
一.闭包 JavaScript中允许嵌套函数,允许函数用作数据(可以把函数赋值给变量,存储在对象属性中,存储在数组元素中),并且使用词法作用域,这些因素相互交互,创造了惊人的,强大的闭包效果.[upd ...