Skip to content

[13.x] feat: add randomOrFactory and randomOrFactoryCreate methods#60271

Closed
calebdw wants to merge 1 commit into
laravel:13.xfrom
calebdw:calebdw/push-vurtulltqstn
Closed

[13.x] feat: add randomOrFactory and randomOrFactoryCreate methods#60271
calebdw wants to merge 1 commit into
laravel:13.xfrom
calebdw:calebdw/push-vurtulltqstn

Conversation

@calebdw
Copy link
Copy Markdown
Contributor

@calebdw calebdw commented May 25, 2026

Hello!

These are some factory helpers that I've been using for years in factories/seeders/tests and it was suggested that I upstream them.

There's many times that I want to grab a random model instead of always creating creating a new factory instance, but I don't want the code to fail if no records exist, some common use-cases:

  • having fewer "wide" parent models that are used across a variety of relations vs. having a ton of "narrow" parent models that are only used for one relationship
  • grabbing a random record from pre-seeded db in tests is generally faster than generating a new one and writing to db (could also have more expensive afterMaking/afterCreating logic that's avoided)
  • etc.

Examples

final class CronLogFactory extends Factory
{
    public function definition(): array
    {
        return [
-            'cron_id' => fn () => Cron::inRandomOrder()->first() ?? Cron::factory(),
+            'cron_id' => fn () => Cron::randomOrFactory(),
            // ...
        ];
    }
}

It also allow customizing the query or factory:

public function configure(): static
{
    return $this->afterMaking(function (Contact $contact): void {
-        $contact->customer_id ??= Customer::query()
-            ->inRandomOrder()
-            ->engineering()
-            ->first()?->id
-            ?? Customer::factory()
-                ->engineering()
-                ->create()->id
+        $contact->customer_id ??= Customer::randomOrFactoryCreate(
+            fn ($q) => $q->whereEngineering(),
+            fn ($f) => $f->engineering(),
+        )->id;
    });
}

Thanks!

@calebdw calebdw force-pushed the calebdw/push-vurtulltqstn branch from 4e7c809 to 02493ac Compare May 25, 2026 17:52
@calebdw calebdw force-pushed the calebdw/push-vurtulltqstn branch from 02493ac to c2fedcb Compare May 25, 2026 18:12
@DarkGhostHunter
Copy link
Copy Markdown
Contributor

DarkGhostHunter commented May 25, 2026

I believe this belongs more to the Factory instance rather than the model itself, but the problem is that when you go into the factory, you assume already you're creating models that are not in the database itself.

It could be Cron::factory()->firstRandom($callback = null) to retrieve a random model, allow to use a callback to alter the query (like only cron for a month or after a date).

It could be Cron::factory()->firstRandomOrCreate() and Cron::factory()->firstRandomOrMake(). If it's a callback, use it as query modifier, otherwise assume these are attributes.

@calebdw
Copy link
Copy Markdown
Contributor Author

calebdw commented May 25, 2026

  • the factory does not query the database which is why I placed it on the trait---good middle ground between queries and factory
  • the methods allow passing a count for multiple models to be returned / generated---so firstRandom doesn't make sense
  • the methods need to allow both adding scopes to the builder and adding states to the factory---the methods are not guaranteed to match

@DarkGhostHunter
Copy link
Copy Markdown
Contributor

Then I don't see any problem in doing Cron::inRandomOrder()->first() ?? Cron::factory().

@taylorotwell
Copy link
Copy Markdown
Member

Thanks for your pull request to Laravel!

Unfortunately, I'm going to delay merging this code for now. To preserve our ability to adequately maintain the framework, we need to be very careful regarding the amount of code we include.

If applicable, please consider releasing your code as a package so that the community can still take advantage of your contributions!

@calebdw calebdw deleted the calebdw/push-vurtulltqstn branch May 26, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants