CakePHP 3.8 belongsTo不会在关联更改时将更改持久化到新关联

zkure5ic  于 2022-11-12  发布在  PHP
关注(0)|答案(1)|浏览(161)

我目前正在使用CakePHP为我编写的一些票务逻辑提供一个基于crud的API。我遇到了一个问题,我试图更改一个belongsTo关联和新关联中的数据,但它没有持久化。
执行持久化的控制器如下所示:

<?php

class TasksController extends Cake\Controller\Controller
{
    public function initialize()
    {
        parent::initialize();
    }

    public function edit(array $ids): void
    {
        $validationErrors = [];

        $tasks = $this->Tasks->find('all')
            ->contain($this->setAssociations($query))
            ->where([$this->Model->getAlias().'.id IN' => $ids]);

        foreach ($tasks as $id => $task) {
            $this->Tasks->patchEntity($task, $this->request->getQuery()[$id], [
                'associated' => ['Asset']
            ]);
        }

        if ($this->Tasks->saveMany($tasks)) {
            $this->response = $this->response->withStatus(200);
        } else {
            // Handle errors
        }

        // Render json output for success / errors
        $this->set($this->createViewVars(['entities' => $tasks], $validationErrors));
    }
}

任务表中资产的关联如下所示:

<?php

class TasksTable extends Cake\ORM\Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Asset', [
            'className' => 'Assets',
            'foreignKey' => 'asset_id',
            'joinType' => 'LEFT'
        ]);
    }
}

这些构建规则附加到资产表:

<?php

class AssetsTable extends Cake\ORM\Table
{
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->isUnique(['number', 'group_id'], 'The Number you selected is in use'));
        $rules->add($rules->isUnique(['name', 'group_id'], 'The Name you selected is in use'));
    }
}

我发送的请求的正文如下所示:

{
    "421933": {
        "description": "This task was edited by the api!",
        "asset": {
            "id": "138499",
            "description": "This asset was edited by they api!",
            "name": "105",
            "number": "6"
        }
    }
}

基本上,名称105和编号6被标记为不唯一,因为它们已经在资产138499上设置为这些值。相反,查询尝试将名称105和编号6编辑到当前与任务实体(645163)关联的资产实体中,这将触发isUnquie构建规则失败。
您可以通过在上述控制器中的saveMany调用之前打印$tasks来查看这一点:

Array
(
    [0] => App\Model\Entity\Task Object
        (
            [id] => 421933
            [description] => This task was edited by the api!
            .....
            [asset] => App\Model\Entity\Asset Object
                (
                    [id] => 645163
                    [name] => 105
                    [description] => This asset was edited by they api!
                    [number] => 6
                    ....
                )

        )

)

将资产138499编辑为任务421933的关联似乎应该可以工作,因为看起来可以在CakePHP文档中以这种方式保存belongsTo关联,如下所示:

<?php

$data = [
    'title' => 'First Post',
    'user' => [
        'id' => 1,
        'username' => 'mark'
    ]
];

$articles = TableRegistry::getTableLocator()->get('Articles');
$article = $articles->newEntity($data, [
    'associated' => ['Users']
]);

$articles->save($article);

是否可以在同一事务中关联belongsTo关联并编辑它?如果可以,我的请求或代码应该如何不同地构造?
谢谢你!

ufj5ltwl

ufj5ltwl1#

正如评论中所暗示的那样,您遇到了编组器的限制,对此还没有非常严格的解决方法(您可能希望打开一个问题over at GitHub,也许有人希望承担为这种类型的编组/保存添加支持的任务)。
书中的例子并不是最好的例子,实际上我个人会删除/更改它,它只在允许批量分配主键时才起作用(默认情况下烘焙实体不是这样),并且它也只在创建新记录时起作用,即当要保存的实体标记为“新”时。然而,这样做会引入各种各样的怪癖,例如,验证规则会将数据视为“创建”(新),应用程序规则会将实体视为“更新”(非新),但不会接收任何现有/原始数据,其他保存阶段回调也是如此,这会将审计等工作搞砸,基本上是需要新数据 * 和 * 原始数据的任何工作。
长话短说,即使你能让它像这个例子一样工作,你也会在后面引入其他的问题,所以现在最好忘记这个问题。我现在想到的唯一的解决方法是比较给定的资产主键,手动加载现有的资产实体,并在修补数据之前替换已经加载的实体。大致是这样的:

foreach ($tasks as $task) {
    $data = $this->request->getData($task->id);
    $assetId = (int)\Cake\Utility\Hash::get($data, 'asset.id');
    if ($task->asset->id !== $assetId) {
        $task->asset = $this->Tasks->Asset->get($assetId);
    }

    $this->Tasks->patchEntity($task, $data, [
        'associated' => ['Asset']
    ]);
}
  • 请注意,我已经将您的getQuery()用法更改为getData(),因为您似乎不太可能通过查询字符串传递该数据。*

相关问题