我有一个用户可以登录的程序。我有一个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方法,但我真的不喜欢这个,因为我觉得实际设置的任何属性应该是有效的,它仍然没有完全摆脱问题。
我可以使用哪些其他方法来允许用户设置对象属性并验证它们,将任何错误发送回用户?或者我现在正在做什么?
答案 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>