Reputation: 1272
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
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
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