CakePHP 4 - 如何验证需要将数据保存到多个表的表单

如何解决CakePHP 4 - 如何验证需要将数据保存到多个表的表单

如果之前有人问过这个问题,我们深表歉意。我能找到的所有示例都是旧的或适用于 CakePHP 的旧版本,例如cakephp: saving to multiple models using one form 7 岁。

我在 CakePHP 4.1.6 中有一个应用程序。数据库中的两个表称为 tbl_userstbl_orgs(本例中的“orgs”表示“Organisations”)。

当我添加一个组织时,我还想创建一个用户,该用户是该组织内的主要联系人。这涉及在提交表单时保存到 tbl_orgstbl_users 表。

我遇到的问题是如何让表单在提交时运行 tbl_userstbl_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 指定的规则。例如,如果我输入相同的公司名称两次,它会在线显示相应的错误:

enter image description here

然后我尝试为 TblUsers 引入字段。我用点符号作为表单字段的前缀,例如这旨在对应于 tbl_users.email 输入字段:

<?= $this->Form->control('TblUser.email') ?>

检查 HTML 时,它与 TblOrgs 不同。例如,maxlengthrequired 之类的东西不存在。它实际上不知道 TblUsers。我知道我的 Controller 方法中的 $org 正在为 TblOrgs 而不是 TblUsers 指定一个新实体。我查看了 Saving With Associations 上的 CakePHP 文档,它说

save() 方法还能够为关联创建新记录

但是,在文档中,它给出了示例:

$firstComment = $articlesTable->Comments->newEmptyEntity();
// ...
$tag2 = $articlesTable->Tags->newEmptyEntity();

在这种情况下,TagsComments 是不同的模型,但 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 是因为:

  1. 表名被转换为小写下划线格式。
  2. 这是一个从组织到用户的 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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-