【问题标题】:Get validation errors from associated model in Form从表单中的关联模型获取验证错误
【发布时间】:2014-11-21 17:16:46
【问题描述】:

我有 2 个模型 Owner 和 User 关联为 User BelongsTo Owner。

我只有一个表格来收集两个模型/表格的数据。

表单布局在下面的 add.ctp 中声明:

<div class="container-fluid">
    <div class="row">
        <div class="col-md-9">
            <h1><?= __("Création d'un nouveau propriétaire") ?></h1>
            <p>&nbsp;</p>
            <div class="col-md-19 col-md-offset-2 container-fluid well">
                <?= $this->Form->create($owner, array('novalidate' => true)) ?>
                    <fieldset>
                        <div class="form-group">
                            <?= $this->Form->input('username', array('label' => __("Nom d'utilisateur :"), 'class' => 'form-control', 'placeholder' => __("Nom d'utilisateur"), 'autocomplete' => 'off')); ?>
                        </div>
                        <div class="form-group">
                            <?=  $this->Form->input('password', array('label' => __("Mot de passe :"), 'class' => 'form-control', 'placeholder' => __("Mot de passe"))); ?>
                        </div>

                        // I cut some fields but username and password belong to User, postcode and city belong to Owner

                        <div class="form-group">
                            <?= $this->Form->input('postcode', array('label' => __("Code postal :"), 'class' => 'form-control', 'placeholder' => __("Code postal"))); ?>
                        </div>
                        <div class="form-group">
                            <?= $this->Form->input('city', array('label' => __("Ville :"), 'class' => 'form-control', 'placeholder' => __("ville"))); ?>
                        </div>

                    </fieldset>
                <?= $this->Form->button(__('Enregistrer')) ?>
                <?= $this->Form->end() ?>
            </div>
        </div>
    </div>
</div>

此表单由 OwnersController.php 控制

public function add() {

    $this->loadModel('Users');

    $user = $this->Users->newEntity($this->request->data);
    $owner = $this->Owners->newEntity($this->request->data);

    $addDatas = [ 
            'owner_id' => 'id',
            'email'     => $owner['email'],
            'role'  => 'owner',
            'token'     => md5(time() . '-' . uniqid()),
    ];

    $user = $this->Users->patchEntity($user, $addDatas);

    $owner->users = [$user];

    if ($this->request->is('post')) {
        if ($this->Owners->validate($owner)) {
            if ($this->Owners->save($owner)) {
                $user = $this->Users->get($owner->users[0]['id']);
                $email = new Email('gmail');
                $email->template('activationLink')
                    ->emailFormat('text')
                    ->to($owner['email'])
                        ->from('myemail@monsite.com')
                    ->subject(__('Votre inscription sur monsite.com'))
                    ->viewVars(['user' => $user])
                    ->send();

                $this->Flash->success(__("Merci de vous être enregistré. un email a été envoyé à {0} pour activer votre compte", $owner['email']));
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__("Impossible de vous enregistrer, veuillez corriger les erreurs"));
        } else {
            $this->Flash->error(__("Impossible de vous enregistrer, veuillez corriger les erreurs"));
        }
    }

    $this->set(compact('owner'));
}

一切正常,我可以输入我的数据,验证并神奇地保存所有者和用户的数据,这要归功于出色的 ORM。

但我对来自验证器的错误消息有疑问。

无论错误来自所有者的验证器还是用户的验证器,我都会在表单中看到我的 flash 消息告诉我有问题,但我只看到所有者数据的字段错误消息,而不是用户数据的错误消息。

如果我在提交空白表单后调试所有者,我可以看到:

object(App\Model\Entity\Owner) {

