Use faker in laravel when inserting thousand of records - php

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

How to pass arguments from seeders to factories?

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.

laravel seeder is giving an error and won't seed

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

Error in using PHP fakers

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());
});
}
}

Laravel fill database table column from array using factory

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']
]
)
])
];
});

lumen generate fake data with faker from given array

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.

Categories