Creating and Update Laravel Eloquent
Asked Answered
F

15

190

What's the shorthand for inserting a new record or updating if it exists?

<?php

$shopOwner = ShopMeta::where('shopId', '=', $theID)
    ->where('metadataKey', '=', 2001)->first();

if ($shopOwner == null) {
    // Insert new record into database
} else {
    // Update the existing record
}
Footboy answered 17/9, 2013 at 1:34 Comment(4)
I'm guessing shopId is not your primary key, right?Cythera
@SergiuParaschiv, yep. it's notFootboy
Check out the answer from @ErikTheDeveloper. It shows a nice embeded eloquent method that should do the job.Confute
The exact same thing is fully answered in the link below #18840441Rosaliarosalie
H
251

Here's a full example of what "lu cip" was talking about:

$user = User::firstOrNew(array('name' => Input::get('name')));
$user->foo = Input::get('foo');
$user->save();

Below is the updated link of the docs which is on the latest version of Laravel

Docs here: Updated link

Hatter answered 9/1, 2014 at 0:23 Comment(4)
exactly! 'firstOrNew' also exists in 4.0 (not mentionned in the docs)Lanctot
Also we can check $user is new/retrieved by using if($user->exists).J
@J That would likely cause race conditionsBasketball
new syntax seems to be updateOrInsert(array $attributes, array $values = []) in 5.5: github.com/laravel/framework/blob/5.5/src/Illuminate/Database/…Salient
R
113

2020 Update

As in Laravel >= 5.3, if someone is still curious how to do so in easy way it's possible by using: updateOrCreate().

For example for the asked question you can use something like:

$matchThese = ['shopId'=>$theID,'metadataKey'=>2001];
ShopMeta::updateOrCreate($matchThese,['shopOwner'=>'New One']);

Above code will check the table represented by ShopMeta, which will be most likely shop_metas unless not defined otherwise in the model itself.

And it will try to find entry with

column shopId = $theID

and

column metadateKey = 2001

and if it finds then it will update column shopOwner of found row to New One.

If it finds more than one matching rows then it will update the very first row that means which has lowest primary id.

If not found at all then it will insert a new row with:

shopId = $theID,metadateKey = 2001 and shopOwner = New One

Notice Check your model for $fillable and make sure that you have every column name defined there which you want to insert or update and rest columns have either default value or its id column auto incremented one.

Otherwise it will throw error when executing above example:

Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field '...' doesn't have a default value (SQL: insert into `...` (`...`,.., `updated_at`, `created_at`) values (...,.., xxxx-xx-xx xx:xx:xx, xxxx-xx-xx xx:xx:xx))'

As there would be some field which will need value while inserting new row and it will not be possible, as either it's not defined in $fillable or it doesn't have a default value.

For more reference please see Laravel Documentation at: https://laravel.com/docs/5.3/eloquent

One example from there is:

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
);

which pretty much clears everything.

Query Builder Update

Someone has asked if it is possible using Query Builder in Laravel. Here is reference for Query Builder from Laravel docs.

Query Builder works exactly the same as Eloquent so anything which is true for Eloquent is true for Query Builder as well. So for this specific case, just use the same function with your query builder like so:

$matchThese = array('shopId'=>$theID,'metadataKey'=>2001);
DB::table('shop_metas')::updateOrCreate($matchThese,['shopOwner'=>'New One']);

Of course, don't forget to add DB facade:

use Illuminate\Support\Facades\DB;

OR

use DB;
Rosenarosenbaum answered 23/12, 2016 at 8:51 Comment(7)
How about query builder?Shod
What about it ? :)Rosenarosenbaum
I want to do the same thing with Query Builder. Not Eloquent. Is it possible?Shod
Updated my answer, look for "Query Builder Update" section in the above answer.Rosenarosenbaum
I tried DB::table('shop_metas')::updateOrCreate method but this give me following error BadMethodCallException in Macroable.php line 59: Method updateOrInsert does not exist. Even though I use DB;Daryldaryle
@SwapnilShende you error says, you are trying to use "updateOrInsert" which doesnt exist in Laravel, its "updateOrCreate"Rosenarosenbaum
can we check it using our primary key?Marnimarnia
H
91

Updated: Aug 27 2014 - [updateOrCreate Built into core...]

