2016-02-17 3 views
10

В Laravel, посев базы данных обычно осуществляется через фабрики моделей. Таким образом, вы определите план для вашей модели с использованием данных обманщика, и сказать, сколько экземпляров вам нужно:Laravel - Взаимоотношения с посевом

$factory->define(App\User::class, function (Faker\Generator $faker) { 
    return [ 
     'name' => $faker->name, 
     'email' => $faker->email, 
     'password' => bcrypt(str_random(10)), 
     'remember_token' => str_random(10), 
    ]; 
}); 

$user = factory(App\User::class, 50)->create(); 

Однако, позволяет сказать, что ваша модель пользователя имеет hasMany отношения со многими другими моделями, как в Post модель, например:

Post: 
    id 
    name 
    body 
    user_id 

Таким образом, в этой ситуации, вы хотите, чтобы семя таблицы сообщений с фактических пользователей, которые были посеяны в таблице пользователей. Это, кажется, не быть явно обсуждали, но я нашел следующее в документации Laravel:

$users = factory(App\User::class, 3) 
    ->create() 
    ->each(function($u) { 
     $u->posts()->save(factory(App\Post::class)->make()); 
    }); 

Так что в вашем User заводе, вы создаете X число сообщений для каждого пользователя вы создаете. Однако в большом приложении, где, возможно, 50 - 75 моделей разделяют отношения с пользовательской моделью, ваш пользовательский сеятель, по сути, в конечном итоге высевает всю базу данных со всеми ее отношениями.

Мой вопрос: это лучший способ справиться с этим? Единственное, что я могу придумать, это сначала Seed the Users (без посева каких-либо отношений), а затем вытащить случайных пользователей из БД по мере необходимости, пока вы высеиваете другие Модели. Однако в тех случаях, когда они должны быть уникальными, вам нужно будет отслеживать, какие пользователи были использованы. Кроме того, похоже, это добавит много дополнительных запросов к процессу посева.

ответ

4
$factory->define(App\User::class, function (Faker\Generator $faker) { 
    return [ 
     'name' => $faker->name, 
     'email' => $faker->email, 
     'password' => bcrypt(str_random(10)), 
     'remember_token' => str_random(10), 
    ]; 
}); 

$factory->define(App\Post::class, function (Faker\Generator $faker) { 
    return [ 
     'name' => $faker->name, 
     'body' => $faker->paragraph(1), 
     'user_id' => factory(App\User::class)->create()->id, 
    ]; 
}); 

Так что теперь, если вы сделаете это factory(App\Post::class, 4)->create() это создаст 4 различные должности и в этом процессе также создать 4 разных пользователей.

Если вы хотите тот же пользователь для всех сообщений, что я обычно делаю:

$user = factory(App\User::class)->create(); 
$posts = factory(App\Posts::class, 40)->create(['user_id' => $user->id]); 
+0

Спасибо за ответ. Я пробовал такие вещи. Однако, если я хочу использовать тот же набор пользователей, например, 20 других моделей, я чувствую, что мои сеялки довольно запутались. По сути, вся моя логика посева заканчивается в одном файле, потому что «Пользователь» связан с множеством других моделей. Но, возможно, нет лучшего способа. – djt

5

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

Решение может быть: один DatabaseSeeder и частные методы внутри класса, чтобы поддерживать метод «run» немного чище. У меня есть этот пример ниже, в котором есть User, Link, LinkUser (многие-ко-многим) и заметки (много-к-одному).

Для отношений «многие ко многим» я сначала создаю все ссылки и получаю вставленные идентификаторы. (поскольку идентификаторы являются auto-inc, я думаю, что идентификаторы можно было бы получить легче (получить max), но в данном примере это не имеет значения). Затем создайте пользователей и присоедините некоторые случайные ссылки каждому пользователю (многие-ко-многим). Он также создает случайные заметки для каждого пользователя (пример много-к-одному). Он использует «заводские» методы.

