首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Laravel更新模型的一对多关系项目

Laravel更新模型的一对多关系项目
EN

Stack Overflow用户
提问于 2021-05-13 09:14:10
回答 1查看 862关注 0票数 0

我有两个能言善辩的模型:

代码语言:javascript
复制
class User extends Model
{
    public function items()
    {
        return $this->hasMany(Item::class, "userId");
    }
}
代码语言:javascript
复制
class Item extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class, "userId");
    }
}

Item的专栏包括iduserIdname

当我想更新用户(PUT /users/<id>)时,我也希望在同一个请求中更新项目,这意味着:

  1. 跳过现有项目
  2. 添加新项目
  3. 移除额外的项目

之后,DB和关系都应该有最新的项目。

请求数据示例:

代码语言:javascript
复制
{
    "userAttribute1": "Something",
    "userAttribute2": "Something else",
    "items": [
        {
            "name": "Name1"
        },
        {
            "name": "Name2"
        }
    ]
}

我试着用Laravel寻找一种简单的方法来实现这一点,例如,对于多到多的关系,您只需调用attach/detachsync,就可以用ids自动更新它们。

我已经为我的一对多的关系而使这令人憎恶:

代码语言:javascript
复制
// Controller method for PUT /users/<id>
public function update(User $user, Request $request)
{
    // Update user attributes normal way
    $user->fill($request->validated());
    $user->save();
    
    // Array, e.g. [ { "name": "Name1" }, { "name": "Name2" } ]
    $newItems = $request->validated()["items"];
    
    // Collection of items
    $currentItems = $user->items;
    
    // Array to keep track of which items to delete later
    $removeItemIds = [];
    
    foreach ($currentItems as $i => $currentItem)
    {
        // currentItem is Item model

        $exists = false;
        
        foreach ($newItems as $j => $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 deletable
            $removeItemIds[] = $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),
    ];
}

这个用户+条目模型只是一个例子--我有多个类似的关系(不仅仅是name列),复制粘贴这段代码,稍微修改一下它似乎有点傻。

Laravel以所有这些花哨的快捷方式和易于使用/魔术的方法而闻名,所以我在这里的问题是:是否有一种更简单、更短的方法来进行此更新?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-14 01:52:59

您可以使用Laravel收藏品

首先,更新用户:

代码语言:javascript
复制
$user->update($request->validated());

然后,您可以同步用户项:

代码语言:javascript
复制
$new = ['item 1', 'item 2']; // Your request items

// Get the current items your user have:
$items = $user->items->pluck('name')->toArray();

// Now, you need to delete the items your user have but are not present in the request items array:
$deleteItems = $user->items->pluck('name', 'id')
  ->reject(function($value, $id) use ($new) {
      return in_array($value, $new);
  })
  ->keys();

Item::whereIn('id', $deleteItems)->delete();

// Last but not least, you need to create the new items and attach it to the user:
collect($new)->each(function($insertData) use ($user) {
  $user->items()->firstOrcreate([
      'name' => $insertData
  ]);
});

对于具有多个字段的模型,需要将两个数组传递给firstOrCreate方法。第一个数组将用于查找模型,如果未找到,则将通过两个数组的合并创建该数组:

代码语言:javascript
复制
$user->items()->firstOrcreate([
   'name' => $insertData
], [
    'description' => $description,
    'quantity' => $quantity
]);

由于您使用的是firstOrCreate,所以它只会在没有名称找到的情况下创建项目,并且不会有重复的项。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67516738

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档