[英]CakePHP 4 - how to validate forms that need to save data to multiple tables
抱歉,如果以前有人问过这个问题。 我能找到的所有示例都是旧的或适用于 CakePHP 的旧版本,例如cakephp:使用一种形式保存到多个模型已有7 年的历史。
我在 CakePHP 4.1.6 中有一个应用程序。 数据库中的两个表称为tbl_users
和tbl_orgs
(在这种情况下,“orgs”表示“组织”)。
当我添加一个组织时,我还想创建一个作为组织内主要联系人的用户。 这涉及在提交表单时保存到tbl_orgs
和tbl_users
表。
我遇到的问题是如何让tbl_orgs
在提交时运行tbl_users
和tbl_orgs的验证规则。
这就是我们的应用程序当前的结构:
在src/Controller/TblOrgsController.php
中有一个名为add()
的 Controller 方法。 这是由bake
生成的,最初用于将新组织插入tbl_orgs
表中。 此时它在tbl_users
方面没有做任何事情,但它在保存新组织和运行适当的验证规则方面起作用。
一个验证规则是companyname
中的每条公司名称记录tbl_orgs
必须是唯一的。 如果您尝试插入超过 1 个名称为“My Company Limited”的公司,则会出现验证错误“该公司名称已存在”:
// src/Model/Table/TblOrgsTable.php
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add(
$rules->isUnique(['companyname']),
[
'errorField' => 'companyname',
'message' => 'This company name already exists',
]
);
return $rules;
}
虽然上述适用于TblOrgs
,但我们在 TblUsers 中也有一个buildRules()
,它在TblUsers
字段上应用类似的逻辑, email
确保所有 email 地址对于每个用户都是唯一的。
在add()
Controller 方法中,我们首先为TblOrgs
指定一个新的空实体:
// src/Controller/TblOrgsController.php
public function add()
{
$org = $this->TblOrgs->newEmptyEntity();
// ...
$this->set(compact('org'));
}
创建表单时,我们传递$org
:
// templates/TblOrgs/add.php
<?= $this->Form->create($org) ?>
<?= $this->Form->control('companyname') ?>
<?= $this->Form->end() ?>
当浏览器呈现TblOrgs
字段时,我们可以检查 HTML 并查看这些字段是否遵循相应的 Model。 这很清楚,因为诸如required="required"
和maxlength="100"
类的内容对应于字段不允许为空并且是数据库中的VARCHAR(100)
字段的事实:
<input type="text" name="companyname" required="required" id="companyname" maxlength="100">
它也适用于buildRules
的TblOrgs
中指定的规则。 例如,如果我两次输入相同的公司名称,它会显示相应的在线错误:
然后我尝试为TblUsers
引入字段。 我在表单字段前面加上点符号,例如,这旨在对应于tbl_users.email
输入字段:
<?= $this->Form->control('TblUser.email') ?>
检查 HTML 时,它的作用与TblOrgs
。 例如,不存在诸如maxlength
或required
之类的内容。 它实际上不知道TblUsers
。 我知道我的 Controller 方法中的$org
是为TblOrgs
而不是TblUsers
指定一个新实体。 我查看了有关通过关联保存的 CakePHP 文档,其中说
save()
方法还能够为关联创建新记录
但是,在文档中给出的示例:
$firstComment = $articlesTable->Comments->newEmptyEntity();
// ...
$tag2 = $articlesTable->Tags->newEmptyEntity();
在这种情况下, Tags
与Comments
是不同的 Model,但newEmtpyEntity()
对两者都有效。 考虑到这一点,我add()
方法调整为:
$org = $this->TblOrgs->TblUsers->newEmptyEntity();
但这现在为TblUsers
提供了一个实体。 似乎你可以有一个或另一个,但不能同时拥有。
这对我的用例不起作用的原因是我可以为TblOrgs
(但不是TblUsers
)运行我的验证规则,反之亦然。
您如何设置它以运行两个模型的验证规则? 一个表单可能需要将数据保存到多个表中,并且您希望每个表的验证规则都运行,这似乎不是一个不合理的要求。 我从文档中得到的印象是它是可能的,但不清楚如何。
作为参考,这两个表之间存在适当的关系:
// src/Model/Table/TblOrgsTable.php
public function initialize(array $config): void
{
$this->hasMany('TblUsers', [
'foreignKey' => 'o_id',
'joinType' => 'INNER',
]);
}
和
// src/Model/Table/TblUsersTable.php
public function initialize(array $config): void
{
$this->belongsTo('TblOrgs', [
'foreignKey' => 'o_id',
'joinType' => 'INNER',
]);
}
好的,这里有很多混乱需要清理。 :-) 根据您所写的内容,我在这里的假设是您正在尝试使用单个表单来添加一个新组织以及其中的第一个用户,然后也许稍后您会添加更多用户组织。
首先, $this->TblOrgs->TblUsers
是你的用户表 object,所以当你使用
$org = $this->TblOrgs->TblUsers->newEmptyEntity();
您正在做的是创建一个新的用户实体。 您通过 orgs 表到达该表 object 并且您将其称为$org
的事实并没有改变这一点。 它不会以某种方式神奇地创建一个包含空白用户实体的空白组织实体。 但是这里根本不需要那个实体结构,只需要空的 org 实体。 Go 回到简单的:
$org = $this->TblOrgs->newEmptyEntity();
现在,在你的表单中,你会想要这样的东西:
<?= $this->Form->create($org) ?>
<?= $this->Form->control('companyname') ?>
<?= $this->Form->control('tbl_users.0.email') ?>
<?= $this->Form->end() ?>
该字段称为tbl_users.0.email
,因为:
tbl_users.1.email
。 注意:确定表单助手希望您以何种格式创建字段名称的一个好方法是从数据库中读取现有记录集(在本例中为组织及其用户),然后将其转储数据,类似于debug($org);
. 您会看到$org
有一个名为tbl_users
的属性,它是一个数组,它将直接指向我上面描述的这个结构。
像这样设置字段后,您应该能够将结果数据直接修补到 controller 中的$org
实体中,并且无需任何其他工作即可保存。 该补丁将创建整个结构,实体为 class TblOrg
,其tbl_users
属性是一个包含单个实体 class TblUser
的数组,并且已经对它们进行了验证。 (至少应该;您可以使用debug($org);
如上所述来确认。)并且当您保存此实体时,它将首先保存TblOrg
实体,然后在保存之前将该新 ID 添加到TblUser
实体中,以及检查两者的规则,并确保如果不能全部保存,则不会将任何内容保存到数据库中。 只需一次save
调用,这一切都会为您自动发生!
如果您的关联是 hasOne 或 belongsTo 关系(例如,如果您要添加一个新用户以及他们所在的组织,而不是相反),您可以转储一个示例$user
,并查看它有一个名为tbl_org
的属性,它只是一个直接实体,而不是一组实体,请注意tbl_org
现在是单数的,因为它只是一个实体而不是一堆实体。 在这种情况下,要使用的字段名称将是tbl_org.companyname
,其中根本没有数组索引。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.