Если вы замените «ссылку» для своей «почты», это должно сработать. (Вы можете удалить раздел «Примечание», затем ...)

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

<?php 

use Illuminate\Database\Seeder; 

class DatabaseSeeder extends Seeder 
{ 
    /** 
    * Run the database seeds. 
    * 
    * @return void 
    */ 
    public function run() 
    { 
     // Create random links 
     factory(App\Link::class, 100)->create(); 

     // Fetch the link ids 
     $link_ids = App\Link::all('id')->pluck('id')->toArray(); 

     // Create random users 
     factory(App\User::class, 50)->create()->each(function ($user) use ($link_ids) { 

      // Example: Many-to-many relations 
      $this->attachRandomLinksToUser($user->id, $link_ids); 

      // Example: Many-to-one relations 
      $this->createNotesForUserId($user->id); 
     }); 

     // Make sure you have a user to login with (your own email, name and password) 
     $this->updateCredentialsForTestLogin('[email protected]', 'John Doe', 'my-password'); 
    } 

    /** 
    * @param $user_id 
    * @param $link_ids 
    * @return void 
    */ 
    private function attachRandomLinksToUser($user_id, $link_ids) 
    { 
     $amount = random_int(0, count($link_ids)); // The amount of links for this user 
     echo "Attach " . $amount . " link(s) to user " . $user_id . "\n"; 

     if($amount > 0) { 
      $keys = (array)array_rand($link_ids, $amount); // Random links 

      foreach($keys as $key) { 
       DB::table('link_user')->insert([ 
        'link_id' => $link_ids[$key], 
        'user_id' => $user_id, 
       ]); 
      } 
     } 
    } 

    /** 
    * @param $user_id 
    * @return void 
    */ 
    private function createNotesForUserId($user_id) 
    { 
     $amount = random_int(10, 50); 
     factory(App\Note::class, $amount)->create([ 
      'user_id' => $user_id 
     ]); 
    } 

    /** 
    * @param $email 
    * @param $name 
    * @param $password 
    * @return void 
    */ 
    private function updateCredentialsForTestLogin($email, $name, $password) 
    { 
     $user = App\User::where('email', $email)->first(); 
     if(!$user) { 
      $user = App\User::find(1); 
     } 
     $user->name = $name; 
     $user->email = $email; 
     $user->password = bcrypt($password); // Or whatever you use for password encryption 
     $user->save(); 
    } 
} 
8

Вы можете использовать saveMany также.Например:

factory(User::class, 10)->create()->each(function ($user) { 
    $user->posts()->saveMany(factory(Posts::class, 5)->make()); 
}); 
+0

Я не знал о 'saveMany()', пока не увидел этот ответ. Спасибо. :) – Vaughany

4

Вы можете сделать это с помощью затворов в ModelFactory, как обсуждалось here.

Это решение работает чисто и элегантно с сеятелями.

$factory->define(App\User::class, function (Faker\Generator $faker) { 
    return [ 
     'name' => $faker->name, 
     'email' => $faker->email, 
     'password' => bcrypt(str_random(10)), 
     'remember_token' => str_random(10), 
    ]; 
}); 

$factory->define(App\Post::class, function (Faker\Generator $faker) { 
    return [ 
     'name' => $faker->name, 
     'body' => $faker->paragraph(1), 
     'user_id' => function() { 
      return factory(App\User::class)->create()->id; 
     }, 
    ]; 
}); 

Для вашего сеялка, использовать что-то простое, как это:

//create 10 users 
factory(User::class, 10)->create()->each(function ($user) { 
    //create 5 posts for each user 
    factory(Post::class, 5)->create(['user_id'=>$user->id]); 
}); 

ПРИМЕЧАНИЯ: Этот метод не создает ненужные записи в базе данных, а пройденные атрибуты назначаются до создания соответствующих записей.

Смежные вопросы