Code前端首页关于Code前端联系我们

PHP laravel 中多对多关系示例详解

terry 2年前 (2023-09-25) 阅读数 50 #后端开发

有数据的表交叉交叉,相互链接。 Laravel 的一对一和一对多的话更容易理解。官网已经很详细了,这里就不解释了。我就不详细说了,但是我会注意多对多关系

常见的关联关系是多对多,即A表中的一条记录通过以下方式与B表中的多条记录关联中间表C,反之亦然。例如,一个用户有多个角色,反之亦然,一个角色对应多个用户。

为了测试这种关联,我们以官网用户 角色 为例:

需要三个数据表:users、roles 和 role_user。 role_user 表按照关联模型的名称按字母顺序命名(此处,role_user 是中间表)。它包含两列:user_id 和 role_id。

多对多关系是通过编写一个返回 PatriToMany 方法返回的结果的方法来定义的。废话不多说,直接上数据结构:

1:在 角色 表中创建角色,并添加一些初始化数据:

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', '[email protected]', '$2y$10$J/yXqscucanrHAGZp9G6..', 'ilocXtjZJwhrmIdLG1cKOYegeCwQCkuyx1pYAOLuzY2PpScQFT5Ss7lBCi7i', '2016-04-21 16:26:23', '2016-12-14 09:29:59');
INSERT INTO `users` VALUES ('2', 'baidu', '[email protected]', '$2y$10$', null, '2016-04-22 06:48:10', '2016-04-22 06:48:10');
INSERT INTO `users` VALUES ('3', 'fantasy', '[email protected]', '', null, '2017-06-14 10:38:57', '2017-06-15 10:39:01');

2:在 角色 表中创建角色,并添加一些初始化数据:

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,命名规则按字母顺序排列,角色位置在前面,用户放在后面,并用_分隔。这一切都是为了符合 Eloquent 的默认映射设置:如果在定义多对多关联时没有指定转换表,Eloquent 的默认转换表将使用此规则进行分区。 。

创建角色模型:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
 * Class Role
 * @package App\Models
 * @mixin \Eloquent
 */
class Role extends Model
{
}

然后我们在用户模型上定义角色方法:

<?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用户,那么需要通过中间表作为第二个参数输入PatriToMany方法。如果中间表中的字段不是user_id和role_id,我们这里命名为$user_id和$role_id。然后您需要将 $user_id 作为第三个参数传递给该方法,并将 $role_id 作为第四个参数传递给该方法。通过这个方法。如果关联方法的名称不是角色,也可以将第五个参数指定为关联方法对应的名称。

接下来,我们在控制器中编写测试代码:

<?php
$user = User::find(1);
$roles = $user->roles;
echo '用户'.$user->name.'所拥有的角色:';
foreach($roles as $role)
  echo $role->name.' '; //对应输出为:用户admin所拥有的角色:司令 军长 团战

当然,与所有其他类型的关系一样,您可以调用角色方法为关系查询添加条件约束:

User::find(1)->roles()->orderBy('name')->get();

如前所述,要指定关系连接表的名称,Eloquent 按字母顺序连接两个关系模型的名称。但是,您可以通过将第二个参数传递给BelongToMany方法来覆盖此约定:

return $this->belongsToMany('App\Models\Role', 'user_roles');

除了自定义链接表的表名称之外,您还可以通过将其他参数传递给BelongToMany方法来自定义表中的字段名称。列名。第三个参数是要定义关联关系的模型的外键名称,第四个参数是要加入的模型的外键名称:

return $this->belongsToMany('App\Models\Role', 'user_roles', 'user_id', 'role_id');

定义相对关联

用多对多关联定义相对关系关系 对于关联关系,只需调用关联模型中的 PatriToMany 方法即可。我们在角色模型中定义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');
  }
}

可以看到,定义的关联与对应用户中定义的关联一模一样,只不过第一个引用的是App\Models\Role,第二个引用的是App 。 \Models\User ,并且由于我们再次使用 PatriToMany 方法,因此在定义关系(而不是多对多)时,所有用于自定义表和键的常用选项都可用。

测试代码如下:

$role = Role::find(2);
$users = $role->users;
echo '角色#'.$role->name.'下面的用户:';
foreach ($users as $user) 
  echo $user->name.' ';//对应输出为:角色#司令下面的用户:admin fantasy baidu

可以看到,多对多关联处理需要一个转换表。 Eloquent 提供了几种与此临时表交互的有用方法。例如,假设有许多 Role 对象与 User 对象关联。访问这些关系后,我们可以使用这些模型上的枢轴属性来访问中间表的字段:

$roles = User::find(1)->roles;
foreach ($roles as $role) 
  echo $role->pivot->role_id.'<br>';//对应输出为:2 3 5

请注意,我们检索的每个角色模型都会自动分配一个枢轴属性。该属性包含一个表示临时表的模型,可以像其他 Eloquent 模型一样使用。

默认情况下,只能在枢轴对象上使用模型的主键。如果您的数据透视表包含其他属性,则必须在定义关系时指定它们:

return $this->belongsToMany('App\Models\Role')->withPivot('column1', 'column2');

例如,我们修改 role_user 表以添加用户名字段。数据如下:

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

编辑模型用户:

<?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

如果您希望数据透视表自动包含created_at和updated_at时间戳,请在定义关系时使用withTimestamps方法:

return $this->belongsToMany('App\Models\Role')->withTimestamps();

通过中间字段表关联过滤

定义关联时还可以使用wherePivot和wherePivotIn方法对patriToMany返回的结果集进行过滤:

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
        )
    )
)

如果您希望数据透视表自动包含时间戳created_at和updated_at。定义关系时使用 withTimestamps 方法:

return $this->belongsToMany('App\Models\Role')->withTimestamps();

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门