Laravel 10模型多对多关联无法即时加载问题求助
Laravel 10 模型关联在created事件中无法即时获取的问题
环境
- Laravel 10
- PHP 8.1
数据库迁移代码
Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); Schema::create('teams', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); Schema::create('team_user', function (Blueprint $table) { $table->unsignedBigInteger('team_id'); $table->unsignedBigInteger('user_id'); $table->foreign('team_id')->references('id')->on('teams')->onDelete('cascade'); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); }); Schema::table('users', function (Blueprint $table) { $table->unsignedBigInteger('current_team_id')->after('id')->default(0); });
数据库数据
users表
INSERT INTO `users` (`id`, `current_team_id`, `name`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`) VALUES (100, 0, 'Test User', 'test@test.com', NULL, '$2y$10$JuEirza2w0.9Qv60Hk7f2.CVaG9/WuDzb9qEHYR.BgN/ZviVqqJL.', NULL, '2023-04-21 09:29:24', '2023-04-21 09:29:24');
team_user表
INSERT INTO `team_user` (`team_id`, `user_id`) VALUES (1, 100), (2, 100);
teams表
INSERT INTO `teams` (`id`, `name`, `created_at`, `updated_at`) VALUES (1, 'Team 1', '2023-04-17 08:46:55', '2023-04-21 09:25:29'), (2, 'Team 2', '2023-04-17 08:50:10', '2023-04-17 08:50:10');
模型定义
User模型
protected $fillable = [ 'current_team_id', 'name', 'email', 'password', ]; public function teams() { return $this->belongsToMany(Team::class); }
Team模型
protected $fillable = [ 'name', ]; public function users() { return $this->belongsToMany(User::class); }
问题重现
- 在User模型的
created事件中尝试获取关联团队:
protected static function booted(): void { static::created(function (User $user) { dd($user->teams()->first()->id); $user->current_team_id = $user->teams()->first()->id; }); }
触发错误:
Attempt to read property "id" on null
- 修改为重新查询用户后获取关联:
protected static function booted(): void { static::created(function (User $user) { $user = User::find($user->id); $teams = $user->teams()->get(); dd($teams); }); }
返回空集合:
Illuminate\Database\Eloquent\Collection {#2026 ▼ // app/Models/User.php:71 #items: [] #escapeWhenCastingToString: false }
但直接查询已存在的用户可以正常获取关联团队,且pivot表数据存储正确。
问题原因
核心原因有两点:
- 事件触发时机:
created事件在用户数据刚插入数据库时立即触发,但此时关联的team_user中间表记录还未被插入(通常是先创建用户,再执行关联attach操作,这一步在created事件之后)。 - 事务机制:如果用户创建和团队关联操作在同一个数据库事务中执行,
created事件触发时事务尚未提交,此时查询数据库无法看到未提交的关联数据。
解决方案
方案一:将逻辑移到关联操作之后
直接在用户创建并完成团队关联后,手动设置current_team_id:
$user = User::create([ // 用户字段 ]); // 关联团队 $user->teams()->attach([1, 2]); // 设置当前团队 $user->update(['current_team_id' => $user->teams()->first()->id]);
方案二:使用afterCommit延迟执行事件回调
如果必须依赖模型事件,使用afterCommit方法让回调在事务提交后执行,此时关联数据已写入数据库:
protected static function booted(): void { static::created(function (User $user) { $user->afterCommit(function () use ($user) { $firstTeam = $user->teams()->first(); if ($firstTeam) { $user->update(['current_team_id' => $firstTeam->id]); } }); }); }
方案三:提前指定当前团队ID
如果在创建用户时已经明确要关联的团队,可以直接在创建时赋值current_team_id,无需查询关联:
$firstTeamId = 1; $user = User::create([ 'name' => 'Test User', 'email' => 'test@test.com', 'password' => bcrypt('password'), 'current_team_id' => $firstTeamId, ]); $user->teams()->attach([$firstTeamId, 2]);
内容的提问来源于stack exchange,提问作者Nathan Bayne
相关产品推荐
相关产品推荐

