skip to Main Content

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?

2

Answers


    • 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();
    
    Login or Signup to reply.
  1. 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 CarbonCarbon;
    
    $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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search