锂和验证复杂的形式输入 - 如何?

时间:2012-03-29 19:41:25

标签: php validation cakephp model-view-controller lithium

我做了很多Lithium教程(如果他们帮助别人,下面的链接,以及表明我完成了我的作业:)我了解创建模型,视图,控制器和使用的最基本部分MVC根据表单输入创建数据库记录。

但是,我是MVC for webapps和Lithium的新手,我不确定如何在更复杂的情况下编写代码。这是一个普遍的问题,但我有两个具体的验证问题:

  • 如何验证表单提交的日期数据?
  • 如何检查两个用户的电子邮件字段是否具有相同的值?

我非常感谢对这些问题的任何帮助,这样的具体例子也将真正帮助我理解如何在其他情况下做好MVC编码!

日期条目 - 验证跨多个表单输入分割的数据

出于用户界面原因,注册表单会要求用户在三个字段中输入他们的DOB:

<?=$this->form->field('birthday', array('type' => 'select', 'list' => array(/*...*/))); ?>
<?=$this->form->field('birthmonth', array('type' => 'select', 'list' => array(/*...*/))); ?>
<?=$this->form->field('birthyear', array('type' => 'select', 'list' => array(/*...*/))); ?>

验证此服务器端的最佳方法是什么?我想我应该利用自动验证,但我不确定对于一组不属于模型的变量的最佳方法。 E.g:

  • 我应该在$this->request->data中对UsersController进行后期处理吗?例如。在将$this->request->data传递给UsersController之前修改Users::create内的$this->request->data
  • 我应该从Validator::isDate中提取表单字段并使用UsersController内的Users::create静态调用吗?
  • 有没有办法在模型中为不属于模型的表单变量组合编写验证规则?
  • 我应该覆盖<?=$this->form->field('email_address'); ?> <?=$this->form->field('verify_email_address'); ?> 并在那里进行所有额外的验证和后期处理吗?

所有这些似乎都可以起作用,虽然有些看起来有点难看,但我不知道哪些可能会给我带来重大问题。

[编辑:与此密切相关的是将三个表格字段合并为一个字段以保存在模型中的问题]

电子邮件条目 - 检查两个表单字段是相同的,但只存储一个

对于常识/常规做法,注册表单会要求用户指定两次电子邮件地址:

{{1}}

如何编写一个自动验证规则来检查这两个表单字段是否具有相同的值,但只将email_address保存到数据库中?

这感觉就像上面几乎一样的问题,因为我能想到的可能答案清单是一样的 - 所以我提交这个作为一个问题,但我真的很感谢你的帮助这两个部分,因为我认为这个部分的解决方案将是微妙的,不同的,同样具有启发性!

[编辑:与此密切相关的是不将verify_email_address存储到我的模型和数据库中的问题]

关于Lithium的一些背景知识

我已经读过其他人了,但这三个教程让我了解了我与用户的关系并立即注册表单...

关于密切相关主题的其他一些StackOverflow问题(但没有回答它,也没有回答特定于锂的问题)

  • this question的一个答案建议创建一个单独的控制器(和模型......?) - 它对我来说感觉不太“锂”,我担心它可能很脆弱/很容易出错以及
  • This wonderful story说服我,我担心把它放在控制器中是对的,但我不确定什么是好的解决方案
  • This one on views让我觉得我应该以某种方式将它放在模型中,但我不知道在Lithium中执行此操作的最佳方法(请参阅上面日期条目下的项目符号列表)
  • 并且this Scribd演示文稿询问了我希望在最后一页回答的问题......于是它停止了而没有回答它!

注意:CakePHP风格的答案也很好。我不知道,但它很相似,如果我需要,我相信我可以从中翻译出来!

1 个答案:

答案 0 :(得分:4)

我建议在Model而不是Controller中执行此操作 - 无论您从哪里进行保存,都会发生这种情况。

对于日期字段问题,在模型中,覆盖save()方法并处理将数据中的多个字段转换为一个日期字段,然后再调用parent::save进行实际保存。任何高级操作都可能发生在那里。

使用隐藏表单字段获取错误消息以显示声音的评论中描述的技术非常好。

为了比较两个电子邮件字段是否相等,我建议您定义一个自定义验证器。您可以使用Validator::add在引导程序中执行此操作。

use lithium\util\Validator;
use InvalidArgumentException;

Validator::add('match', function($value, $format = null, array $options = array()) {
    $options += array(
        'against' => '',
        'values' => array()
    );
    extract($options);
    if (array_key_exists($against, $values)) {
        return $values[$against] == $value;
    }
    return false;
});

然后在你的模型中:

public $validates = array(
    "email" => array(
        "match",
        "message" => "Please re-type your email address.",
        "against" => "email2"
    )
);

编辑:根据评论,这是在控制器中执行自定义规则验证的方法:

public function save() {
    $entity = MyModel::create($this->request->data);
    $rules = array(
        "email" => array(
            "match",
            "message" => "Please re-type your email address.",
            "against" => "email2"
        )
    );

    if (!$entity->validates($rules)) {
        return compact('entity');
    }

    // if your model defines a `$_schema` and sets `$_meta = array('locked' => true)`
    // then any fields not in the schema will not be saved to the db

    // here's another way using the `'whitelist'` param
    $blacklist = array('email2', 'some', 'other', 'fields');
    $whitelist = array_keys($entity->data());
    $whitelist = array_diff($whitelist, $blacklist);

    if ($entity->save(null, compact('whitelist'))) {
        $this->redirect(
            array("Controller::view", "args" => array($entity->_id)),
            array('exit' => true)
        );
    }

    return compact('entity');
}

将数据设置为实体的一个优点是,如果出现验证错误,它将自动预填到表单中。