CakePHP 4 - 保存关联数据不适用于 3 个简单表格

CakePHP 4 - saving associated data not working with 3 simple tables

我在 CakePHP 4 中有一个应用程序,但在保存相关模型数据时遇到问题。我已阅读 Cake 文档中的 Saving Associated Data,但非常不清楚这如何适用,尤其是在我的用例中。

该应用程序有 3 个 table 与此问题相关:

该应用程序允许用户请求服装项目 (items) 和 input/save 此类项目的表单具有不同尺寸的下拉列表 (sizes)。每个尺寸都有一个唯一的 ID。用户在保存项目时可以 select 一个或多个 大小。 items_sizes_wanted table 应该根据用户 select 编辑的大小和相应的项目 ID 保存一行(或多行)。例如,如果他们为项目 999 保存尺寸 2、3 和 4,则此 table:

中将有 3 行
size_id | item_id
--------|---------
2       | 999
3       | 999
4       | 999

代码已经烘焙,Table classes 中的关联看起来没问题:

// 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',
        ]);
    }

该项目的实体 class 看起来也不错:

// src/Model/Entity/Item.php

// @property \App\Model\Entity\ItemsSizesWanted[] $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 均具有有效 ID,即来自 sizes table.

的 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 table 和闪烁的成功消息“您的项目已列出。”显示在浏览器中。

但是 - 没有数据写入 items_sizes_wanted

我不确定这是为什么。链接的文档没有具体说明应该如何使用 Form Helper,所以我假设我对该表单字段的语法是正确的,但它可能不是。

如果我在控制器中使用 debug($item); die; 按下保存后调试实体,它有 'items_sizes_wanted' => [ ],即使我使用表单 select 编辑了多个尺寸选项。

请有人帮忙,因为我不知道这里出了什么问题?

免责声明:我不太了解 CakePHP,但我想我知道解决方案,或者至少可以为您指明正确的方向。

您只获得一个选定大小而不是多个的原因是因为生成的输入字段被命名为 items_sizes_wanted[size_id],但是,为了 PHP 将多个值解析为一个数组,它们需要被命名为 items_sizes_wanted[size_id][]。当请求参数以[]结尾时,那么PHP会正确解析所有请求属性到一个数组中。

例如:这是包含 items_sizes_wanted[size_id][]=A&items_sizes_wanted[size_id][]=B&items_sizes_wanted[size_id][]=C

的 POST 正文的请求的 var_dump($_POST);
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)

这是我对Cake不太熟悉的部分PHP。我查看了代码 对于 CakePHP's FormHelper, and based on the template code,我认为您需要将 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'
    ]
) 
?>

基于the __call() method in FormHelper,你可能也可以这样写:

$this->Form->selectMultiple(
    'items_sizes_wanted.size_id', 
    [
        'options' => $sizes, 
        'multiple' => 'multiple'
    ]
);

但是,我不熟悉创建 control($fieldName, $options)inputType($fieldName, $options) 之间的细微差别,因此它们可能会产生不同的输出。

我目前正在 CakePHP4.x 中的一个项目中工作。我的项目也有很多关联,它在 table 中保存正常,但 CakePHP 烘焙它与你的完全不同。让我告诉你不同之处,也许它有一些帮助。

我会将我的实体名称、tables 等“翻译”为您问题中使用的名称,好吗?

首先,简而言之:在我的项目中,蛋糕没有为关系table烘焙模型(实体和table)。关系 table 没有自己的模型,仅在 ItemsTableWantedSizesTableinitialize 方法中引用。 ItemWantedSize 实体以及视图也有细微的变化。

其次,您的实体名称不符合Cake的命名约定,这会导致很多问题。这甚至可能是您现在遇到的问题的原因。我更改了一些名称以符合它们,但我建议您仔细阅读它:https://book.cakephp.org/4/en/intro/conventions.html.

第三个更重要的,让我们开始吧。

我的多对多关系 mySQL table 没有自己的 Table 模型。我的 SQL 确实有 items_wanted_sizes table,但是 CakePHP 项目 NOT 有对应的模型 ItemsWantedSizesTable 也不 ItemsWantedSizes。它确实有 ItemsTableWantedSizesTable table 和 ItemWantedSize 实体,仅此而已。

让我们看看 Table 模型。 关系 mySQL table items_wanted_sizes 仅在 tables initialize 两个 table 模型在 PHP 中的方法,像这样:

// 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',
    ]);
}

WantedSizes 也是如此Table:

// 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',
    ]);
}

关于实体模型,我也没有关系实体模型。 ItemWantedSize 实体模型相互引用,但是, 与您的情况相反,它们不引用关系 table (仅相互引用) :

// src/Model/Entity/Item.php

// @property \App\Model\Entity\WantedSize[] $wanted_sizes // NOT item_wanted_sizes

protected $_accessible = [
    // ...
        'wanted_sizes' => true, // NOT item_wanted_sizes
    // ...
];

WantedSize 相同:

// src/Model/Entity/WantedSize.php

// @property \App\Model\Entity\Item[] $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 它是多选的,因为它在 table 配置中。

生成的HTML和你的大不一样(比如404上面也回答了):

<select name="wanted_sizes[_ids][]" multiple="multiple" id="wanted-sizes-ids">
<option value="1">Some wanted size...</option>
<!-- ... -->
</select>

这对我来说非常好,将数据保存在 mysql 中的关系 table 中。