Just in case people are still coming across this... I found out a few weeks after writing this, that this is in fact part of Laravel's Eloquent's core...

Digging into Eloquent’s equivalent method(s). You can see here:

https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L553

on :570 and :553

    /**
     * Create or update a record matching the attributes, and fill it with values.
     *
     * @param  array  $attributes
     * @param  array  $values
     * @return static
     */
    public static function updateOrCreate(array $attributes, array $values = array())
    {
        $instance = static::firstOrNew($attributes);

        $instance->fill($values)->save();

        return $instance;
    }

Old Answer Below


I am wondering if there is any built in L4 functionality for doing this in some way such as:

$row = DB::table('table')->where('id', '=', $id)->first();
// Fancy field => data assignments here
$row->save();

I did create this method a few weeks back...

// Within a Model extends Eloquent
public static function createOrUpdate($formatted_array) {
    $row = Model::find($formatted_array['id']);
    if ($row === null) {
        Model::create($formatted_array);
        Session::flash('footer_message', "CREATED");
    } else {
        $row->update($formatted_array);
        Session::flash('footer_message', "EXISITING");
    }
    $affected_row = Model::find($formatted_array['id']);
    return $affected_row;
}

I would love to see an alternative to this if anyone has one to share.

Handbarrow answered 24/12, 2013 at 16:52 Comment(5)
There is and it's called firstOrNew / firstsOrCreateBrynnbrynna
@malcolmhall I've updated the answer above. It turns out Eloquent has many features that I've found myself rebuilding ;) Always good to spend some time browsing the docs :)Handbarrow
packagist's 4.2.0 (stable 2014/6/1) doesn't contain updateOrCreate. But one can implement it looking at the source. ModelName::firstOrNew(['param' => 'condition'])->fill(Input::get())->save(); should do it.Hedve
Just watch out that Laravel doesn't run it as a transaction, so if you have unique keys and another user creates it with the same key simultaneously you may get an exception. I believe that one of the advantages of RedBeanPHP is this type of thing is done in an a transaction for you.Brynnbrynna
Thanks for pointing out the use of fill() That has aided me greatly!Banger
H
21

firstOrNew will create record if not exist and updating a row if already exist. You can also use updateOrCreate here is the full example

$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
); 

If there's a flight from Oakland to San Diego, set the price to $99. if not exist create new row

Reference Doc here: (https://laravel.com/docs/5.5/eloquent)

Hypanthium answered 4/2, 2018 at 9:24 Comment(0)
F
18

Save function:

$shopOwner->save()

already do what you want...

Laravel code:

    // If the model already exists in the database we can just update our record
    // that is already in this database using the current IDs in this "where"
    // clause to only update this model. Otherwise, we'll just insert them.
    if ($this->exists)
    {
        $saved = $this->performUpdate($query);
    }

    // If the model is brand new, we'll insert it into our database and set the
    // ID attribute on the model to the value of the newly inserted row's ID
    // which is typically an auto-increment value managed by the database.
    else
    {
        $saved = $this->performInsert($query);
    }
Farmstead answered 17/9, 2013 at 9:26 Comment(2)
That does not look like an atomic upsert operation. If it is not, this might cause race conditions.Royston
This code is to check if model is loaded from DB or is a Memory Based Model. Update or Create needs explicit definition of key columns to be checked and can't be performed implicitly.Milo
E
7

If you need the same functionality using the DB, in Laravel >= 5.5 you can use:

DB::table('table_name')->updateOrInsert($attributes, $values);

or the shorthand version when $attributes and $values are the same:

DB::table('table_name')->updateOrInsert($values);
Exequatur answered 9/9, 2018 at 15:31 Comment(0)
B
6
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));

Then make your changes and save. Note the firstOrNew doesn't do the insert if its not found, if you do need that then its firstOrCreate.

Brynnbrynna answered 24/8, 2014 at 11:59 Comment(0)
R
4

Like the firstOrCreate method, updateOrCreate persists the model, so there's no need to call save()

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.

$flight = App\Flight::updateOrCreate(
   ['departure' => 'Oakland', 'destination' => 'San Diego'],
   ['price' => 99]
);

And for your issue

