firstOrCreate() is a Laravel Eloquent method that searches for a record matching a set of attributes, and if none exists, creates and saves a new one — all in a single call. Available in Laravel 10, 11, and 12, it eliminates the manual if/else pattern of checking for a record and creating it yourself. You get the model back either way, already persisted in the database.
:::note[TL;DR]
Model::firstOrCreate($attributes, $values)finds the first match or creates a new record- First argument: the search criteria (used in the WHERE clause)
- Second argument: extra fields set only when creating a new record (not used for searching)
- Automatically saves to the database — no
->save()call needed - Differs from
firstOrNew(): that method returns an unsaved instance you must save manually :::
What is the syntax for firstOrCreate()?
The method accepts two arrays. The first is required; the second is optional.
Model::firstOrCreate(array $attributes, array $values = [])
The $attributes array is the search criteria — these fields are used in the WHERE clause. If no match is found, Laravel creates a new record using a merge of both $attributes and $values. The $values fields are only applied when creating, not when searching.
How does firstOrCreate() work in practice?
Scenario: A user signs in with Google OAuth. You call
User::firstOrCreate(['email' => $oauthEmail], ['name' => $oauthName, 'provider' => 'google']). If that email already exists in your users table, you get the existing record back. If it doesn’t, Laravel creates a new user with the email, name, and provider — and saves it. No if/else. No separatesave().
Without firstOrCreate(), the same logic takes more code. Here’s the manual approach:
$email = $oauthUser->getEmail();
$user = User::where('email', $email)->first();
if ($user === null) {
$user = User::create([
'email' => $email,
'name' => $oauthUser->getName(),
'provider' => 'google',
]);
}
With firstOrCreate(), that entire block collapses to one line:
$user = User::firstOrCreate(
['email' => $oauthUser->getEmail()],
[
'name' => $oauthUser->getName(),
'provider' => 'google',
]
);
The result is the same. The email is the search key. name and provider are only written when a new record is being created — they don’t affect the lookup.
What is the difference between the two parameters?
The split matters. First argument: fields used to find the record. Second argument: fields added only on creation.
Consider this call:
$tag = Tag::firstOrCreate(
['slug' => 'laravel-tips'],
['name' => 'Laravel Tips', 'color' => '#FF6347']
);
Laravel runs SELECT * FROM tags WHERE slug = 'laravel-tips' LIMIT 1. If found, it returns that record — it doesn’t touch name or color. If not found, it inserts a new row with slug, name, and color all set.
This is intentional. You don’t want to accidentally overwrite existing field values during a “find or create” operation.
What is the difference between firstOrCreate() and firstOrNew()?
The key difference is persistence. firstOrCreate() saves to the database automatically. firstOrNew() returns an unsaved model instance — you call ->save() yourself.
| Method | Saves to DB? | Use when… |
|---|---|---|
firstOrCreate() | Yes, immediately | You want the record saved without extra steps |
firstOrNew() | No, manual save required | You need to modify the instance before saving |
If you need to enrich the model with data from an external API before saving, use firstOrNew(). If you just want a guaranteed-persisted record with no extra logic, firstOrCreate() is the right call.
Can I use firstOrCreate() with unique constraints?
Yes, and it’s designed for exactly this. firstOrCreate() is safe to use with unique columns as search criteria. It’s commonly used for seeding tags, categories, roles, and similar reference data.
// Seeding roles without duplicates
$roles = ['admin', 'editor', 'viewer'];
foreach ($roles as $role) {
Role::firstOrCreate(['name' => $role]);
}
Running this multiple times won’t create duplicates. Each iteration either finds the existing role or creates it fresh.
:::warning
firstOrCreate() is not atomic by default. In high-concurrency scenarios, two requests can simultaneously find no matching record and both attempt to create one, causing a duplicate key error. In Laravel 10+, consider wrapping it in a try/catch for UniqueConstraintViolationException, or use updateOrCreate() if you want to also update existing records.
:::
Summary
firstOrCreate($attributes, $values)finds a record by$attributesor creates a new one with both arrays merged- The second argument is only used when creating — it never affects the search
- The new record is saved to the database immediately — no
->save()needed - Use
firstOrNew()instead when you need to modify the model instance before persisting it - In concurrent environments, wrap in a try/catch to handle potential unique constraint violations
FAQ
Does firstOrCreate() fire model events?
Yes. If a new record is created, the creating and created model events fire. If an existing record is found, no events fire.
Can I use firstOrCreate() with composite keys?
Yes. Pass multiple fields in the first argument to search on multiple columns simultaneously: Model::firstOrCreate(['user_id' => 1, 'tag_id' => 5]).
Does firstOrCreate() return different values based on whether it created or found a record?
No. It always returns the model instance. To check whether the record was just created, access the wasRecentlyCreated attribute: if ($user->wasRecentlyCreated) { ... }.
Is firstOrCreate() available in Laravel 10, 11, and 12? Yes. It’s a long-standing Eloquent method, unchanged across all three versions.
What if I need to update an existing record during this operation?
Use updateOrCreate() instead. It finds the first match and updates it with new values, or creates it if not found.
What to Read Next
- Laravel firstOrNew(): Find or Prepare a Record — the unsaved-instance variant, for when you need to modify before saving
- Update a Laravel Record Without Touching Timestamps — how to save without changing
updated_at - Laravel Eloquent whereDate() Method Explained — date-based filtering in Eloquent