2016-02-17 92 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()); 
    }); 

所以在您的用戶工廠,你爲你創建的每個用戶創建帖子的X號。然而,在一個可能有50-75個模型與用戶模型共享關係的大型應用程序中,您的用戶播種器實質上會最終爲整個數據庫播種所有它的關係。

我的問題是:這是處理這個問題的最好方法嗎?我唯一能想到的其他事情就是先播種用戶(不播種任何關係),然後在播種其他模型時根據需要從數據庫中提取隨機用戶。但是,如果他們需要獨一無二,則必須跟蹤哪些用戶已被使用。此外,似乎這會在播種過程中增加大量額外的查詢批量。

回答

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和類中的私有方法,以保持'運行'方法有點乾淨。我在下面有這個例子,它有一個User,Link,LinkUser(多對多)和一個Note(多對一)。

對於多對多關係,我首先創建所有鏈接,並獲取插入的ID。 (因爲id是auto-inc,我認爲id可以更容易獲取(get 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]); 
}); 

注意:此方法不會在數據庫中創建不需要的項,而不是通過屬性分配之前創建的相關記錄。