PHP laravel中的多对多关系实例详解

 更新时间:2017年06月07日 08:59:30   作者:范特西的麦田  
数据表之间是纵横交叉、相互关联的,laravel的一对一,一对多比较好理解,本文重点通过实例给大家讲解 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 ('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.'<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;

如果你想要你的 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();

以上所述是小编给大家介绍的PHP laravel中的多对多关系实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

相关文章

  • CI框架学习笔记(二) -入口文件index.php

    CI框架学习笔记(二) -入口文件index.php

    本文介绍的是CI框架的入口文件index.php的相关资料,需要的朋友可以参考下
    2014-10-10
  • php操作csv文件代码实例汇总

    php操作csv文件代码实例汇总

    这篇文章主要介绍了php操作csv文件代码实例汇总,本文给出6个操作CSV文件的代码实例,包括读取、写入、读取指定区间等,需要的朋友可以参考下
    2014-09-09
  • php中的strpos使用示例

    php中的strpos使用示例

    strpos()函数返回字符串在另一个字符串中第一次出现的位置。如果没有找到该字符串,则返回 false,下面看示例使用方法
    2014-02-02
  • 教你php如何实现验证码

    教你php如何实现验证码

    这篇文章教大家php如何实现验证码,验证码在表单实现越来越多了,但是用js的写的验证码,总觉得不方便,所以学习了下php实现的验证码,感兴趣的小伙伴们可以参考一下
    2016-01-01
  • 基于php-fpm 参数的深入理解

    基于php-fpm 参数的深入理解

    本篇文章是对php-fpm参数进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • laravel通用化的CURD的实现

    laravel通用化的CURD的实现

    这篇文章主要介绍了laravel通用化的CURD的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • 源码分析 Laravel 重复执行同一个队列任务的原因

    源码分析 Laravel 重复执行同一个队列任务的原因

    laravel 的队列服务对各种不同的后台队列服务提供了统一的 API,下面这篇文章通过源码分析给大家介绍了关于 Laravel 重复执行同一个队列任务的原因,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-12-12
  • PHP学习记录之面向对象(Object-oriented programming,OOP)基础【接口、抽象类、静态方法等】

    PHP学习记录之面向对象(Object-oriented programming,OOP)基础【接口、抽象类、静态方法等

    这篇文章主要介绍了PHP学习记录之面向对象(Object-oriented programming,OOP)基础,结合实例形式分析了PHP面向对象程序设计中接口、抽象类、静态方法等相关概念、原理、用法与操作注意事项,需要的朋友可以参考下
    2019-12-12
  • PHP实现长文章分页实例代码(附源码)

    PHP实现长文章分页实例代码(附源码)

    当文章内容比较长,为了更好的满足用户体验度,我们将文章内容分页显示处理,而一般分页处理是在后台发布文章的时候就将提交的内容生成多个分页后的静态文件。通过本文结合实例采用php动态将长文章内容进行分页处理
    2016-02-02
  • php实现的获取网站备案信息查询代码(360)

    php实现的获取网站备案信息查询代码(360)

    有时候我们需要获取网站备案信息,那么就可以用这个小偷程序,获取了,信息从360抓取
    2013-09-09

最新评论