2017-04-04 14 views
1
で複数のユニークの列を播種

はじめLaravel:私は名前のモデルを持っている</p> <p><strong>背景</strong></p> <p>:偽物

何アップの人々、私は、モデル工場と複数の独特の列についての質問を得ました画像。このモデルは、言語サポートが別のモデルImageTextに格納されています。 ImageTextは、image_idカラム、言語カラム、テキストカラムを持ちます。

ImageTextは組み合わせimage_idはと言語はユニークである必要がありにおける制約のMySQLを持っています。

class CreateImageTextsTable extends Migration 
{ 

    public function up() 
    { 
     Schema::create('image_texts', function ($table) { 

      ... 

      $table->unique(['image_id', 'language']); 

      ... 

     }); 
    } 

    ... 

は今、私は播種が行われた後、いくつかのImageTextモデルを持っている各画像をしたいです。これは、モデル工場とこのシーダと簡単です:この使用したモデル工場と偽物を播種する場合ただし、あなたが頻繁にこのメッセージが残され

factory(App\Models\Image::class, 100)->create()->each(function ($image) { 
    $max = rand(0, 10); 
    for ($i = 0; $i < $max; $i++) { 
     $image->imageTexts()->save(factory(App\Models\ImageText::class)->create()); 
    } 
}); 

問題

[PDOException]                             
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '76-gn' for key 'image_texts_image_id_language_unique' 

これは、ある時点ではforループの中で、fakerは['image_id'、 'language']の一意性制約を破って、同じlanguageCodeを画像に対して2回ランダム化するからです。

$factory->define(App\Models\ImageText::class, function (Faker\Generator $faker) { 

    return [ 
     'language' => $faker->unique()->languageCode, 
     'title' => $faker->word, 
     'text' => $faker->text, 
    ]; 
}); 

をしかし、その後、あなたの代わりに十分なimageTextsが作成された後に偽物がlanguageCodesを使い果たすという問題を得る:

あなたはこれを言うためにあなたのImageTextFactoryを更新することができます。

現在のソリューション

これは現在つlanguageCodesの一意のカウンタをリセットし、シーダーがするのループに入る前に、TE固有のカウンタをリセット工場を呼び出すImageText、2つの異なる工場を有することによって解決されます。さらにImageTextを作成します。しかし、これはコードの重複であり、これを解決するより良い方法があるはずです。

質問

あなたは工場出荷時に上の保存されているモデルを送信する方法はありますか?もしそうなら、現在のImageに既にImageTextが添付されているかどうかを確認するために工場内のチェックをすることができます。そうでない場合は、languageCodesの一意のカウンタをリセットします。

$factory->define(App\Models\ImageText::class, function (Faker\Generator $faker) { 

    $firstImageText = empty($image->imageTexts()); 

    return [ 
     'language' => $faker->unique($firstImageText)->languageCode, 
     'title' => $faker->word, 
     'text' => $faker->text, 
    ]; 
}); 

もちろん、現在提供します:

[ErrorException]   
Undefined variable: image 

それが何らかの形でこれを達成することが可能である私の目標は、このようなものになるだろうか?

答えて

1

私はそれを

を解決し、私はこの問題の解決のために多くのことを検索し、他の多くもそれを経験したことがわかりました。リレーションのもう一方の端に1つの要素だけが必要な場合は、it's very straight forward

「マルチカラムユニーク制約」の追加は、これを複雑にしました。私が見つけた唯一の解決策は、「MySQLの制限を忘れて、PDOの例外の試行でファクトリの作成を囲むこと」でした。これは、他のPDOExceptionsもキャッチされるので、悪い解決策のように感じました。私はImageTableSeederとImageTextTableSeederにシーダーを分け、そして、彼らは両方とも非常にまっすぐ進むされているこの作品を作るために

ソリューション

。彼らの実行するコマンドは次のようになり、両方:

public function run() 
{ 
    factory(App\Models\ImageText::class, 100)->create(); 
} 

魔法がImageTextFactoryの内部で起こる:

$factory->define(App\Models\ImageText::class, function (Faker\Generator $faker) { 

    // Pick an image to attach to 
    $image = App\Models\Image::inRandomOrder()->first(); 
    $image instanceof App\Models\Image ? $imageId = $image->id : $imageId = null; 

    // Generate unique imageId-languageCode combination 
    $imageIdAndLanguageCode = $faker->unique()->regexify("/^$imageId-[a-z]{2}"); 
    $languageCode = explode('-', $imageIdAndLanguageCode)[1]; 

    return [ 
     'image_id' => $imageId, 
     'language' => $languageCode, 
     'title' => $faker->word, 
     'text' => $faker->text, 
    ]; 
}); 

これは、それは次のようになります。

$imageIdAndLanguageCode = $faker->unique()->regexify("/^$imageId-[a-z]{2}"); 

我々はregexify-式でIMAGEIDを使用し、この場合は「 - 」文字で区切られた独自の組み合わせにも含まれているものを追加します。これは、 "841-en"、 "58-bz"、 "96-xx"などのような結果を生成します。imageIdは常にデータベース内の実イメージです。

imageIdと一緒に言語コードに固有のタグを貼り付けるので、はimage_idとlanguageCodeの組み合わせが一意であることがわかります。これはまさに私たちが必要とするものです!

は、今、私たちは単純にして、作成された言語コード、または何でも私達は発生したかった他の一意のフィールドを抽出することができます。

  • キャッチする必要はありません:

    $languageCode = explode('-', $imageIdAndLanguageCode)[1]; 
    

    このアプローチに次のような利点があります例外

  • 可読性のために工場とシードを分離することができます。
  • コードはコンパクトです

ここでの欠点は、キーの1つが正規表現として表現できるキーの組み合わせのみを生成できることです。これが可能な限り、これはこの問題を解決するための良いアプローチのようです。

0

あなたのソリューションは、組み合わせとして再正規化できるものに対してのみ動作します。複数の別々のFaker生成数/文字列/その他のオブジェクトの組み合わせが一意である必要があり、再正規化できない多くのユースケースがあります。

このような場合のために、あなたがそうのような何かを行うことができます:

$factory->define(App\Models\ImageText::class, function (Faker\Generator $faker) { 

    static $combos; 
    $combos = $combos ?: []; 

    // Pick an image to attach to 
    $image = App\Models\Image::inRandomOrder()->first(); 
    $image instanceof App\Models\Image ? $imageId = $image->id : $imageId = null; 

    // Generate unique imageId-languageCode combination 
    while($languageCode = $faker->languageCode && in_array([$imageId, $languageCode], $combos) {} 
    $combos[] = [$imageId, $languageCode]; 

    return [ 
     'image_id' => $imageId, 
     'language' => $languageCode, 
     'title' => $faker->word, 
     'text' => $faker->text, 
    ]; 
}); 
:あなたの具体的な質問/ユースケースについては

$factory->define(App\Models\YourModel::class, function (Faker\Generator $faker) { 
    static $combos; 
    $combos = $combos ?: []; 
    $faker1 = $faker->something(); 
    while($faker2 = $faker->somethingElse() && in_array([$faker1, $faker2], $combos) {} 
    $combos[] = [$faker1, $faker2]; 
    return ['field1' => $faker1, 'field2' => $faker2]; 
}); 

、ここで同じライン上の解決策です

関連する問題

 関連する問題