    'new' => true,
    'accessible' => [
        'email' => true,
        'phone' => true,
        'company' => true,
        'tva_number' => true,
        'address' => true,
        'postcode' => true,
        'city' => true,
        'users' => true
    ],
    'properties' => [
        'company' => '',
        'email' => '',
        'phone' => '',
        'address' => '',
        'postcode' => '',
        'city' => '',
        'users' => [
            (int) 0 => object(App\Model\Entity\User) {

                'new' => true,
                'accessible' => [
                    'username' => true,
                    'password' => true,
                    'first_name' => true,
                    'last_name' => true,
                    'email' => true,
                    'role' => true,
                    'owner' => true,
                    'sites_user' => true,
                    'active' => true,
                    'token' => true
                ],
                'properties' => [
                    'username' => '',
                    'password' => '$2y$10$EtGitvmw7CeIOJOeA4eNKuZBifd97f165O9I5gidpiC7p1NYRGsE.',
                    'first_name' => '',
                    'last_name' => '',
                    'email' => '',
                    'role' => 'owner',
                    'token' => '4f842f347e06aff9a3778199c9a8d20a'
                ],
                'dirty' => [
                    'username' => true,
                    'password' => true,
                    'first_name' => true,
                    'last_name' => true,
                    'email' => true,
                    'role' => true,
                    'token' => true
                ],
                'original' => [],
                'virtual' => [],
                'errors' => [
                    'username' => [
                        (int) 0 => 'Vous devez indiquer un nom d'utilisateur'
                    ],
                    'first_name' => [
                        (int) 0 => 'Vous devez indiquer votre prénom'
                    ],
                    'last_name' => [
                        (int) 0 => 'Vous devez indiquer votre nom'
                    ],
                    'email' => [
                        (int) 0 => 'This field cannot be left empty'
                    ]
                ],
                'repository' => 'Users'

            }
        ]
    ],
    'dirty' => [
        'company' => true,
        'email' => true,
        'phone' => true,
        'address' => true,
        'postcode' => true,
        'city' => true,
        'users' => true
    ],
    'original' => [],
    'virtual' => [],
    'errors' => [
        'email' => [
            (int) 0 => 'Vous devez saisir votre email'
        ],
        'phone' => [
            (int) 0 => 'Vous devez saisir votre téléphone'
        ],
        'address' => [
            (int) 0 => 'Vous devez saisir votre adresse'
        ],
        'postcode' => [
            (int) 0 => 'Vous devez saisir votre code postal'
        ],
        'city' => [
            (int) 0 => 'vous devez saisir votre ville'
        ]
    ],
    'repository' => 'Owners'

}

如您所见,该对象包含有关所有者和关联用户的错误,但由于我无法猜测的原因,cakePHP 似乎无法从用户那里获取错误。

所以我想知道它是否应该工作,如果我在某个地方犯了一个错误,可以解释它无法检索用户的错误。

【问题讨论】:

  • 这不是已经在 IRC 上解决了吗?您的字段名称缺少关联:book.cakephp.org/3.0/en/views/helpers/…
  • 不幸的是不是ndm。由于我不太确定了解该文档,因此在将用户添加到 $this->set(compact('owner', 'user')); 后,我尝试了 User.username、Users.username 和 $user.username;没有任何效果。但我敢肯定这里有一些我不明白的地方。
  • 事实上,我不明白为什么输入可以完美地工作(设置和获取值)而不给 input() 提供更多信息,而错误消息却没有。我想知道问题是否来自其他事物。

标签: cakephp cakephp-3.0


【解决方案1】:

如前所述,这里的主要问题是您的输入缺少适当的关联。


在实体之前检查请求数据

输入获取输入数据的事实(这可能是您根据 cmets 判断的困惑)与实体上设置或未设置的内容无关!

这是由于实体上下文的工作方式(上下文是位于表单助手和数据之间的层),在它尝试检查实体的数据之前,它会检查请求,这就是它选择的地方例如username 字段,如果它会在实体中检查它,那么它不会找到它,因为它会在Owner 实体上查找它,因为字段名称中缺少关联。

https://github.com/cakephp/cakephp/blob/3.0.0-beta3/src/View/Form/EntityContext.php#L201-L204


验证错误只会出现在实体上

