Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using PHP Faker in Laravel to generate "unique with" entry when seeding a database using a factory

So Similar to the unique with validation rule (See: https://github.com/felixkiss/uniquewith-validator), I want to know how to generate a entry, where one column is unique with another one. I want to seed my database as follows.

Example:

There are 12 steps in the "steps" table. Each step should have 5 categories associated with each one that are stored in the "step_categories" table. Each of those categories are assigned a unique order number 1 through 5 that is unique with each "step_id".

See this image here for an example of what the database should look like: https://imgur.com/a/XYA5yyn

I had to manually to make the entries in the database for the above image example. I don't want to have to generate this manually every time, say I make a mistake and have to rollback the migrations for example.

I am using a factory to generate this data. So the factory name is StepCategoriesFactory.php and clearly I'm calling the factory with the create() method from the DatabaseSeeder.php file.

I thought about doing this in a for loop, then i got as far as realizing when i called the 'step_id' => App\Model::all()->random()->id to grab a new id, that I wouldn't be able to ensure I wasn't grabbing the id that i just generated 5 entries for. I'm really new with Laravel, and I'm not sure where to even start on this. There's no real information on SO where faker can use the unique with another column. How would I Go about this?

NOTE: The step id is not always going to be 1-12. The step ID might be different depending on whether a step gets deleted and remade. So just assigning the step_id to equal 1-12 wont work.

UPDATE: Here's some code I just wrote, and I think I'm on the right track. Maybe. I've grabbed the step_id by it's number field as that will always be 1-12, and I've grabbed the IID out of the entry. But now I'm stuck on how to generate the order 1-5 without repeating itself. I still haven't run this yet as its incomplete and I know it'll throw an error without the correct order number.

UPDATE 2: I think I'm on the right track here. However I'm getting an undefined variable error. When I put the first line from within the anonymous function, it's resetting the order to "1" for every entry. How do i make the $autoIncrement variable available to the anonymous function? The Seeder has stayed the same between updates.

Image of the error: https://imgur.com/a/ywkd0Lb Second image with the Die/Dump error in terminal: https://imgur.com/a/rOGRv32

Reference this article here: https://laracasts.com/discuss/channels/laravel/model-factory-increment-value-faker?page=1

UPDATE 3: I forgot the use ($autoIncrement) line of code for the anonymous function. Code below has been updated, but now I'm getting a different error saying that the order column has a null value and can't be inserted. clearly it should be '1'. Even after I call my $autoIncrement->next(); which should increment it to '1' it's still returning null according to the terminal. However, when I do a diedump on $autoIncrement->current() it's returning 1. Weird.

Update 3 error: https://imgur.com/a/STOmIjF

StepCategoriesFactory.php

use Faker\Generator as Faker;

$autoIncrement = autoIncrement();

$factory->define(App\StepCategory::class, function (Faker $faker) use ($autoIncrement) {
    // Generate Created At and Updated at DATETIME
    $DateTime = $faker->dateTime($max = 'now');
    $autoIncrement->next();
    $order = (int) $autoIncrement->current();

    return [
        // Generate Dummy Data
        'order' =>  $order,
        'name' => $faker->words(4, true),
        'created_at' => $DateTime,
        'updated_at' => $DateTime,
    ];
});

function autoIncrement()
{
    for ($i = 0; $i < 5; $i++) {
        yield $i;
    }
}

Edit: Put a bounty on this question, as I think it would be helpful for the community to get a detailed answer. I'm looking for help to explain how to go about making sure I'm grabbing the same entry through each loop.

like image 925
J. Robinson Avatar asked Jun 01 '18 03:06

J. Robinson


People also ask

How do you use faker in seeders?

$factory->define(App\User::class, function (Faker $faker) { return [ 'user_name' => $faker->name, 'user_email' => $faker->unique()->safeEmail, 'user_address' => $faker->sentence(5), 'user_phone' => '123', 'password' => 'y$TKh8H1.

What is the use of Faker in Laravel?

Faker Overview Faker generates fake or dummy data for your application and has built-in capability to generate fake data of the following types: Base. Lorem Ipsum Text. Person.

What does Laravel use for seeding?

Laravel includes the ability to seed your database with data using seed classes. All seed classes are stored in the database/seeders directory. By default, a DatabaseSeeder class is defined for you. From this class, you may use the call method to run other seed classes, allowing you to control the seeding order.

Which command to run seeding in Laravel?

Laravel Seeding Creating a Seeder To create seeders, you may use the make:seeder Artisan command. All seeders generated will be placed in the database/seeds directory. Generated seeders will contain one method: run . You may insert data into your database in this method.


2 Answers

FINALLY SOLVED!

So I took in everyone's answers, and thought long and hard about using a for loop to create the order number. 1-5. The problem that I was running into at the end was that the $i variable was not resetting. So after the yield I had to check if the $i variable equalled 5 and then reset it back to zero.

Heres the code!

StepCategories.php

use Faker\Generator as Faker;

$autoIncrement = autoIncrement();

$factory->define(App\StepCategory::class, function (Faker $faker) use ($autoIncrement) {
    // Generate Created At and Updated at DATETIME
    $DateTime = $faker->dateTime($max = 'now');

    // Get the next iteration of the autoIncrement Function
    $autoIncrement->next();
    // Assign the current $i value to a typecast variable.
    $order = (int) $autoIncrement->current();


    return [
        // Generate Dummy Data
        'order' =>  $order,
        'name' => $faker->words(4, true),
        'created_at' => $DateTime,
        'updated_at' => $DateTime,
    ];
});

function autoIncrement()
{
    // Start a loop
    for ($i = 0; $i <= 5; $i++) {
        // Yield the current value of $i
        yield $i;
        // If $i is equal to 5, that must mean the start of a new loop
        if($i == 5) {
            // Reset $i to 0 to start over.
            $i = 0;
        }
    }
}

DatabaseSeeder.php

// Generate Dummy Categories
// Run the factory 12 times
foreach(range(1, 12) as $i) {
    // Generate 5 entries each time
    factory(App\StepCategory::class, 5)->create([
        // Since all steps have a number 1-12 grab the step by the number column and get it's ID
        'step_id' => App\Step::where('number', '=', $i)->first()->id,
    ]);
}

Thanks to all who helped!

like image 149
J. Robinson Avatar answered Oct 05 '22 14:10

J. Robinson


Sorry if you don't understand my point so I'll try to explain it in code

    use Illuminate\Database\Seeder;


$factory->define(App\StepCategory::class, function (Faker $faker) {
    // Generate Created At and Updated at DATETIME
    $DateTime = $faker->dateTime($max = 'now');
    $step_id = function () {
            return factory('App\Step')->create()->id;
        };

    return [
        // Generate Dummy Data
       'step_id' => $step_id,
        'order' => uniqueOrder($step_id),
        'name' => $faker->words(4, true),
        'created_at' => $DateTime,
        'updated_at' => $DateTime,
    ];
});

function uniqueOrder($step_id)
{
    $unique = rand(1,5);
    do {
       $unique = rand(1,5);
     }
     while(StepCategory::where('step_id', $step_id)->andWhere( 'order', $unique)->exists())

  return $unique;

}
like image 35
Bader Avatar answered Oct 05 '22 15:10

Bader