0
0 Comments

I have two Eloquent models:

class User extends Model
{
    public function items()
    {
        return this->hasMany(Item::class, "userId");     } } </pre></div><!-- /wp:codemirror-blocks/code-block --> <!-- wp:codemirror-blocks/code-block {"showPanel":false,"languageLabel":"no","mode":"clike","mime":"text\/x-c++src"} --> 			<div class="wp-block-codemirror-blocks-code-block code-block"><pre>class Item extends Model {     public function user()     {         returnthis->belongsTo(User::class, "userId");
    }
}

Item
‘s columns are
id
,
userId
and
name
.

When I want to update the user (

PUT /users/<id>
) I also want to update the items, meaning:

  1. Skip existing items
  2. Add new items
  3. Remove extra items

Afterwards both DB and the relation should have up-to-date items.

I tried to look for a simple way to do this with Laravel, for example with many-to-many relations you can just call

attach
/
detach
and
sync
to automatically update them with ids.

I have made this abomination for my one-to-many relation:

// Controller method for PUT /users/<id>
public function update(User user, Requestrequest)
{
    // Update user attributes normal way
    user->fill(request->validated());
    user->save();          // Array, e.g. [ { "name": "Name1" }, { "name": "Name2" } ]newItems = request->validated()["items"];          // Collection of itemscurrentItems = user->items;          // Array to keep track of which items to delete laterremoveItemIds = [];
    
    foreach (currentItems asi => currentItem)     {         // currentItem is Item modelexists = false;
        
        foreach (newItems asj => newItem)         {             // newItem is array, e.g. { "name": "Name1" }              if (currentItem->name === newItem["name"])             {exists = true;
                break;
            }
        }
        
        if (exists)         {             // New item already exists, remove from newItems array             unset(newItems[j]);         }         else         {             // Current item does't exist anymore, remove from currentItems collection and mark as deletableremoveItemIds[] = currentItem->id;             unset(currentItems[i]);         }     }          // Add remaining new items     foreach (newItems as newItem)     {item = Item::make(newItem);item->userId = user->id;item->save();
        
        currentItems->push(item);
    }
    
    // Delete extra items
    user->items()->whereIn("id",removeItemIds)->delete();
    
    // Update relation so the returned data is up-to-date as well
    user->setRelation("items",currentItems);
    
    return [
        "user" => new UserResource($user),
    ];
}

This user + items model is just an example – I have multiple similar relations (where there is more than just

name
column) and copy pasting this code everywhere and slightly modifying it seems a little bit dumb.

Laravel is known for all these fancy shortcuts and easy to use/magic methods so my question here is: is there a simpler and shorter way to do this update?

Anonymous Asked question May 13, 2021