我在 CakePHP 4 中有一个应用程序,但在保存关联的模型数据时遇到问题。我已经阅读了在 Cake 文档中保存关联数据,但目前还不清楚这如何应用,尤其是在我的用例中。
该应用程序有3个与此问题相关的表格:
items
sizes
items_sizes_wanted
该应用程序允许用户请求服装项目(items
),并且输入/保存此类项目的表格具有不同尺寸的下拉列表(sizes
)。每个大小都有一个唯一的 ID。用户可以在保存项目时选择一个或多个大小。items_sizes_wanted
表应包含一行(或多行),具体取决于用户选择的大小,以及相应的项目 ID。例如,如果他们为 Item 999 保存了尺寸 2、3 和 4,则此表中将有 3 行:
size_id | item_id
--------|---------
2 | 999
3 | 999
4 | 999
代码已烘焙,Table 类中的关联看起来正常:
// src/Model/Table/ItemsSizesWantedTable.php
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('items_sizes_wanted');
$this->belongsTo('Items', [
'foreignKey' => 'item_id',
'joinType' => 'INNER',
]);
$this->belongsTo('Sizes', [
'foreignKey' => 'size_id',
'joinType' => 'INNER',
]);
}
该项的实体类看起来也正常:
// src/Model/Entity/Item.php
// @property AppModelEntityItemsSizesWanted[] $items_sizes_wanted
protected $_accessible = [
// ...
'items_sizes_wanted' => true,
// ...
];
在保存项目(src/templates/Items/add.php
)的表单中,我使用了表单助手并使用点表示法命名它:
<?php
// Note that $sizes is an array of key/value pairs from the
// 'sizes' table.
?>
<?= $this->Form->control('items_sizes_wanted.size_id', ['options' => $sizes, 'multiple' => 'multiple']) ?>
在浏览器中呈现时,这将生成有效的数组语法名称。里面渲染的<option>
都有有效的ID,即sizes
表中的ID。
<select name="items_sizes_wanted[size_id]" multiple="multiple">
当我使用以下方法将数据保存在控制器(src/Controller/ItemsController.php
)中时:
public function add()
{
$item = $this->Items->newEmptyEntity();
if ($this->request->is('post')) {
$item = $this->Items->patchEntity($item, $this->request->getData());
// Edit: some of the entity properties are manually set at this point, e.g.
$item->item_status = 'Wanted';
if ($this->Items->save($item)) {
$this->Flash->success(__('Your item has been listed.'));
}
}
}
数据将正确保存到items
表中,并在浏览器中显示闪存成功消息"您的项目已列出"。
但是 - 没有数据写入items_sizes_wanted
.
我不确定这是为什么。链接的文档没有具体说明应该如何使用表单助手,所以我假设该表单字段的语法是正确的,但可能不是。
如果我在控制器中使用debug($item); die;
按保存后调试实体,即使我使用该表单选择了多个大小选项,它也会'items_sizes_wanted' => [ ]
。
请有人帮忙,因为我对这里出了什么问题感到迷茫?
免责声明:我不太了解 CakePHP,但我认为我要么知道解决方案,要么至少可以为您指明正确的方向。
您只获得一个选定大小而不是多个大小的原因是生成的输入字段被命名为items_sizes_wanted[size_id]
,但是,为了让 PHP 将多个值解析为一个数组,它们需要命名为items_sizes_wanted[size_id][]
。当请求参数以[]
结尾时,PHP 会将所有请求属性正确地解析为数组。
例如:下面是包含 POST 正文的请求var_dump($_POST);
items_sizes_wanted[size_id][]=A&items_sizes_wanted[size_id][]=B&items_sizes_wanted[size_id][]=C
array (size=1)
'items_sizes_wanted' =>
array (size=1)
'size_id' =>
array (size=3)
0 => string 'A' (length=1)
1 => string 'B' (length=1)
2 => string 'C' (length=1)
将其与items_sizes_wanted[size_id]=A&items_sizes_wanted[size_id]=B&items_sizes_wanted[size_id]=C
的 POST 正文进行比较(请注意,每个末尾的空大括号已被删除):
array (size=1)
'items_sizes_wanted' =>
array (size=1)
'size_id' => string 'C' (length=1)
这是我不太熟悉 CakePHP 的部分。我查看了代码 对于 CakePHP 的 FormHelper,并且基于模板代码,我认为您需要将表单代码更改为add.php
这样(为了可读性而重新格式化):
<?php
// Note that $sizes is an array of key/value pairs from the
// 'sizes' table.
?>
<?=
$this->Form->control(
'items_sizes_wanted.size_id',
[
'options' => $sizes,
'multiple' => 'multiple'
'type' => 'selectMultiple'
]
)
?>
根据 FormHelper 中的__call()
方法,您还可以像这样编写它:
$this->Form->selectMultiple(
'items_sizes_wanted.size_id',
[
'options' => $sizes,
'multiple' => 'multiple'
]
);
但是,我不熟悉创建control($fieldName, $options)
和inputType($fieldName, $options)
之间的细微差别,因此它们可能会产生不同的输出。
我目前正在从事CakePHP 4.x的一个项目。我的项目也有很多关联,它在表格中保存得很好,但 CakePHP 的烘焙方式与您的完全不同。让我向您展示差异,也许它有一些帮助。
我会将我的实体、表等的名称"翻译"到您的问题中使用的名称,好吗?
首先,简要介绍一下:在我的项目中,Cake 没有为关系表烘焙模型(实体和表)。关系表没有自己的模型,仅在ItemsTable
和WantedSizesTable
的initialize
方法中引用。Item
和WantedSize
实体以及视图中也有一些细微的变化。
其次,您的实体名称不符合 Cave 的命名约定,这可能会导致许多问题。这甚至可能是您现在正在忍受的问题的原因。为了符合它们,我已经更改了一些名称,但我建议您仔细阅读:https://book.cakephp.org/4/en/intro/conventions.html。
第三,也是更重要的,让我们开始吧。
我的多对多关系 mySQL 表没有自己的表模型。我的SQL确实有一个items_wanted_sizes
表,但是CakePHP项目没有相应的模型称为ItemsWantedSizesTable
也没有ItemsWantedSizes
。它确实有ItemsTable
和WantedSizesTable
表以及Item
和WantedSize
实体,仅此而已。
让我们看看表模型。关系 mySQL 表items_wanted_sizes
仅在 PHP 中两个表模型的表initialize
方法中引用,如下所示:
// ItemsTable.php
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('items');
$this->setDisplayField('item_name');
$this->setPrimaryKey('id');
// ...
// Other associations...
// ...
// The relational mysql table only shows here:
$this->belongsToMany('WantedSizes', [
'foreignKey' => 'item_id', // Item Id field from the relational table
'targetForeignKey' => 'wanted_size_id', // Size Id field from the relational table
'joinTable' => 'items_wanted_sizes',
]);
}
同样的情况也发生在WantedSizesTable上:
// WantedSizesTable.php
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('wanted_sizes');
$this->setDisplayField('wanted_size_name');
$this->setPrimaryKey('id');
// ...
// Other associations...
// ...
// The relational mysql table only shows here:
$this->belongsToMany('Items', [
'foreignKey' => 'wanted_size_id', // Size Id fieldname from the relational table
'targetForeignKey' => 'item_id', // Item Id fieldname from the relational table
'joinTable' => 'items_wanted_sizes',
]);
}
关于实体模型,我也没有关系实体模型。Item
和WantedSize
实体模型都相互引用,但与您的情况相反,它们不引用关系表(仅相互引用):
// src/Model/Entity/Item.php
// @property AppModelEntityWantedSize[] $wanted_sizes // NOT item_wanted_sizes
protected $_accessible = [
// ...
'wanted_sizes' => true, // NOT item_wanted_sizes
// ...
];
通缉大小相同:
// src/Model/Entity/WantedSize.php
// @property AppModelEntityItem[] $items // NOT item_wanted_sizes
protected $_accessible = [
// ...
'items' => true, // NOT item_wanted_sizes
// ...
];
现在我们看到了我们的模型,让我们跳转添加(或编辑)操作视图。正确设置关联后,我只需要执行以下操作:
// src/templates/Items/add.php
echo $this->Form->control('wanted_sizes._ids', ['options' => $wantedSizes]);
我甚至不需要告诉 FormHelper 这是一个多选,因为它在表配置中。
生成的 HTML 与您的完全不同(就像上面也回答了 404):
<select name="wanted_sizes[_ids][]" multiple="multiple" id="wanted-sizes-ids">
<option value="1">Some wanted size...</option>
<!-- ... -->
</select>
这对我来说效果很好,将数据保存在 mysql 的关系表中。
在 Cakephp4 中,有一件事要检查。如果实体在保存之前修补后未显示关联数据。可以通过在控制器中修补实体后转储实体来进行测试。相关数据应显示在此处。
$discount = $this->Discounts->patchEntity($discount, $this->request->getData());
dd($discount);
检查实体。关联的数据是否在 $_accessible 数组中?您更新的字段需要在此数组中,但关联的模型/表中也需要。
class Discount extends Entity
{
protected $_accessible = [
...
'products' => true,
...
];
}
https://api.cakephp.org/4.0/class-Cake.ORM.Entity.html#$_accessible