Предотвратить раздачу уникальных ключевых нарушений

У меня есть уникальное ограничение в моей базе данных для двух столбцов.

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

Теперь я использую Faker в классе сеялки, чтобы заполнить эту сводную таблицу:

$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
    ]);
}

Однако это создает дубликаты, и заполнение не выполняется из-за ошибки SQL.

Как убедиться, что я не пытаюсь вставить дубликаты?


person MikkoP    schedule 12.08.2015    source источник


Ответы (3)


Вы можете использовать это:

$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: это может не сработать, если с самого начала данные TableA и TableB несовместимы (это означает, что из этих данных невозможно сформировать 20 уникальных пар)

person Foris    schedule 27.11.2016

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

Приведенный выше код приведет к строке, состоящей из двух чисел от 0 до 49, которая никогда не повторяется. Теперь вы можете разбить базу данных на две части и безопасно заполнить ее.

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

Два числа могут быть одинаковыми в строке, но строка всегда будет уникальной. Не забудьте добавить по одному для каждой из частей, потому что мы не хотим (и не можем) заполнять нашу базу данных нулями.

person Rafael Berro    schedule 18.08.2017

Вместо использования faker используйте метод коллекции random:

$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],
    ]);
}

Отрегулируйте это в соответствии с вашими потребностями.

Например, это может быть то, что вы действительно хотите:

$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,
        ]);
    }
}

Что бы это ни было, поиграйте с этим сами. Тут не надо фейка.

person Joseph Silber    schedule 12.08.2015
comment
Ясно, что оператор хочет использовать подделку... Есть и другие способы обойти это. - person AndrewMcLagan; 27.06.2016