当关联更改时,CakePHP 3.8 belongsTo 不会保留对新关联的更改
CakePHP 3.8 belongsTo does not persist changes to new association when association is changed
我目前正在使用 CakePHP 为我编写的一些票务逻辑提供基于 api 的 crud。我 运行 遇到了一个问题,我试图更改新关联中的 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));
}
}
任务中资产的关联 table 如下所示:
<?php
class TasksTable extends Cake\ORM\Table
{
public function initialize(array $config)
{
$this->belongsTo('Asset', [
'className' => 'Assets',
'foreignKey' => 'asset_id',
'joinType' => 'LEFT'
]);
}
}
这些构建规则附加到资产 table:
<?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 编辑到资产实体中目前与 Task 实体 (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 关联,如记录here:
<?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 关联并在同一事务中对其进行编辑?如果是这样,我的请求或代码的结构应该如何不同?
谢谢!
正如评论中所暗示的那样,您 运行 受到编组器的限制,目前还没有过于简单明了的解决方法(您可能想提出一个问题 over at GitHub,也许有人想要承担为此类 marshalling/saving) 添加支持的任务。
书中的例子并不是最好的例子,事实上我个人 remove/change 它只有在允许批量分配主键时才有效(默认情况下对于烘焙实体它不是),并且它也只在创建新记录时起作用,即当要保存的实体被标记为“新”时。然而,这样做会引入各种怪癖,比如验证规则会将数据视为“创建”(新),应用程序规则会将实体视为“更新”(不是新的),但不会收到任何 existing/original 数据,与其他保存阶段回调相同,这会弄乱诸如审计之类的东西,基本上任何你需要新数据 和 原始数据的东西。
长话短说,即使您可以像那个例子一样工作,您也只会在以后引入其他问题,所以最好暂时忘掉它。我现在想到的唯一解决方法是比较给定的资产主键,手动加载现有资产实体,并在修补数据之前替换已经加载的实体,大致如下:
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()
,因为您似乎不太可能通过查询字符串传递该数据.
我目前正在使用 CakePHP 为我编写的一些票务逻辑提供基于 api 的 crud。我 运行 遇到了一个问题,我试图更改新关联中的 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));
}
}
任务中资产的关联 table 如下所示:
<?php
class TasksTable extends Cake\ORM\Table
{
public function initialize(array $config)
{
$this->belongsTo('Asset', [
'className' => 'Assets',
'foreignKey' => 'asset_id',
'joinType' => 'LEFT'
]);
}
}
这些构建规则附加到资产 table:
<?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 编辑到资产实体中目前与 Task 实体 (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 关联,如记录here:
<?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 关联并在同一事务中对其进行编辑?如果是这样,我的请求或代码的结构应该如何不同?
谢谢!
正如评论中所暗示的那样,您 运行 受到编组器的限制,目前还没有过于简单明了的解决方法(您可能想提出一个问题 over at GitHub,也许有人想要承担为此类 marshalling/saving) 添加支持的任务。
书中的例子并不是最好的例子,事实上我个人 remove/change 它只有在允许批量分配主键时才有效(默认情况下对于烘焙实体它不是),并且它也只在创建新记录时起作用,即当要保存的实体被标记为“新”时。然而,这样做会引入各种怪癖,比如验证规则会将数据视为“创建”(新),应用程序规则会将实体视为“更新”(不是新的),但不会收到任何 existing/original 数据,与其他保存阶段回调相同,这会弄乱诸如审计之类的东西,基本上任何你需要新数据 和 原始数据的东西。
长话短说,即使您可以像那个例子一样工作,您也只会在以后引入其他问题,所以最好暂时忘掉它。我现在想到的唯一解决方法是比较给定的资产主键,手动加载现有资产实体,并在修补数据之前替换已经加载的实体,大致如下:
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()
,因为您似乎不太可能通过查询字符串传递该数据.