13.1 关联新增

1、比如有一个功能:给一个用户增加关联的书籍。则方法如下。

首先观察book表,没有时间字段,需要取消自动写入时间。同时,也要为book表设置取消批量赋值

  1. //取消批量赋值
  2. protected $guarded = [];
  3. //取消自动时间字段
  4. public $timestamps = false;

然后在控制类中写入:

  1. //先限定用户
  2. $user = User::find(19);
  3. //给这个用户关联的book 新增一条记录
  4. //user_id 会自动写入19,title 自定义
  5. $user->book()->save(new Book(['title' => '《哈利波特》']));

然后运行,其执行的SQL为:

  1. select * from `laravel_users` where `laravel_users`.`id` = 19 limit 1
  2. insert into `laravel_books` (`title`, `user_id`) values ('《哈利波特》', 19)

当然,也可以批量新增:

  1. //先限定用户
  2. $user = User::find(19);
  3. //批量新增
  4. $user->book()->saveMany([
  5. new Book(['title' => '《哈利波特》']),
  6. new Book(['title' => '《指环王》'])
  7. ]);

其执行的SQL为:

  1. select * from `laravel_users` where `laravel_users`.`id` = 19 limit 1
  2. insert into `laravel_books` (`title`, `user_id`) values ('《哈利波特》', 19)
  3. insert into `laravel_books` (`title`, `user_id`) values ('《指环王》', 19)

2、createcreateMany 只需要插入数组即可完成关联新增;

比如:

  1. //先限定用户
  2. $user = User::find(19);
  3. //关联插入
  4. $user->book()->create([
  5. 'title' => '《哈利波特》'
  6. ]);

再如:

  1. //先限定用户
  2. $user = User::find(19);
  3. //关联批量插入
  4. $user->book()->createMany([
  5. ['title' => '《哈利波特》'],
  6. ['title' => '《指环王》']
  7. ]);

其执行结果和上面一致。

3、此外,还有:findOrNewfirstOrNewfirstOrCreateupdateOrCreate 方法;

findOrNew:Find a model by its primary key or return new instance of the related model. firstOrNew:Get the first related model record matching the attributes or instantiate it. firstOrCreate:Get the first related record matching the attributes or create it. updateOrCreate:Create or update a related record matching the attributes, and fill it with values.

比如:

  1. //先限定用户
  2. $user = User::find(19);
  3. return $user->book()->firstOrNew([
  4. 'title' => '《哈利波特》'
  5. ]);

这种方式,如果能找到与title相关的数据,则返回该数据,如果没有找到,则生成一个模型(非数据库插入),再返回该模型,是临时的

再如:

  1. //先限定用户
  2. $user = User::find(19);
  3. return $user->book()->firstOrCreate([
  4. 'title' => '《哈利波特》'
  5. ]);

这种方式,如果能找到与title相关的数据,则返回该数据,如果没有找到,则在数据库中插入该数据,然后再返回插入的这条数据。

13.2 关联删除

使用 delete() 进行关联删除。如:

  1. //关联删除,删除user_id=19 的书
  2. $user = User::find(19);
  3. $user->book()->delete();

执行的SQL为:

  1. select * from `laravel_users` where `laravel_users`.`id` = 19 limit 1
  2. delete from `laravel_books` where `laravel_books`.`user_id` = 19 and `laravel_books`.`user_id` is not null

13.3 关联修改

1、使用 update() 方法进行关联修改。如:

//关联修改,修改 user_id=19 的书
$user = User::find(19);
$user->book()->update(['title' => '《修改书籍》']);

执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 19 limit 1

update `laravel_books` set `title` = '《修改书籍》' where `laravel_books`.`user_id` = 19 and `laravel_books`.`user_id` is not null

2、使用 associate() 方法来修改掉书籍关联的用户,即修改user_id:

//修改关联的外键,即:user_id 修改,换用户
$user = User::find(20);
$book = Book::find(9);
$book->user()->associate($user);
$book->save();

执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 20 limit 1
select * from `laravel_books` where `laravel_books`.`id` = 9 limit 1
update `laravel_books` set `user_id` = 20 where `id` = 9

另外,如果想取消一本书的拥有者,比如将 user_id 设置为null,字段要设置可以null;

$book = Book::find(8);
$book->user()->dissociate();
$book->save();

执行的SQL为:

select * from `laravel_books` where `laravel_books`.`id` = 8 limit 1
update `laravel_books` set `user_id` = '' where `id` = 8

3、在搜索书籍的对应用户的时候,空null 字段会导致用户出现null 数据;我们可以采用空对象默认模型的方式,去解决这个问题;

在Book模型中,为关联的代码,追加写入 withDefault() 方法,如下所示:

