Mr.SH
Mr.SH

Reputation: 457

Laravel Eloquent update related records

I have two mysql tables

1. sales (id,buyer_id)
2. sales_items (id,sales_id,item_id,price,quantity)

very simple two tables. I am having issue while want to update any record, like when I edit some sales record I can

  1. add new item
  2. modify previously added item
  3. or delete previously added item

Now I want to update sales_items tables also.

What I am doing now is

  1. first deleting all records of sales in sales_items table
  2. Add new records
$task = Auth::user()->sales_items()->where(['sales_id'=>1]);
$task->delete();

//$SalesProducts array have all rows
SaleItem::insert($SalesProducts);

code is working but is this correct approach or there is batter way to do that? I am using Laravel 8 Eloquent and all tables have models

Thanks

Upvotes: 2

Views: 2116

Answers (2)

M Khalid Junaid
M Khalid Junaid

Reputation: 64466

Normally this type of update process is a bit lengthy but efficient and optimal as compared to delete and insert hack, there are many benefits of this approach

  • Primary key of a records remains same as compared to other approach which creates a new record and assign a new primary key
  • You can track update_at timestamp which will change on every update while in case of delete/insert the created_at and updated_at will have same values forever
  • No unnecessary auto increments added on database side, like sale 1 has 3 records with id 1,2,3 and on update without changing any data the records will be deleted and inserted again and their ids will be 4,5,6 and next auto increment will be set to 7

Lets say in database you already have 2 rows for sale 1

______________________________________________
| id | sales_id | item_id | price | quantity |
----------------------------------------------
|  1 |   1      |    1    |  20   |   5      |
----------------------------------------------
|  2 |   1      |    2    |  10   |   5      |
----------------------------------------------

Let say you have following collection to perform update for sale 1

$collection = collect([
    [
        "id"=> null,
        "sales_id"=>1,
        "item_id"=> 1,
        "price"=>10,
        "quantity"=> 0
    ],
    [
        "id"=> 1,
        "sales_id"=>1,
        "item_id"=> 1,
        "price"=>10,
        "quantity"=> 0
    ]
]);

Now if we compare database results and the requested update collection we can see the there is

  • one insert for first item in collection because id is null
  • one update for second item in collection because id 1 exists in database
  • one delete because in requested collection there is no item with id 2 so that needs to be deleted

To perform above updates first we need to collect all items where id is null so we will add these records in database

$addItems = $collection->whereNull('id'); 
if($addItems->count() > 0){
    SaleItem::insert($addItems);
}

For update we need to collect items where id is present and these records will be updated in database as

$updateItems = $collection->whereNotNull('id');
foreach($updateItems as $key => $updateItem) {
    SaleItem::where('id', $updateItem->id)->update($updateItem);
}

Or we can use upserts for first and second step as

SaleItem::upsert($updateItems, ['id']);

For delete we can get all ids from collection and perform delete operation on rows that do not match these ids as

$deleteItems = $updateItems->pluck('id');
SaleItem::whereNotIn('id', $deleteItems)->delete();

Upvotes: 1

IGP
IGP

Reputation: 15786

You have 2 options:

1. Use Eloquent events

# Sale model
protected static function booted()
{
    static::deleting(function ($sale) {
        SaleItems::where('sales_id', $sale->id)->delete();
        return true;
    });
}
# Item model
protected static function booted()
{
    static::deleting(function ($item) {
        SaleItems::where('item_id', $item->id)->delete();
        return true;
    });
}

This should run whenever a Sale or Item fire the deleting event. I'm not sure if mass-deletion works correctly.

2. Use Database foreign key constraints

# sales_items table migration

$table->foreignId('item_id')
      ->constrained('items')
      ->onUpdate('cascade')
      ->onDelete('cascade');

$table->foreignId('sales_id')
      ->constrained('sales')
      ->onUpdate('cascade')
      ->onDelete('cascade');

This should delete automatically the sales_items record if they are associated with an item or sales that just got deleted. However, this will NOT work with softDeletes.

Upvotes: 0

Related Questions