Laravel 关联更新与批量赋值详解
基础关联更新
一对多关联更新
// 基本关联更新
$user = User::find(1);
$user->posts()->update(['status' => 'published']);
// 带条件更新
$user->posts()->where('status', 'draft')
->update(['published_at' => now()]);
多对多关联更新(中间表)
$user = User::find(1);
// 附加角色(带中间表数据)
$user->roles()->attach($roleId, ['expires_at' => now()->addMonth()]);
// 更新中间表数据
$user->roles()->updateExistingPivot($roleId, [
'expires_at' => now()->addMonths(6),
'status' => 'active'
]);
// 同步关联(只保留给定的ID)
$user->roles()->sync([
1 => ['status' => 'active'],
2 => ['status' => 'inactive'],
]);
关联模型批量赋值(create)
安全批量赋值
// 定义模型填充规则
class Post extends Model
{
protected $fillable = ['title', 'content', 'user_id'];
// 或使用不可填充
// protected $guarded = ['id', 'is_admin'];
}
// 通过关联创建
$user = User::find(1);
$post = $user->posts()->create([ => '新文章',
'content' => '内容...'
]);
// 批量创建
$postsData = [
['title' => '文章1', 'content' => '内容1'],
['title' => '文章2', 'content' => '内容2'],
];
$user->posts()->createMany($postsData);
批量赋值限制规则
class User extends Model
{
// 可批量赋值的字段
protected $fillable = ['name', 'email', 'password'];
// 或排除不可填充字段
protected $guarded = ['id', 'is_admin'];
// 自定义赋值逻辑
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
}
高级关联更新技巧
使用关联事件
// 模型事件
class User extends Model
{
protected static function booted()
{
static::updating(function ($user) {
// 更新前的逻辑
Log::info('用户信息更新', $user->getDirty());
});
static::updated(function ($user) {
// 更新后触发关联更新
$user->posts()->update(['cached_user_name' => $user->name]);
});
}
}
关联更新嵌套
// 手动事务处理
DB::transaction(function () {
$user = User::find(1);
// 更新主模型
$user->update([
'name' => '新名字',
'email' => 'new@email.com'
]);
// 更新关联模型
$user->posts()->update(['author_name' => '新名字']);
$user->profile()->update(['bio' => '新简介']);
// 同步关联并更新中间表
$user->roles()->sync([
1 => ['status' => 'active', 'updated_at' => now()],
2 => ['status' => 'inactive']
]);
});
// 使用宽松更新中跳过验证
$user->posts()->get()->each(function ($post) {
$post->category_id = 1;
$post->save(['timestamps' => false]);
});
前端批量赋值防护
使用FormRequest验证
class UpdateUserRequest extends FormRequest
{
public function authorize()
{
return $this->user()->can('update', $this->user);
}
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $this->user->id,
'password' => 'nullable|string|min:8',
'roles' => 'array|exists:roles,id'
];
}
public function messages()
{
return [
'name.required' => '姓名不能为空',
'email.email' => '邮箱格式不正确'
];
}
}
// 控制器中使用
public function update(User $user, UpdateUserRequest $request)
{
$user->update($request->validated());
// 更新关联角色
if ($request->has('roles')) {
$user->roles()->sync($request->roles);
}
return redirect()->route('users.show', $user);
}
JSON字段批量赋值
class Post extends Model
{
protected $casts = [
'meta' => 'array',
'tags' => 'json'
];
protected $fillable = ['title', 'content', 'meta', 'tags'];
}
// 更新JSON字段
$user->posts()->where('id', 1)->update([
'meta->views' => 100,
'meta->likes' => 50
]);
// 使用JSON更新
$user->posts->each(function ($post) {
$post->update([
'meta' => array_merge($post->meta, ['shared' => true])
]);
});
性能优化建议
// 批量更新而不是循环
// 不推荐
$user->posts->each(function ($post) {
$post->status = 'published';
$post->save();
});
// 推荐
$user->posts()->where('draft', true)
->update(['status' => 'published']);
// 选择性加载关联数据
$users = User::with(['posts' => function ($query) {
$query->select('id', 'title', 'status')
->where('status', 'active');
}])->get();
这些方法覆盖了 Laravel 中关联更新的主要场景,根据实际需求选择合适的方式,记得在处理大量数据时考虑性能影响。
