Задача: необходимо было сгенерировать данных для таблиц 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
Оставить комментарий