因此,虽然您的输入可以获取数据,因为它以“错误”字段名存储在请求中,但验证错误只能在实体上找到,这就是您的代码最终出现问题的地方, Owner 实体没有 username 字段,但这是实体上下文查找错误的地方。

https://github.com/cakephp/cakephp/blob/3.0.0-beta3/src/View/Form/EntityContext.php#L468

它不会在那里找到任何东西,因为错误位于第一个User实体中,存储在Owner实体users属性(Owner-&gt;users[0]-&gt;errors)中,所以为了能够接这些错误,你的字段名必须看起来像

users.0.username

它反映了实体中的结构。

http://book.cakephp.org/3.0/en/views/helpers/form.html#field-naming-conventions


关联模型只需要创建一个新的主实体

在您的控制器中,您正在创建一个 User 实体和一个 Owner 实体,这不是必需的,因为您的模型是关联的,newEntity() 调用 Owners 表将创建所有需要一个Owner 实体及其数据及其所有关联实体数据,即User 实体。

http://book.cakephp.org/3.0/en/orm/table-objects.html#converting-request-data-into-entities

如果您需要为User 实体设置其他数据,只需修补已创建并放置在Owner 实体users 属性中的实体,如下所示:

public function add() {
    $this->loadModel('Users');

    // Creates an entity with all necessary data
    $owner = $this->Owners->newEntity($this->request->data);

    if ($this->request->is('post')) {
        if(isset($owner->users[0])) {
            // Patch the existing (new) entity with some 
            // additional data
            $this->Users->patchEntity($owner->users[0], [
                'email' => $owner->email,
                'role'  => 'owner',
                'token' => md5(time() . '-' . uniqid()),
            ]);
        } else {
            // Bail out here or check for the existence of
            // a user using validation.
            //
            // You should have some kind of validation somewhere
            // in order to make sure that a user (only 
            // one (1)) was submitted, and the controller
            // mostly isn't really the right place for such logic
        }

        if ($this->Owners->save($owner)) {
            $this->Flash->success(__("Merci de vous être enregistré. un email a été envoyé à {0} pour activer votre compte", $owner['email']));
            return $this->redirect(['action' => 'index']);
        } else {
            $this->Flash->error(__("Impossible de vous enregistrer, veuillez corriger les erreurs"));
        }
    }

    $this->set(compact('owner'));
}

请注意,我已从示例中删除了 validate()get() 调用,不一定需要显式调用验证,因为根据您的 cmets,您可能不需要它,如果需要,您现在需要,并且如果您愿意,那么您将禁用由save() 调用触发的验证,因为不需要验证两次。

http://book.cakephp.org/3.0/en/orm/table-objects.html#validating-entities

该示例应该可以正常运行,但请确保您已阅读有关 how to avoid mass assigment attacks! 的部分!

【讨论】:

  • 非常感谢您提供如此完整且易于理解的答案。您的代码一切正常。只需修复一点: $user = $this->User->get() - 没有 s 的用户,即实体。无论如何,我可能不了解patchEntity的一件事。事实上,我的数据并没有丢失。所有都正确记录在数据库中。而且看起来 patchEntity 只是将其他数据与现有数据合并,那么为什么已经存在的数据应该消失呢? validate() 调用真的有必要吗?
  • @caBBAlainB 对不起,我把事情搞混了,所描述的行为当然只适用于已经持久化的实体,在新实体上修补会将所有字段标记为脏。 validate() 调用不一定是必要的,不,除非明确禁用,否则在保存时将调用验证,我只是复制了您的代码而没有考虑太多,get() 调用也是如此。我会更新我的答案。
  • 完美的ndm,非常感谢。
【解决方案2】:

您可以使用 set 方法轻松地将错误踢到视图中

if ($Models->errors()) {
$errors=$Models->errors();
 $this->set(compact('errors'));

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    • 2021-04-10
    • 2011-05-02
    • 1970-01-01
    相关资源
    最近更新 更多