如何解决CakePHP 4 - 如何验证需要将数据保存到多个表的表单
如果之前有人问过这个问题,我们深表歉意。我能找到的所有示例都是旧的或适用于 CakePHP 的旧版本,例如cakephp: saving to multiple models using one form 7 岁。
我在 CakePHP 4.1.6 中有一个应用程序。数据库中的两个表称为 tbl_users
和 tbl_orgs
(本例中的“orgs”表示“Organisations”)。
当我添加一个组织时,我还想创建一个用户,该用户是该组织内的主要联系人。这涉及在提交表单时保存到 tbl_orgs
和 tbl_users
表。
我遇到的问题是如何让表单在提交时运行 tbl_users
和 tbl_orgs
的验证规则。
我们的应用程序目前的结构如下:
在add()
中有一个名为src/Controller/TblOrgsController.php
的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
,但我们在 buildRules()
中也有一个 TblUsers
,它在 email
字段上应用了类似的逻辑,以确保所有电子邮件地址对于每个用户。
在 add()
控制器方法中,我们首先为 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 并查看它们是否遵循相应的模型。这很清楚,因为 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
指定一个新实体。我查看了 Saving With Associations 上的 CakePHP 文档,它说
save()
方法还能够为关联创建新记录
但是,在文档中,它给出了示例:
$firstComment = $articlesTable->Comments->newEmptyEntity();
// ...
$tag2 = $articlesTable->Tags->newEmptyEntity();
在这种情况下,Tags
与 Comments
是不同的模型,但 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',]);
}
解决方法
好的,这里有很多混乱需要澄清。 :-) 我的假设是,根据您所写的内容,您正在尝试使用单个表单来添加一个新组织和其中的第一个用户,然后也许稍后您会向其中添加更多用户组织。
首先,$this->TblOrgs->TblUsers
是你的 users 表对象,所以当你使用
$org = $this->TblOrgs->TblUsers->newEmptyEntity();
您正在做的是创建一个新的用户实体。您通过 orgs 表到达该表对象并调用它 $org
的事实不会改变这一点。它不会以某种方式神奇地创建一个空白的组织实体,其中包含一个空白的用户实体。但是这里根本不需要实体结构,只需要空的组织实体。回到简单:
$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
是因为:
- 表名被转换为小写下划线格式。
- 这是一个从组织到用户的 hasMany 关系,所以它需要一个用户数组;我们必须为该数组提供一个数字索引,0 是一个很好的起点。如果您要同时添加第二个用户,则该字段将为
tbl_users.1.email
。
注意:找出表单助手希望您以何种格式创建字段名称的一个好方法是从数据库(在本例中为组织及其用户)读取现有记录集,然后只需转储该数据,例如debug($org);
。您会看到 $org
有一个名为 tbl_users
的属性,它是一个数组,它将直接指向我上面描述的这个结构。
通过像这样设置字段,您应该能够将结果数据直接修补到您的控制器中的 $org
实体中,并且无需任何其他工作即可将其保存。该补丁将创建整个结构,具有类 TblOrg
的实体,具有 tbl_users
属性,该属性是一个包含类 TblUser
的单个实体的数组,并且验证将在两个都。 (至少它应该;你可以使用上面提到的 debug($org);
来确认它。)当你保存这个实体时,它会首先保存 TblOrg
实体,然后将该新 ID 添加到 {{ 1}} 实体,然后再保存它,并检查两者的规则,并确保如果无法全部保存,则不会将任何内容保存到数据库中。只需一次 TblUser
调用,这一切都会自动发生!
如果您的关联是hasOne 或belongsTo 关系(例如,如果您要添加新用户以及他们所在的组织,而不是相反),您可以转储样本save
,并看到它有一个名为 $user
的属性,它只是一个直接实体,而不是实体数组,并注意 tbl_org
现在是单数,因为它只是一个实体而不是一堆.在这种情况下,要使用的字段名称将是 tbl_org
,其中根本没有数组索引。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。