$shopOwner = ShopMeta::updateOrCreate(
   ['shopId' => $theID, 'metadataKey' => '2001'],
   ['other field' => 'val' ,'other field' => 'val', ....]
);
Rosaliarosalie answered 30/3, 2018 at 14:5 Comment(0)
M
2

One more option if your id isn't autoincrement and you know which one to insert/update:

$object = MyModel::findOrNew($id);
//assign attributes to update...
$object->save();
Michelinamicheline answered 16/8, 2017 at 10:59 Comment(0)
G
1

Actually firstOrCreate would not update in case that the register already exists in the DB. I improved a bit Erik's solution as I actually needed to update a table that has unique values not only for the column "id"

/**
 * If the register exists in the table, it updates it. 
 * Otherwise it creates it
 * @param array $data Data to Insert/Update
 * @param array $keys Keys to check for in the table
 * @return Object
 */
static function createOrUpdate($data, $keys) {
    $record = self::where($keys)->first();
    if (is_null($record)) {
        return self::create($data);
    } else {
        return self::where($keys)->update($data);
    }
}

Then you'd use it like this:

Model::createOrUpdate(
        array(
    'id_a' => 1,
    'foo' => 'bar'
        ), array(
    'id_a' => 1
        )
);
Genealogy answered 25/8, 2014 at 14:14 Comment(1)
what was good in not doing this: 1. Delete based on key, and 2. create with new values. These were still 2 operations. is it to save the time to index in case of creation and deletion?Sculpin
S
1

like @JuanchoRamone posted above (thank @Juancho) it's very useful for me, but if your data is array you should modify a little like this:

public static function createOrUpdate($data, $keys) {
    $record = self::where($keys)->first();
    if (is_null($record)) {
        return self::create($data);
    } else {
        return $record->update($data);
    }
}
Somite answered 4/7, 2016 at 23:59 Comment(2)
Just a quick note that this should be updateOrCreate instead of createOrUpdateIndex
Ok but if there's 1000 rows, it will be 1000 queries running?Simpatico
T
1

UpdateOrCreate method means either update or creates by checking where condition.
It is simple as in the code you can see, in the users table, it will check if an email has the value $user->email then it will update the data (which is in the 2nd param as an array) or it will create a data according to it.

$newUser = User::updateOrCreate(['email' => $user->email],[
                'name' => $user->getName(),
                'username' => $user->getName().''.$user->getId(),
                'email' => $user->getEmail(),
                'phone_no' => '',
                'country_id' => 0,
                'email_verified_at' => Carbon::now()->toDateTimeString(),
                'is_email_verified' => 1,
                'password'=>Hash::make('Secure123$'),
                'avatar' => $user->getAvatar(),
                'provider' => 'google',
                'provider_id' => $user->getId(),
                'access_token' => $user->token,
                ]);
Tea answered 23/11, 2022 at 5:44 Comment(0)
M
0

Isn't this the same as updateOrCreate()?

It is similar but not the same. The updateOrCreate() will only work for one row at a time which doesn't allow bulk insert. InsertOnDuplicateKey will work on many rows.

https://github.com/yadakhov/insert-on-duplicate-key

Mangle answered 24/5, 2018 at 11:42 Comment(0)
C
0

Try more parameters one which will surely find and if available update and not then it will create new

$save_data= Model::firstOrNew(['key1' => $key1value,'key'=>$key2value]);
//your values here
$save_data->save();
Castara answered 18/2, 2021 at 11:50 Comment(0)
R
-2

check if a user exists or not. If not insert

$exist = DB::table('User')->where(['username'=>$username,'password'=>$password])->get();
if(count($exist)  >0) {
    echo "User already exist";;
}
else  {
    $data=array('username'=>$username,'password'=>$password);
    DB::table('User')->insert($data);
}
Laravel 5.4           
Roubaix answered 24/4, 2017 at 10:14 Comment(5)
Welcome to SO.Take a look at this how-to-answer for providing quality answer. ---Magically
Please also tag the framework you are using, php version, database.Vanmeter
i am using Laravel 5.4 ,php7 and mysqlRoubaix
Sabrina It's not an ideal solution as a functions already exists in laravel for doing so. But your's is a general solutionKirkpatrick
Its old school method laravel already has a function for this. See selected answerHypanthium

© 2022 - 2024 — McMap. All rights reserved.