Laravel中如何为带外键的关联表编写Seeder数据填充
Laravel多对多中间表Seeder填充实现
前置代码修复
你现有Movie模型的多对多关联定义存在类名引用错误,关联目标写为了Menu::class,必须先修正为Genre::class,否则关联写入会报错,修正后代码:
class Movie extends Model { use HasFactory; protected $fillable = ['name', 'status', 'image_path']; public function genres() { return $this->belongsToMany(Genre::class, 'genre_movie'); } }
实现思路
不需要手动硬编码genre_id、movie_id,直接通过Eloquent模型提供的多对多关联操作方法写入中间表,框架会自动获取对应记录的主键ID,配合外键约束完全可以避免ID匹配错误。注意填充顺序必须是先填充genres、movies两张主表,最后填充中间关联表。
步骤1:配置Seeder执行顺序
在database/seeders/DatabaseSeeder.php中按顺序注册Seeder,保证主表数据先于中间表生成:
public function run() { $this->call([ GenreSeeder::class, MovieSeeder::class, GenreMovieSeeder::class, // 中间表Seeder最后执行 ]); }
步骤2:编写主表Seeder
先写入两张主表的测试数据,示例如下:
GenreSeeder
// database/seeders/GenreSeeder.php <?php namespace Database\Seeders; use App\Models\Genre; use Illuminate\Database\Seeder; class GenreSeeder extends Seeder { public function run() { $genres = [ ['name' => '动作'], ['name' => '喜剧'], ['name' => '科幻'], ['name' => '爱情'], ['name' => '恐怖'], ['name' => '犯罪'], ]; foreach ($genres as $genre) { Genre::create($genre); } } }
MovieSeeder
// database/seeders/MovieSeeder.php <?php namespace Database\Seeders; use App\Models\Movie; use Illuminate\Database\Seeder; class MovieSeeder extends Seeder { public function run() { $movies = [ ['name' => '流浪地球2', 'status' => 1, 'image_path' => 'images/lldq2.jpg'], ['name' => '满江红', 'status' => 1, 'image_path' => 'images/mjh.jpg'], ['name' => '消失的她', 'status' => 1, 'image_path' => 'images/xsdt.jpg'], ['name' => '孤注一掷', 'status' => 1, 'image_path' => 'images/gzyz.jpg'], ]; foreach ($movies as $movie) { Movie::create($movie); } } }
步骤3:编写中间表Seeder
提供两种常用的填充逻辑,按需选择即可:
方案A:随机生成关联关系
适合测试场景,给每个分类随机绑定2-3部电影:
// database/seeders/GenreMovieSeeder.php <?php namespace Database\Seeders; use App\Models\Genre; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; class GenreMovieSeeder extends Seeder { public function run() { // 清空中间表旧数据,避免唯一键冲突 DB::table('genre_movie')->truncate(); $allMovieIds = \App\Models\Movie::pluck('id'); // 遍历所有分类,随机绑定电影 Genre::all()->each(function ($genre) use ($allMovieIds) { $randomMovieIds = $allMovieIds->random(rand(2, 3))->toArray(); // attach方法会自动写入当前分类ID和选中的电影ID到中间表 $genre->movies()->attach($randomMovieIds); }); } }
方案B:固定绑定关联关系
适合需要精确控制电影和分类对应关系的场景,按名称匹配对应模型,不需要手动查ID:
public function run() { DB::table('genre_movie')->truncate(); // 自定义绑定规则:键为电影名,值为对应分类名数组 $bindMap = [ '流浪地球2' => ['动作', '科幻'], '满江红' => ['动作', '喜剧'], '消失的她' => ['爱情', '恐怖'], '孤注一掷' => ['动作', '犯罪'], ]; foreach ($bindMap as $movieName => $genreNames) { $movie = \App\Models\Movie::where('name', $movieName)->first(); if (!$movie) continue; $genreIds = Genre::whereIn('name', $genreNames)->pluck('id'); // sync方法会自动同步关联,自动删除该电影旧的无效关联 $movie->genres()->sync($genreIds); } }
多对多关联操作方法说明:
- attach($ids):追加传入ID对应的关联记录,不影响已有绑定关系
- detach($ids):删除传入ID对应的关联记录
- sync($ids):全量同步关联,仅保留传入ID对应的绑定关系,其余旧关联会自动清除
内容的提问来源于stack exchange,提问作者Illia
相关产品推荐
相关产品推荐

