数据表之间是纵横交叉、相互关联的,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 (‘1’, ‘1’, ‘2’, ‘2017-06-07 11:42:13’, ‘2017-06-21 11:32:16’);
INSERT INTO role_user
VALUES (‘2’, ‘1’, ‘3’, ‘2017-06-07 11:32:13’, ‘2017-06-07 11:22:13’);
INSERT INTO role_user
VALUES (‘3’, ‘2’, ‘4’, ‘2017-06-07 11:32:13’, ‘2017-06-07 11:12:13’);
INSERT INTO role_user
VALUES (‘4’, ‘1’, ‘5’, ‘2017-06-07 11:32:13’, ‘2017-06-07 11:22:13’);
INSERT INTO role_user
VALUES (‘5’, ‘3’, ‘6’, ‘2017-06-07 11:32:13’, ‘2017-06-07 11:52:13’);
INSERT INTO role_user
VALUES (‘6’, ‘3’, ‘2’, ‘2017-06-07 11:32:13’, ‘2017-06-07 11:42:13’);
INSERT INTO role_user
VALUES (‘7’, ‘2’, ‘2’, ‘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.’
‘;//对应输出为:2 3 5
\
注意我们获取到的每一个 Role
模型都被自动赋上了 pivot
属性。该属性包含一个代表中间表的模型,并且可以像其它 Eloquent 模型一样使用。
默认情况下,只有模型主键才能用在 pivot
对象上,如果你的 pivot
表包含额外的属性,必须在定义关联关系时进行指定:
return $this->belongsToMany(‘App\Models\Role’)->withPivot(‘column1’, ‘column2’);
比如我们修改role_user表增加一个字段 username 数据如下:
<?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
(
[0] => Array
(
[id] => 3
[name] => 军长
[created_at] => 2017-06-14 10:38:57
[updated_at] => 2017-06-15 10:39:01
[pivot] => Array
(
[user_id] => 1
[role_id] => 3
[username] => 马特2
)
)
)
如果你想要你的pivot
表自动包含created_at
和updated_at
时间戳,在关联关系定义时使用withTimestamps
方法:
return $this->belongsToMany(‘App\Models\Role’)->withTimestamps();