Задача: необходимо было сгенерировать данных для таблиц people, articles и comments.
При создании моделей использовал следующие команды:
php artisan make:model People -mf php artisan make:model Article -mf php artisan make:model Comment -mf
параметр -f отвечает за factory, которые я хотел использовать.
Создались 3 модели, в которые добавил методы для создания связей:
/app/People.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class People extends Model
{
public function articles()
{
return $this->hasMany(Article::class, 'author_id', 'id');
}
}
/app/Article.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
public function people()
{
return $this->belongsTo(People::class, 'author_id', 'id');
}
}
/app/Comment.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
public function article()
{
return $this->belongsTo(Article::class, 'article_id', 'id');
}
public function people()
{
return $this->belongsTo(People::class, 'author_id', 'id');
}
}
factory создались по адресу(их я уже немного дополнил):
/database/factories/ArticleFactory.php
use Faker\Generator as Faker;
$factory->define(App\Article::class, function (Faker $faker) {
return [
'title' => $faker->name,
];
});
/database/factories/PeopleFactory.php
use Faker\Generator as Faker;
$factory->define(App\People::class, function (Faker $faker) {
return [
'first_name' => $faker->name,
'last_name' => $faker->lastName,
'twitter' => '@' . $faker->firstName,
];
});
/database/factories/CommentFactory.php
use Faker\Generator as Faker;
$factory->define(App\Comment::class, function (Faker $faker) {
return [
'body' => $faker->text(100),
];
});
Следующим шагом генерировал классы начальных данных:
php artisan make:seeder PeopleTableSeeder php artisan make:seeder ArticleTableSeeder php artisan make:seeder CommentTableSeeder
Дальше в /database/seeds/DatabaseSeeder.php в методе run объявил вызов созданных классов
public function run()
{
$this->call(PeopleTableSeeder::class);
$this->call(ArticleTableSeeder::class);
$this->call(CommentTableSeeder::class);
}
Прочитал документацию получилось следующее:
database/seeds/PeopleTableSeeder.php
use Illuminate\Database\Seeder;
class PeopleTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(App\People::class, 10)->create();
}
}
database/seeds/ArticleTableSeeder.php
use Illuminate\Database\Seeder;
class ArticleTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(App\Article::class, 3)->make()->each(function ($article) {
//take random people
$people = App\People::orderByRaw('RAND()')->first();
$article->people()->associate($people)->save();
});
}
}
database/seeds/CommentTableSeeder.php
use Illuminate\Database\Seeder;
class CommentTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(App\Comment::class, 1)->make()->each(function ($comment) {
$people = App\People::orderByRaw('RAND()')->first();
$article = App\Article::orderByRaw('RAND()')->first();
$comment->people()->associate($people);
$comment->article()->associate($article);
$comment->save();
});
}
}
Для того, чтобы использовать уже существующие сущности для привязки, использовал следующую конструкцию
App\People::orderByRaw('RAND()')->first();
P.S. Как мне подсказали позднее: можно было часть логики генерации упростить и вставить в factory, например factory Article:
<?php
use Faker\Generator as Faker;
$factory->define(App\Article::class, function (Faker $faker) {
return [
'author_id' => function () {
return factory(App\People::class)->create()->id;
},
'title' => $faker->sentence($nbWords = 6, $variableNbWords = true),
];
});
Подводные камни
1. В процессе создания столкнулся с такой проблемой из-за недопонимания связи.
Изначально, в классе ArticleTableSeeder, чтобы привязать people к article, я пытался сделать так
$a->people()->save(factory(App\People::class)->create());
И получал ошибку:
BadMethodCallException : Method Illuminate\Database\Query\Builder::save does not exist.
Оказалось, что так сохранять можно, если в методе people() связь hasMany, hasOne, в моем случае там была реализована связь belongsTo.
Это связь дочернего элемента к родителю и чтобы сохранить, необходимо выполнить ассоциацию с родителем и сохранить:
$people = App\People::orderByRaw('RAND()')->first();
$a->people()->associate($people)->save();
2. Важно понимать разницу между методами make() и create() в FactoryBuilder
factory(App\Comment::class)->make() — создает экземпляр App\Comment, но не сохраняем в БД
factory(App\Comment::class)->create() — создает экземпляр App\Comment и сохраняет в БД
из-за не понимания разницы, не мог понять, почему получаю ошибку вида:
Illuminate\Database\QueryException : SQLSTATE[HY000]: General error: 1364 Field ‘author_id’ doesn’t have a default value (SQL: insert into `articles` (`title`, `updated_at`, `created_at`) values (Ilene Kiehn, 2018-08-03 10:01:56, 2018-08-03 10:01:56))
Полезный материал:
https://laracasts.com/discuss/channels/eloquent/save-on-polymorphic-relation-dont-work
https://laravel.com/docs/5.6/seeding
Оставить комментарий