2

I'm having problem understanding the updateOrCreate Eloquent method.

I have a simple DB where I'm saving information from API and running every x minutes.

What I need to to is to do an INSERT if the row doesn't exists in the DB or otherwise just do an UPDATE and update the changed data.

How can we achieve this functionality in Laravel?

Here is what I have right now, but what happens it rewrites the same data across all rows.

Not sure if I understand it correctly.

I'm basically passing in a "condition" ie. fields that needs to be checked against if they are unique - campaign_id+variation_id should be unique. (it's backed by UNIQUE key in MySQL) and my data array:

$data = [
    'campaign_id' => $variation['campaign_id'],
    'variation_id' => $variation['variation_id'],
    'name' => $variation['name'],
    'description' => $variation['description'],
];

MyModel::updateOrCreate( ['campaign_id' => $variation['campaign_id'], 'variation_id' => 
$variation['variation_id'] ], $data);

It seems to work with the updateOrInsert which is a Query Builder method, but I wanted to achieve this with Eloquent, thought it will be easy. Plus Query Builder doesn't fill the timestamps for me.

Thanks for any hints!

depi
  • 45
  • 9

2 Answers2

1

Looking at the source of Laravel, updateOrCreate() uses firstOrNew($attributes) under water.

public function updateOrCreate(array $attributes, array $values = [])
{
    return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
        $instance->fill($values)->save();
    });
}

Source

So when 1 or more items are found based on $attributes, it will only update the first result from those matched items.

Log all your queries and check what your code is actually doing. Logging queries

Using updateOrCreate() should result in 1 select and 1 update OR insert query, so 2 in total.

Robert
  • 5,703
  • 2
  • 31
  • 32
  • select * from `variations` where (`campaign_id` = ? and `variation_id` = ?) limit 1" "update `variations` set `name` = ?, `description` = ?, `updated_at` = ? where `campaign_id` = ? .... it's omitting the AND variaiton_id = ? condition. No idea why. – depi Nov 05 '17 at 15:58
  • 1
    Then @fmgonzalez's comment applies. You've set the primaryKey to campaign_id, so Laravel expects it to be a unique value. So if it finds 1, it assumes campaign_id is the unique identifier for that model and it ommits the rest from the update query. What you need is a composite primary key, see https://stackoverflow.com/questions/31415213/how-i-can-put-composite-keys-in-models-in-laravel-5 – Robert Nov 05 '17 at 16:07
  • Issue solved thanks to using composite keys. I had to load the package https://github.com/mopo922/LaravelTreats/tree/master/src/Model and it works now. – depi Nov 06 '17 at 04:16
0

So yes, I had the feeling and @Robert gave you the proofs.

You only need to set your model's $primaryKey to a composite value in this way:

protected $primarykey = ['campaign_id', 'variation_id']; 

I hope it works fine for you.

fmgonzalez
  • 823
  • 1
  • 7
  • 17
  • This solved the issue, but I had to load the https://github.com/mopo922/LaravelTreats/tree/master/src/Model package to support composite keys. – depi Nov 06 '17 at 04:16