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']
]
)
])
];
});
Related
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);
}
}
}
I have the factory below. When I set it to create more than one model, the ID does not seem to increment. Please help me understand how I can make this code work so the ID increments each time.
<?php
/* #var $factory \Illuminate\Database\Eloquent\Factory */
use App\PurchaseOrder;
use Faker\Generator as Faker;
use Illuminate\Support\Facades\DB;
$factory->define(PurchaseOrder::class, function (Faker $faker) {
DB::select(DB::raw('SET FOREIGN_KEY_CHECKS=0'));
$numberOfLines = rand(1, 5);
$id = PurchaseOrder::all()->last()->id + 1?? 1;
for ($i = 0; $i < $numberOfLines; $i++) {
$line = factory(App\Line::class)->make([
'purchase_order_id' => $id,
]);
$line->save();
};
$lines = App\Line::where('purchase_order_id', $id)->get();
DB::select(DB::raw('SET FOREIGN_KEY_CHECKS=1'));
return [
'total_price_ex_vat' => $lines->sum('price_ex_vat'),
'total_price_inc_vat' => $lines->sum('price_inc_vat'),
'revised_total_price_ex_vat' => $lines->sum('revised_price_ex_vat'),
'revised_total_price_inc_vat' => $lines->sum('revised_price_inc_vat'),
'deliver_to' => $faker->words(2, true),
'requested_staff_id' => factory(App\Staff::class)->create()->id ,
'auth_staff_id' => Null,
'supplier_id' => factory(App\Supplier::class)->create()->id,
'rejection_code_id' => Null,
'status_id' => 1,
'status_changed_date' => date('Y-m-d'),
'user_id' => factory(App\User::class)->create()->id,
];
});
You have table PurchaseOrder dependent on Lines, and Lines dependent on PurchaseOrder. I believe you should review your db design then, as this seems quite weird.
Why don't you avoid to use Lines in PurchaseOrder and load it as an attribute inside the PurchaseOrder model with an accessor?
For more info: https://laravel.com/docs/5.8/eloquent-mutators#defining-an-accessor
Am using faker in laravel and in my seeder i have
public function run()
{
$faker = new Faker\Generator();
//create a user
$adminuser = App\User::create(
[
'name' => 'admin',
'first_name' => 'firstuser',
'profile_pic'=>$faker->image('storage/app/public/users',400,300, 'people', false),
]
);
But now am getting an error
Unknown formatter "image"
Where am i going wrong
I just want to add an image to a user generated via faker
From the docs on basic usage
public function run()
{
// instead of using new Faker\Generator()
$faker = Faker\Factory::create();
//create a user
$adminuser = App\User::create(
[
'name' => 'admin',
'first_name' => 'firstuser',
'profile_pic' => $faker->image('storage/app/public/users',400,300, 'people', false),
]
);
}
You must use the Method Injection
for example :
<?php
use Illuminate\Database\Seeder;
use Faker\Generator as Faker;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* #param Faker $faker
* #return void
*/
public function run(Faker $faker)
{
$adminuser = App\User::create(
[
'name' => $faker->name,
'first_name' => $faker->firstName,
'profile_pic' => $faker->image(public_path('img'),400,300, 'people', true),
]
);
}
}
How can I seed multiple rows using ModelFactory in Laravel?
Inside ModelFactory.php I have the following code:
$factory->define(App\User::class, function (Faker $faker) {
static $password;
return [
'name' => 'Admin',
'Description' => 'Administrators have full access to everything.'
];
});
How can I add the following arrays, without using raw expressions?
[
'name' => 'Admin',
'description' => 'Administrators have full access to everything.',
],
[
'name' => 'User',
'description' => 'User have normal access.',
],
Thanks
You can use sequence()
User::factory()->count(2)->sequence(['name' => 'admin'],['name' => 'user'])
->create()
example from laravel documentation
$users = User::factory()
->count(10)
->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index])
->create();
source https://laravel.com/docs/8.x/database-testing#sequences
Let's say you want to add 100 users in your database.
Create a UserFactory.php in database/factories:
<?php
use Faker\Generator as Faker;
$factory->define(App\User::class, function (Faker $faker) {
return [
'name' => 'Admin',
'Description' => 'Administrators have full access to everything.'
];
});
Then, in database/seeds/DatabaseSeeder.php:
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory(App\User::class, 100)->create();
}
}
You can find more details about seeding in the Laravel official documentation.
If you want to have one or two columns to have custom data, you can use each function after creating entries.
$names = ['admin', 'user', 'author', 'subscriber'];
factory(App\User::class, 100)->create()->each(function () use ($names) {
$user->name = $names[array_rand($names)];
$user->save();
});
*Note: Use your own logic inside each function to feed the custom data.
A cleaner way than Raghavendra's proposal (creates one entry per name):
$names = ['admin', 'user', 'author', 'subscriber'];
collect($names)->each(function($name) {
App\Models\User::factory()->create($name);
});
I am using the faker class to help seeder my database. The DatabaseSeeder looks like this
<?php
class DatabaseSeeder extends Seeder
{
public function run()
{
Eloquent::unguard();
$tables = [
'users',
'posts',
];
foreach ($tables as $table) {
DB::table($table)->truncate();
}
$this->call('UsersTableSeeder');
$this->call('PostsTableSeeder');
}
}
and the UsersTableSeeder
<?php
class UsersTableSeeder extends Seeder {
public function run()
{
$faker = Faker\Factory::create();
for( $i=0 ; $i<50 ; $i++ ) {
$user = User::create([
'first_name' => $faker->firstName,
'surname' => $faker->lastName,
'email' => $faker->email,
'username' => $faker->userName,
'bio' => $faker->sentences,
'bio_html' => $faker->sentences,
'wesbite' => $faker->url,
'twitter' => $faker->word,
]);
}
}
}
I am getting the following error in the terminal when I try and seed this table.
[Illuminate\Database\Eloquent\MassAssignmentException]
first_name
If I try and seed both I get this
[ErrorException]
preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
I thought including Eloquent::unguard(); stopped this error? I am running the latest version of Laravel.
faker->sentences() and faker->paragraphs() return arrays and your class expects to receive a string.
You can either use faker->text() or you can you can use
implode(" ",$faker->sentences());
Well, i think you just need to do simple in your DatabaseSeeder.php, like this:
public function run()
{
Eloquent::unguard();
$this->call('UsersTableSeeder');
$this->call('PostsTableSeeder');
}
Your UsersTableSeeder.php, like these:
<?php
use Faker\Factory as Faker;
class UsersTableSeeder extends Seeder {
public function run()
{
$faker = Faker::create();
for( $i=0 ; $i<50 ; $i++ ) {
$user = User::create([
'first_name' => $faker->firstName, // try using str_random(10)
'surname' => $faker->lastName, // try using str_random(20)
'email' => $faker->email,
'username' => $faker->userName, // $faker->unique()->userName
'bio' => $faker->sentences,
'bio_html' => $faker->sentences,
'wesbite' => $faker->url,
'twitter' => $faker->word,
]);
}
}
}
And, in your model, User.php, add:
protected $guarded = [];
I executed here and both worked:
php artisan db:seed
php artisan db:seed --class=UsersTableSeeder
I configured laravel and faker, on composer, like these:
"require": {
"laravel/framework": "4.2.*",
"fzaninotto/faker": "dev-master"
},
Hope it help you.
You can define empty array of guarded fields in your model;
class User extends Eloquent
{
protected $guarded = [];
}