96

I know using:

User::firstOrCreate(array('name' => $input['name'], 'email' => $input['email'], 'password' => $input['password']));

Checks whether the user exists first, if not it creates it, but how does it check? Does it check on all the params provided or is there a way to specifiy a specific param, e.g. can I just check that the email address exists, and not the name - as two users may have the same name but their email address needs to be unique.

panthro
  • 22,779
  • 66
  • 183
  • 324

5 Answers5

173

firstOrCreate() checks for all the arguments to be present before it finds a match. If not all arguments match, then a new instance of the model will be created.

If you only want to check on a specific field, then use firstOrCreate(['field_name' => 'value']) with only one item in the array. This will return the first item that matches, or create a new one if not matches are found.

The difference between firstOrCreate() and firstOrNew():

  • firstOrCreate() will automatically create a new entry in the database if there is not match found. Otherwise it will give you the matched item.
  • firstOrNew() will give you a new model instance to work with if not match was found, but will only be saved to the database when you explicitly do so (calling save() on the model). Otherwise it will give you the matched item.

Choosing between one or the other depends on what you want to do. If you want to modify the model instance before it is saved for the first time (e.g. setting a name or some mandatory field), you should use firstOrNew(). If you can just use the arguments to immediately create a new model instance in the database without modifying it, you can use firstOrCreate().

lowerends
  • 5,469
  • 2
  • 24
  • 36
  • I know. How can I specify to match on specific attributes. I only want to check on email and then if that matches update, if not create a new record. – panthro Aug 07 '14 at 13:43
  • Thanks, I've done: $user = User::firstOrNew(array('email' => Input::get('email'))); $user->name = Input::get('name'); $user->save(); Correct? – panthro Aug 07 '14 at 13:47
  • I changed it to firstOrNew, or should I have used firstOrCreate? – panthro Aug 07 '14 at 13:47
  • You can do it in one step: `$user = User::firstOrCreate(array('email' => Input::get('email'), 'name' => Input::get('name')))`. – lowerends Aug 07 '14 at 13:49
  • You can't do it all in one step. If a new user has the same email BUT a different name, it will create a new record (it should update it as email is unique). Hence why I can't do mass assignment. Unless you know of a fix? – panthro Aug 07 '14 at 13:53
  • Ok, in that case you can go with your second comment above. You can also use validation to check for unique values. – lowerends Aug 07 '14 at 13:55
  • Thanks but whats the difference between firstOrCreate and firstOrNew? Also how would I use validation and then do a firstOrCreate - could you provide an example? – panthro Aug 07 '14 at 14:02
  • I've explained the difference in my answer. As for validation, I suggest you have a look at this: http://laravel.com/docs/validation – lowerends Aug 07 '14 at 14:07
  • Thanks, so which should I use? Why would I need a new model instance vs getting the matched item? – panthro Aug 07 '14 at 14:11
  • Thanks, one last thing, so with firstOrCreate I would need to set the params like: firstOrCreate($params) but with firstOrNew I could do: $user = User::firstOrNew(array('email' => Input::get('email'))); $user->name = Input::get('name'); $user->save(); Correct? – panthro Aug 07 '14 at 14:30
  • I think @panthro talk about updateOrCreate function. – Vahe Galstyan Feb 15 '17 at 17:17
  • I have declared $fillable for mass assignment but firstOrCreate inserted an null record into database. Do you know why? – Tommy Hoang Feb 23 '21 at 08:25
  • @lowerends, If not all arguments match, then a new instance of the model will be created. Is it create a new record in Database ? – abu abu Sep 21 '22 at 10:40
94

As of Laravel 5.3 it's possible to do this in one step with firstOrCreate using a second optional values parameter used only if a new record is created, and not for the initial search. It's explained in the documentation as follows:

The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model cannot be found in the database, a record will be inserted with the attributes resulting from merging the first array argument with the optional second array argument.

Example

$user = User::firstOrCreate([
    'email' => 'dummy@domain.example'
], [
    'firstName' => 'Taylor',
    'lastName' => 'Otwell'
]);

This returns the User for the specified email if found, otherwise creates and returns a new user with the combined array of email, firstName, and lastName.


This technique requires Mass Assignment to be set up, either using the fillable or guarded properties to dictate which fields may be passed into the create call.

For this example the following would work (as a property of the User class):

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['email', 'firstName', 'lastName'];
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
umbrel
  • 4,159
  • 1
  • 15
  • 13
  • 31
    I feel it's important to mention that the fields you want committed to the database need to be added to `protected $fillable` on the corresponding model since it uses mass assignment. – WhyAyala Feb 06 '18 at 22:23
  • WhyAyala notice is very important. Cause if you include in protected $fillable = ['email']; just keys from the first method firstOrCreate parameter your query would be like -> INSERT into tablename ('email', `updated_at`, `created_at`) VALUES... – darjus Jul 20 '18 at 07:29
  • Thanks for this clear and direct answer! By the way, it should be written "to be set up" as separate words. "Setup" is only joined when used as an adjective (to describe the word that comes after it), or as a noun (a thing, e.g. "here is your new setup"). Please help bring back correct word joinery and visual reading! – GeneC Jul 20 '21 at 19:35
5

firstOrCreate() checks for all the arguments to be present before it finds a match.

If you only want to check on a specific field, then use firstOrCreate(['field_name' => 'value']) like:

$user = User::firstOrCreate([
    'email' => 'abcd@gmail.com'
], [
    'firstName' => 'abcd',
    'lastName' => 'efgh',
    'veristyName'=>'xyz',
]);

Then it checks only the email.

kjones
  • 1,339
  • 1
  • 13
  • 28
albus_severus
  • 3,626
  • 1
  • 13
  • 25
4

An update:

As of Laravel 5.3 doing this in a single step is possible; the firstOrCreate method now accepts an optional second array as an argument.

The first array argument is the array on which the fields/values are matched, and the second array is the additional fields to use in the creation of the model if no match is found via matching the fields/values in the first array:

See the Laravel API documentation

kjones
  • 1,339
  • 1
  • 13
  • 28
A_funs
  • 1,228
  • 2
  • 19
  • 31
3

You can always check if in current instance the record is created with the help of

$user->wasRecentlyCreated

So basically you can

if($user->wasRecentlyCreated){

 // do what you need to do here

}
Lonare
  • 3,581
  • 1
  • 41
  • 45