防止播种唯一的密钥违规

I have a unique constraint in my database for two columns.

$table->unique(array("table_a_id", "table_b_id"));

Now I use Faker in a seeder class to fill in this pivot table:

$tableAIds = TableA::all()->lists("id")->toArray();
$tableBIds = TableB::all()->lists("id")->toArray();

foreach(range(1, 20) as $index) {
    $tableAId = $faker->randomElement($tableAIds);
    $tableBId = $faker->randomElement($tableBIds);

    DB::table("table_a_table_b_pivot")->insert([
        "table_a_id" => $tableAId,
        "table_b_id" => $tableBId
    ]);
}

However this creates duplicates and the seeding fails due to SQL error.

How do I make sure I don't try to insert duplicates?

Instead of using faker, use the collection's random method:

$tableAIds = TableA::all()->lists('id')->random(20);
$tableBIds = TableB::all()->lists('id')->random(20);

foreach(range(0, 19) as $index) {
    DB::table("table_a_table_b_pivot")->insert([
        "table_a_id" => $tableAIds[$index],
        "table_b_id" => $tableBIds[$index],
    ]);
}

Adjust this according to your needs.

For example, this might be what you really want:

$tableAIds = TableA::all()->lists('id');
$tableBIds = TableB::all()->lists('id');

foreach ($tableAIds->random(5) as $tableAId) {
    foreach ($tableBIds->random(2) as $tableBId) {
        DB::table("table_a_table_b_pivot")->insert([
            "table_a_id" => $tableAId,
            "table_b_id" => $tableBId,
        ]);
    }
}

Whatever it is, play with it yourself. No need for faker here.

You can use this:

$tableAIds = TableA::all()->lists("id")->toArray();
$tableBIds = TableB::all()->lists("id")->toArray();
foreach (range(1, 20) as $index) {
    repeat:
    $tableAId = $faker->randomElement($tableAIds);
    $tableBId = $faker->randomElement($tableBIds);
    try {
        DB::table("table_a_table_b_pivot")->insert([
            "table_a_id" => $tableAId,
            "table_b_id" => $tableBId,
        ]);
   } catch (\Illuminate\Database\QueryException $e) {
            //look for integrity violation exception (23000)
            if($e->errorInfo[0]==23000)
              goto repeat;
   }
}

Ps: This may not work if from the begining TableA and TableB datas are inconsistents (meaning it is not possible to form 20 uniques couples from those data )

$unique = $faker->unique()->regexify('[0-4][0-9][0-4][0-9]');

The code above will result in a string composed of two numbers between 0 and 49 that never repeats. Now you can break into two and seed your database securely.

$firstId = ltrim(substr($unique, 0, 2), '0') + 1;
$secondId = ltrim(substr($unique, 2, 2), '0') + 1;

The two numbers can be the same in a string, but a string will always be unique. Don't forget to add one for each of the parts because we don't want (and we can't) to seed our database with zeros.