//Book.php
public function user()
{
    return $this->belongsTo(User::class, 'user_id', 'id')
        ->withDefault([
            'id' => 0,
            'username' => '游客用户'
        ]);
}

13.4 多对多关联的增删改

增删改的都是中间表。

1、多对多的新增:比如,给用户增加一个角色权限,具体如下:

//得到要添加权限的用户
$user = User::find(99);
//得到权限的id,比如超级管理员
$roleId = 1;
//给辉夜设置成超级管理员
$user->role()->attach($roleId);

执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
insert into `laravel_role_users` (`role_id`, `user_id`) values (1, 99)

2、如果你想给中间表附加details 字段的数据,可以使用第二参数;

//得到要添加权限的用户
$user = User::find(99);
//得到权限的id,比如超级管理员
$roleId = 1;
//给辉夜设置成超级管理员
///$user->role()->attach($roleId);
$user->role()->attach($roleId, ['details' => '喀']); //第二参数

执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
insert into `laravel_role_users` (`details`, `role_id`, `user_id`) values ('喀', 1, 99)

3、如果想移出某个用户的角色权限,可以使用detach()方法;

//权限的用户
$user = User::find(99);
//权限的id,比如超级管理员
$roleId = 1;
//删除一个角色权限
$user->role()->detach($roleId);

执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
delete from `laravel_role_users` and `user_id` = 99 and `role_id` in (1)

如果不指定中间表id($roleId),那么就移出这个用户的所有权限角色;

4、也支持批量处理,直接用数组传递参数即可;

//这里传递的是角色权限表的ID
$user->role()->attach([1,2,3]); //附加值:1 => ['detail' => 'xxx']
//删除指定的user_id
$user->role()->detach([1,2,3]);

如:

//权限的用户
$user = User::find(99);
//这里传递的是角色权限表的ID
$user->role()->attach([1 => ['details' => '11111'], 2 => ['details' => '222222'], 3 => ['details' => '333333']]);

执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
insert into `laravel_role_users` (`details`, `role_id`, `user_id`) values ('11111', 1, 99), ('222222', 2, 99), ('333333', 3, 99)

再如:

//权限的用户
$user = User::find(99);
//删除指定的user_id
$user->role()->detach([1,2,3]);

执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
delete from `laravel_role_users` where `user_id` = 99 and `role_id` in (1, 2, 3)

5、使用sync()方法,可以新增角色权限,且可以判断已存在而不再新增;

//同步关联,已存在不再新增
return $user->role()->sync([1,2,3]); //附加值:1 => ['detail' => 'xxx']

比如:

//权限的用户
$user = User::find(99);
//同步关联,已存在不再新增
return $user->role()->sync([1,2,3]);

第一次执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
select * from `laravel_role_users` where `user_id` = 99
insert into `laravel_role_users` (`role_id`, `user_id`) values (1, 99)
insert into `laravel_role_users` (`role_id`, `user_id`) values (2, 99)
insert into `laravel_role_users` (`role_id`, `user_id`) values (3, 99)

然后再执行一次,执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
select * from `laravel_role_users` where `user_id` = 99

这里是因为判断已经存在了,就无需新增了。

接下来,修改代码为:

//权限的用户
$user = User::find(99);
//同步关联,已存在不再新增
return $user->role()->sync([1,2,4]);//最后一个改为4

然后再执行,其 SQL 为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
select * from `laravel_role_users` where `user_id` = 99
delete from `laravel_role_users` where `user_id` = 99 and `role_id` in (3)
insert into `laravel_role_users` (`role_id`, `user_id`) values (4, 99)

这里注意,把已经存在的 3 删除了。

6、使用 udpateExistingPivot() 可更新指定 roleId 的额外字段;

//更新中间表的额外字段
$user->role()->updateExistingPivot($roleId, ['details'=>'喀']);

如:

//权限的用户
$user = User::find(99);
$roleId = 1;
//更新中间表的额外字段
$user->role()->updateExistingPivot($roleId, ['details'=>'哈哈哈']);

其执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
update `laravel_role_users` set `details` = '哈哈哈' where `user_id` = 99 and `role_id` in (1)

直接使用update()是更新所有;

如:

//权限的用户
$user = User::find(99);
//更新中间表的额外字段
$user->role()->update(['details'=>'呵呵呵']);

其执行的SQL为:

select * from `laravel_users` where `laravel_users`.`id` = 99 limit 1
update `laravel_roles` inner join `laravel_role_users` on `laravel_roles`.`id` = `laravel_role_users`.`role_id` set `details` = '呵呵呵' where `laravel_role_users`.`user_id` = 99

然后数据库中,所有user_id=99的记录的details字段,全部修改为 呵呵呵。

另:通过查看源码或IDE 代码提示的方法,有更多的操作;可自行阅读扩展。

以上。