I have one issue with laravel faker, I was looking for a tutorial to insert thousands of records using seeders
This was my PostSeeder.php:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Post;
use App\Models\User;
class PostSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
Post::factory(10)->create();
}
}
Here I was inserting 10 posts, but I need to test thousands or millions of records, so I saw a tutorial and modified the seeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Str;
class PostSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$users= collect(User::all()->modelKeys());
$data = [];
for ($i = 0; $i < 100000; $i++) {
$data[] = [
'body' => Str::random(50),
'image' => 'https://via.placeholder.com/640x480.png/0077dd?text=inventore',
'user_id' => $users->random(),
'created_at' => now()->toDateTimeString(),
'updated_at' => now()->toDateTimeString(),
];
}
$chunks = array_chunk($data, 10000);
foreach ($chunks as $chunk) {
Post::insert($chunk);
}
}
}
With this approach I can insert thousand of records faster, but the problem is that I am not inserting correctly the body and image field
I wanted to try something with faker, in my factory I have this:
PostFactory.php
<?php
namespace Database\Factories;
use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Post::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'body' => $this->faker->text,
'image' => $this->faker->imageUrl(),
'user_id' => function() {
return User::factory()->create()->id;
}
];
}
}
I would like to use faker methods like these ones in the PostSeeder but I can't, what can I do? thank you.
EDIT:
I tried this:
public function run(Faker $faker)
{
$users= collect(User::all()->modelKeys());
$data = [];
for ($i = 0; $i < 50000; $i++) {
$data[] = [
'content' => $faker->text,
'image_path' => $faker->imageUrl(),
'user_id' => $users->random(),
'created_at' => now()->toDateTimeString(),
'updated_at' => now()->toDateTimeString(),
];
}
$chunks = array_chunk($data, 5000);
foreach ($chunks as $chunk) {
Post::insert($chunk);
}
}
And I got this message:
PDOException::("SQLSTATE[HY000]: General error: 2006 MySQL server has gone away")
But when I try with fewer records it works, so, I changed the seeder like this:
$users= collect(User::all()->modelKeys());
$posts = Post::factory(10)->create();
$posts = collect($posts->only(['content','image_path']));
...
...
'content' => $posts->random()->content,
'image_path' => $posts->random()->image_path
...
and this doesn't work, it got this error:
You requested 1 items, but there are only 0 items available.
It looks like $posts->only(['content','image_path']) is not working properly. So I tried this:
Post::factory(10)->create();
$tweets = Tweet::select(['content','image_path'])->get();
...
'content' => $posts->random()->content,
'image_path' => $posts->random()->image_path
...
And again it works with a few records, but when I try with thousands, I get this error again:
PDOException::("SQLSTATE[HY000]: General error: 2006 MySQL server has gone away")
What can I do? thank you
Since model factories create in-memory objects, because of big memory usages it is not suitable for large seeds.
But you can use Faker for data generation:
use Faker\Generator as Faker;
class PostSeeder extends Seeder
{
public function run(Faker $faker)
{
$users= collect(User::all()->modelKeys());
$data = [];
for ($i = 0; $i < 100000; $i++) {
$data[] = [
'body' => $faker->text,
'image' => $faker->imageUrl(),
'user_id' => $users->random(),
'created_at' => now()->toDateTimeString(),
'updated_at' => now()->toDateTimeString(),
];
}
$chunks = array_chunk($data, 10000);
foreach ($chunks as $chunk) {
Post::insert($chunk);
}
}
}
Related
I want to pass arguments ['site_id' => $site->id] to SiteMessage factory:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\SiteMessage;
use App\Models\Site;
class SitesMessagesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
Site::chunk(200, function ($sites) {
foreach ($sites as $site) {
SiteMessage::factory()->count(rand(2, 6))->create(['site_id' => $site->id]);
}
});
}
}
How can I get those argument in my SiteMessage factory class?
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\SiteMessage;
use App\Models\Site;
use App\Models\Integration;
class SiteMessageFactory extends Factory
{
protected $model = SiteMessage::class;
public function definition()
{
return [
**// Soliution: remove line below, it will be overridden automaticaly. \\**
'site_id' => $arguments['site_id'], // Neet to use Id that I passed from seeder.
'integration_id'=> Integration::inRandomOrder()->first()->id,
'type' => rand(0,1) ? 'EMAIL' : 'SMS',
'title' => $this->faker->text($maxNbChars = 12),
'description' => $this->faker->sentence,
'message' => $this->faker->sentence,
'enabled' => 1,
'created_at' => now(),
'updated_at' => now(),
];
}
}
At older Laravel factory version I could pass them in callback like so:
$factory->define(SiteMessage::class, function (Faker $faker, array $arguments = []) {
//
});
but don't know how to achieve it with new Class factories. Any help would be very appreciated :)
As you can see in the laravel documentation about persisting models with factories, when you type:
SiteMessage::factory()->count(rand(2, 6))->create(['site_id' => $site->id]);
The site_id attribute from SiteMessage factory will be overrided by the $site->id you are specifying.
I've been trying to get my seeder to work but it keeps giving me the following error
Call to undefined function Database\Seeders\factory()
at database/seeders/ContactTableSeeder.php:16
12▕ * #return void
13▕ */
14▕ public function run()
15▕ {
➜ 16▕ factory('App\Models\Contact', 100)->create()
17▕ ->each(function($contact) {
18▕ $contact->addresses()->save(
19▕ (factory(App\Address::class)->make())
20▕ );
+24 vendor frames
25 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
My DatabaseSeeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* #return void
*/
public function run()
{
//Model::unguard(); // Disable mass assignment
$this->call(ContactTableSeeder::class);
//Model::reguard();
}
}
My ContactTableSeeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class ContactTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory('App\Models\Contact', 100)->create()
->each(function($contact) {
$contact->addresses()->save(
(factory(App\Address::class)->make())
);
});
}
}
My ContactFactory
<?php
/* #var $factory \Illuminate\Database\Eloquent\Factory */
use App\Models\Contact;
use Faker\Generator as Faker;
$factory->define(Contact::class, function (Faker $faker) {
return [
'firstName' => $faker->firstName,
'lastName' => $faker->lastName,
'email' => $faker->unique()->email,
'phone' => $faker->phoneNumber,
'birthday' => $faker->date($format = 'Y-m-d', $max = 'now')
];
});
My AddressFactory
<?php
/* #var $factory \Illuminate\Database\Eloquent\Factory */
use App\Models\Address;
use Faker\Generator as Faker;
$factory->define(Address::class, function (Faker $faker) {
return [
'number' => $faker->number,
'street' => $faker->streetName,
'city' => $faker->city,
'state' => $faker->state,
'zip' => $faker->postcode,
'type' => 'home',
'contact_id'=> factory(App\Models\Contact::class),
];
});
Contact Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
use HasFactory;
protected $fillable = [ 'firstName', 'lastName', 'email', 'phone', 'birthday' ];
public function addresses()
{
return $this->hasMany('App\Models\Address');
}
}
My Address Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Address extends Model
{
use HasFactory;
protected $fillable = [ 'id', 'number', 'street', 'city', 'state', 'zip', 'type', 'contact_id' ];
public function contacts()
{
return $this->belongsTo('App\Models\Contact');
}
}
I have tried running
composer dump-auto
composer update
None of those normal fixes seem to work.
I really have no idea why its failing
Thanks in advance for your help
In Laravel8, you need to update your factory files like below or add laravel/legacy-factories package to your project. Further info: https://laravel.com/docs/8.x/upgrade#model-factories
<?php
namespace Database\Factories;
use App\Models\Contact;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Contact::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'firstName' => $this->faker->firstName,
'lastName' => $this->faker->lastName,
'email' => $this->faker->unique()->email,
'phone' => $this->faker->phoneNumber,
'birthday' => $this->faker->date($format = 'Y-m-d', $max = 'now')
];
}
}
In seeders use factory like that:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class ContactTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
App\Models\Contact::factory()->create(100)
->each(function($contact) {
$contact->addresses()->save(
App\Address::factory()->make()
);
});
}
}
Replace your factory function factory('App\Models\Contact', 100)->create()
with this code:
\App\Models\Contact::factory()->count(100)
->create();
Why? Because In laravel 8 the default route namespace was removed
In laravel 8 the default route namespace was removed.
Try to change
factory(App\Models\Contact::class,100)->create();
To
\App\Models\Contact::factory()->create();
\App\Models\Contact::factory(100)->create(); \\If you want to create 100 number of record then
I am trying to seed data as provided in this link https://github.com/fzaninotto/Faker. The tables are filled but but I got this error:
$ php artisan db:seed
Seeding: gamesSeeder
Method Illuminate\Database\Query\Builder::issues does not exist.
I am running Lumen (5.6.3) (Laravel Components 5.6.*).
<?php
$factory->define(App\teams::class, function (Faker\Generator $faker) {
return [
'name' => $faker-> word,
];
});
$factory->define(App\games::class, function (Faker\Generator $faker) {
return [
'Team 1' => $faker-> word,
'Team 2' => $faker-> word,
'Score 1' => $faker->numberBetween($min = 0, $max = 10),
'Score 2' => $faker->numberBetween($min = 0, $max = 10),
'Game Date' => $faker->date($format = 'Y-m-d', $max = 'now'),
'Viewers' => $faker-> numberBetween($min = 0, $max = 100000),
];
});
Here are my seeders files.
teamsSeeder:
<?php
use Illuminate\Database\Seeder;
class gamesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory(App\games::class, 30)->create()->each(function($u) {
$u->issues()->save(factory(App\games::class)->make());
});
}
}
and here is gamesSeeder
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use App\teams;
class teamsSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory(App\teams::class, 5)->create()->each(function($u) {
$u->issues()->save(factory(App\teams::class)->make());
});
}
}
I have a database table look like this
|id |col2|col3|col4|
-------------------
| 1 |elm0|....|....|
| 2 |elm1|....|....|
| 3 |elm2|....|....|
|...|....|....|....|
|N+1|elmN|....|....|
I want to fill the col2 with data from array (not in random way).
Example array:
$dataArray = array(elm0, elm1, elm2,...)
I created this factory:
<?php
use Faker\Generator as Faker;
$factory->define(App\Unit::class, function (Faker $faker) {
$dataArray = array(elm0, elm1, elm2,...,elmN);
return [
'col2' => $dataArray[$index];
'col3' => $faker->'whatever';
'col4' => $faker->'whatever';
];
});
How I can do this?
you can build like that:
<?php
use Faker\Generator as Faker;
$factory->define(App\Unit::class, function(Faker $faker) {
$data = array(elm0, elm1, elm2,...,elmN);
foreach($data as $kye=>$value) {
$result['id'] = $key;
$result['col2'] = $value;
$result['col3'] = $faker->'whatever';
$result['col4'] = $faker->'whatever';
}
return $result;
});
When you need to run an array, often foreach() solve your problem.
Hope that help you.
Cheers.
Had a similar problem and decided to skip the factory part and use only seeder. I got to the solution while reading this: Seed multiple rows at once laravel 5 answer by lukasgeiter.
First you make a seeder with:php artisan make:seeder UnitsTableSeeder
Then you will have in your seeder something like this:
<?php
use Faker\Generator as Faker;
use Illuminate\Database\Seeder;
class UnitsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$faker = new Faker;
$data = ['elm0', 'elm1', 'elm2',...,'elmN'];
$rows = [];
foreach ($data as $element) {
$rows[] = ['col2' => $element, 'col3' => $faker->'whatever', 'col4' => $faker->'whatever'];
}
App\Unit::insert($rows);
}
}
After this you can seed your Units table like a sir :)
php artisan db:seed --class=UnitsTableSeeder
<?php
use Faker\Generator as Faker;
$factory->define(App\Unit::class, function(Faker $faker) {
// Grab a random unit
$unit = App\Unit::orderByRaw('RAND()')->first();
// Or create a new unit
$unit = factory(App\Unit::class)->create();
return [
'id' => $unit->id,
'col2' => $faker->'whatever',
'col3' => $faker->'whatever',
'col4' => $faker->'whatever',
];
});
Please check if it is work for you.
If you want to get a random element from an array and fake it then you can do something as below:
$factory->define(Rule::class, function (Faker $faker) {
$data = [
'age' => [
'borrower_age_min' => 'min:21',
'borrower_age_max' => 'max:75'
],
'affordability' => [
'annual_income' => 'integer|min:40000',
'loan_amount' => 'integer|max:3*',
],
'finance' => [
'loan_length' => 'integer|max:12',
'loan_amount' => 'integer|max:500000',
]
];
return [
'rule' => json_encode([
$faker->randomElement(
[
$data['age']['borrower_age_min'],
$data['age']['borrower_age_max'],
$data['affordability']['annual_income'],
$data['affordability']['loan_amount'],
$data['finance']['loan_length'],
$data['finance']['loan_amount']
]
)
])
];
});
I want to generate fake data for user with diffrent role ,
My file is below,
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Faker\Factory as Faker;
class InsertUserSeeder extends Seeder {
/**
* Run the database seeds.
*
* #return void
*/
public function run() {
$faker = Faker::create();
foreach (range(1, 10) as $index) {
DB::table('user')->insert([
'name' => $faker->name,
'email' => $faker->email,
'role' => 'admin',
]);
}
}
}
From above code it will generate 10 records of admin role.
I want to generate records with different role from['admin','superadmin','client','agent','engineer']
how can I fetch value for role from given array of roles.
You can use the method randomElement($array) of the faker to do the job like so:
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Faker\Factory as Faker;
class InsertUserSeeder extends Seeder {
/**
* Run the database seeds.
*
* #return void
*/
public function run() {
$faker = Faker::create();
foreach (range(1, 10) as $index) {
DB::table('user')->insert([
'name' => $faker->name,
'email' => $faker->email,
'role' => $faker->randomElement([
'admin',
'superadmin',
'client',
'agent',
'engineer',
]),
]);
}
}
}
For reusability, I would suggest using model factories instead of creating the models directly in the seeder. You can read up on it in the documentation.