OOP表单验证,不使用多种返回类型

时间:2013-02-27 20:05:12

标签: php forms oop validation

我有一个用户可以登录的程序。我有一个User类,当他们登录时,他们的所有属性(权限,用户名,真实姓名等)都保存为类属性。但是,由于所有数据都需要验证,因此我无法理解用户更新他们自己的信息的最佳方式。

目前,当用户试图更新他们自己的信息时,例如他们的电子邮件地址,我这样做:

// Set new email
$try = $user->setEmail($_POST['new_email']);
if ($try !== true) {
    $errors[] = $try;
}
...
if (isset($errors) {
    // Tell user what went wrong
} else {
    $user->saveValuesToDB();
}

它运行正常,但对我来说似乎相当难看,因为User->setEmail()需要具有混合返回类型,因为电子邮件可能因多种原因而无效(找不到域,格式无效,提供空字符串,已被另一个用户使用,等等,确切的原因需要传递给用户,但据我所知,一般不鼓励混合回报。

我想到的另一种方法是更新所有属性而不执行任何类型的验证,然后在最后执行User->commitAllChanges(),这将执行所有验证,这将带来多个返回所有的setter方法,并将其简化为只有commit方法,但我真的不喜欢这个,因为我觉得实际设置的任何属性应该是有效的,它仍然没有完全摆脱问题。

我可以使用哪些其他方法来允许用户设置对象属性并验证它们,将任何错误发送回用户?或者我现在正在做什么?

5 个答案:

答案 0 :(得分:2)

  

我想到的另一种方法就是更新所有内容   属性没有执行任何类型的验证然后做   User-> commitAllChanges()在最后将执行所有   验证,将从所有的多个回报中取出   setter方法并将其简化为只提交方法

这通常是一种很好的方法。您还可以将整个过程分解为更小的部分,以使其更加通用。

例如,模型可能有一个validate方法返回一个布尔值(一切正常,或者是否有一个或多个错误?),以及一个返回数组的getValidationErrors方法保存您关心的所有信息(可能需要多维)。它可以有commit方法自动调用validate,只有在没有错误的情况下才会保留更改。

  

但我真的不喜欢这个因为我觉得任何属性   实际设置应该是有效的,它仍然没有完全得到   摆脱问题。

“不会被设置的属性”是IMO 方式比最初想象的更麻烦。主要的问题是,如果一个属性反对被设置(通过返回一些错误代码,或者更好,通过抛出异常),那么你必须用“保护性”代码包装所有访问权限 。这非常繁琐非常繁琐。

实际上,通过辅助方法大量为所有属性赋值,然后查看结果是什么(例如通过调用上面的validate方法),这样更方便。

另外,不要忘记在实践中,您希望在数据输入字段旁边显示验证错误,并且这些字段的连线要预先填充模型属性的值。因此,一方面,属性需要有效,另一方面,它们需要完全匹配用户输入的内容(否则用户将要杀死你)。因此,最好放宽“必须始终有效”的约束,转而使用实用程序 - 尽管现在随处可用AJAX表单和客户端验证,但这种关系变得不那么重要了。

答案 1 :(得分:2)

使用try-catch构造。

// somewhere in your project
class UserValidationException extends \Exception {}

// In your form handler, controller, or whatever you have
try {
    $user->setName($_POST['new_email']);
    $user->setEmail($_POST['new_name']);
    // etc.
}
catch (UserValidationException $e)
{
    // tell user what went wrong using $e->getMessage();
}

// In your User class
class User
{
    // ...
    public function setName($newName)
    {
        if (strlen($newName) < 2)
            throw new UserValidationException('User name is too short');
    }
}

答案 2 :(得分:1)

一种方法是保留您的解决方案(setEmail函数)并使其更加OOP。 在setAction中你可以设置一个错误列表

$this->errors[] = 'Domain invaild';

然后使用1或2个函数从类中获取错误

public function hasErrors() {return false == empty($this->errors);}
public function getErrors() {return $this->errors;}

答案 3 :(得分:1)

您当前的方法没有错,但您可以做的是使用例外。

看起来像是:

try
{
    $user->setEmail($_POST['new_email']);
}
catch (Exception $e)
{
    $errors[] = $e->getMessage();
}

你的setEmail方法类似于:

setEmail($email)
{
    if ($tooShort) // validate length
        throw new Exception("Email is too short"); // perhaps have custom exceptions
}

答案 4 :(得分:0)

我在这上面使用了一些MVC.NET方法。基本上抛出异常的不好就是性能,为什么你只想抓住一个呢?如果电子邮件和密码都无效,请让他们同时知道,而不是'电子邮件不正确' - 修复电子邮件 - 重新发布 - '哦密码也不正确可能可能早点告诉你但是嘿,你只能让我赚钱'

  

编辑:
  此外,由于验证返回false,因此不应抛出异常,我们希望无论是否有意(黑客)都会获得无效数据。但是让我们看看它如何处理大规模形式,观察处理速度有多慢。验证错误应该存储并显示给用户,它应该只导致我们的程序输入条件而不是异常。应该为编码错误保留例外,例如我们需要的资源不存在,有人删除了我们需要正确执行代码的库文件或框架,等等。


    <?php

    class ChangePasswordModel
    {

        protected $OldPassword;

        protected $NewPassword;

        protected $ConfirmPassword;

        protected $ValidationMessages;

        protected $DisplayNames;

        public function __construct()
        {
            $this->DisplayNames = array(
                "OldPassword"       => "Old password",
                "NewPassword"       => "New password",
                "ConfirmPassword"   => "Confirm new password",
            );
        }

        public function LoadPost()
        {
            if ( !isset( $_POST["OldPassword"] ) || !isset( $_POST["NewPassword"] ) || !isset( $_POST["ConfirmPassword"] ) )
                return;

            $this->OldPassword      = trim( $_POST["OldPassword"] );
            $this->NewPassword      = trim( $_POST["NewPassword"] );
            $this->ConfirmPassword  = trim( $_POST["ConfirmPassword"] );

            if ( strlen( $this->OldPassword ) < 1 )
                $this->ValidationMessages["OldPassword"] = "Old password is not set";
            if ( strlen( $this->NewPassword ) < 6 )
                $this->ValidationMessages["NewPassword"] = "Password must be at least 5 characters.";
            if ( $this->NewPassword != $this->ConfirmPassword )
                $this->ValidationMessages["ConfirmPassword"] = "Passwords do not match.";
        }

        public function ValidationMessageFor( $name )
        {
            if ( !isset( $this->ValidationMessages[$name] ) )
                return "";

            return $this->ValidationMessages[$name];
        }

        public function DisplayNameFor( $name )
        {
            // Throw exception if not set
            return $this->DisplayNames[$name];
        }

    }

    $Model = new ChangePasswordModel();
    $Model->LoadPost();

    ?>
    <form action="" method="post">
        <div>
            <?= $Model->DisplayNameFor( "OldPassword" ) ?>
        </div>
        <div>
            <input type="password" name="OldPassword" />
            <span><?= $Model->ValidationMessageFor( "OldPassword" ) ?></span>
        </div>

        <div>
            <?= $Model->DisplayNameFor( "NewPassword" ) ?>
        </div>
        <div>
            <input type="password" name="NewPassword" />
            <span><?= $Model->ValidationMessageFor( "NewPassword" ) ?></span>
        </div>

        <div>
            <?= $Model->DisplayNameFor( "ConfirmPassword" ) ?>
        </div>
        <div>
            <input type="password" name="ConfirmPassword" />
            <span><?= $Model->ValidationMessageFor( "ConfirmPassword" ) ?></span>
        </div>

        <p>
            <input type="submit" value="Change Password" />
        </p>
    </form>