grazdev
grazdev

Reputation: 1272

In Laravel, how do you create multiple factory models with different 'created_at' values?

I have a User model and a Car model, and a User can have many Cars, and the cars() relationship should return the user's cars from the most recently created. Here's the test I've written for this relationship:

public function test_user_has_many_cars_relation_returns_cars_from_the_most_recent() {

   $user = User::factory()->create(); 
   $lessRecentCar = Car::factory()->for( $user )->create(); 
   sleep(1); 
   $mostRecentCar = Car::factory()->for( $user )->create(); 

   $hasManyRelation = $user->cars(); 

   $this->assertHasManyRelation( $hasManyRelation, Car::class, 2 );
   $this->assertIsSameModel( $mostRecentCar, $hasManyRelation->first() );

}

This works, however I had to use sleep(1) to create two cars with a different created_at value, because unfortunately using Car::factory()->for( $user )->count(10)->create() (which is a lot nicer) assigns the same value for created_at to all models.

Is there a better workaround than using sleep(1)? Hopefully something that doesn't imply manually assigning a different created_at value for each model one wants to create?

Upvotes: 1

Views: 2673

Answers (2)

M Nadeem Asghar
M Nadeem Asghar

Reputation: 13

Yes, there is a better workaround than using sleep(1) or manually assigning different created_at values. You can override the created_at value in the factory's definition using the afterMaking and afterCreating callbacks. Here's an example:

use Carbon\Carbon;

$factory->define(Car::class, function (Faker $faker) {
    return [
        'user_id' => User::factory(),
        'created_at' => Carbon::now(), // set a default value
        // other attributes
    ];
});

$factory->afterMaking(Car::class, function ($car, $faker) {
    $car->created_at = Carbon::now()->subSeconds(rand(0, 3600)); // override the created_at value
});

$factory->afterCreating(Car::class, function ($car, $faker) {
    // do something after creating the car
});

In this example, we're using the afterMaking callback to override the created_at value for each Car model that is made by the factory. The Carbon::now()->subSeconds(rand(0, 3600)) expression sets the created_at value to a random time within the past hour.

Note that we're using the rand function to generate a random number of seconds between 0 and 3600, which is equivalent to 1 hour. This ensures that each Car model will have a different created_at value and avoids the need for the sleep function.

With this approach, you can create multiple Car models for a User using the Car::factory()->for($user)->count(10)->create() method and each Car model will have a unique created_at value.

Upvotes: 0

AJ Zack
AJ Zack

Reputation: 383

  • make sure to enable mass assignment for your Car model first

  • use faker to override created_at in create() method:

$user = User::factory()->create(); 
$randomCarA = Car::factory()->for( $user )->create(['created_at'=>fake()->date()]);
$randomCarB = Car::factory()->for( $user )->create(['created_at'=>fake()->date()]);

Edit: to get most recent car:

$mostRecentCar=Car::latest()->first();

Upvotes: 1

Related Questions