2016-02-13 73 views
9

假设我有这个模型。 (我为示范的目的,很简单的。)PHP中需要与数据库交互的模型验证

class User 
{ 
    public $id; 
    public $email; 
    public $password; 
    public $errors = []; 

    public function isValid() 
    { 
     if (strpos($this->email, '@') === false) { 
      $this->errors['email'] = 'Please enter an email address'; 
     } 
     // ... 

     return !$this->errors; 
    } 
} 

而且让我们说我有这样的DAO检索,添加,更新和删除用户。

class UserDAO 
{ 
    public function getUsers() { ... } 

    public function getUserById($id) { ... } 

    public function addUser(User $user) { ... } 

    public function updateUser(User $user) { ... } 

    public function deleteUser($id) { ... } 

    public function isEmailUnique($email) { ... } 
} 

当我处理一个形式,我通常做的是这样的:

$userDAO = new UserDAO(); 
$user = new User(); 
$user->email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); 
$user->password = filter_input(INPUT_POST, 'password'); 

if ($user->isValid()) { 
    if ($userDAO->addUser($user)) { 
     // ... 
    } else { 
     // ... 
    } 
} else { 
    // do something with $user->errors 
} 

现在,让我们说我的用户验证的一部分应该是检查邮件是否是独一无二的,我怎么使它用户模型的一部分?因此,当调用$user->isValid()时,它还会检查电子邮件是否唯一?还是我这样做全错了?

由于我对DAO的理解很差,DAO负责与数据库的所有交互。那么我如何使模型与数据库一起工作呢?

+1

的S.Lott的回答可能帮助你http://stackoverflow.com/a/198032/3904215。这里来自tutorialspoint的描述:http://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm – AMartinNo1

回答

2

我的建议是这样的:在验证User型号时不要考虑电子邮件地址的唯一性。唯一性是UserDAO问题,而不是User问题。

如果User可以验证自己,它应该能够孤立地这样做;其验证不应该涉及任何外部交互。

无论电子邮件地址是否唯一,唯一重要的时刻就是在您尝试将其插入数据库时​​。考虑到多个并发用户的可能性,理论上可能用于验证地址的唯一性,并在尝试插入地址时使其不再是唯一的。

我认为最直接和最可靠的方法是在数据库的电子邮件地址上添加一个唯一的约束,然后在您的addUser()方法中,只需要try即可添加它。如果你的数据库告诉你它不是唯一的,那么你知道它不是唯一的。您事先不知道真的

+0

谢谢。你是对的。我过分复杂化了我的设计。 – Mikey

1

我认为在这种情况下,验证是应用程序逻辑的一部分,因为您需要未存储在模型中的数据。所以在不同的控制器功能中实现验证逻辑会更好。

而且,与类似的答案已经是一个类似的问题:Best Place for Validation in Model/View/Controller Model?

0

的UserDAO的类必须实现的方法称为userExists。此方法仅检查电子邮件地址是否已存在。它在BD中检查它,所以它的位置在UserDAO类中。它必须是私有方法,并且addUser使用它返回正确的值或false/null

1

保留User类,因为它本身就是一个很好的公民。

我会做的方法isEmailUnique私人(IFF它只是用于那些)并通过与内addUser该电子邮件中的User存在检查。另一方面,这将把逻辑的责任推到DAO(参见:Responsibilities and use of Service and DAO Layers

所以,如果你改变isValid行为检查,如果用户已经在数据库中,你会打破你的设计。

0

我认为你可以使用DAO作为验证函数的参数。

public function isValid($dao) 
{ 
    if (strpos($this->email, '@') === false) { 
     $this->errors['email'] = 'Please enter an email address'; 
    } 
    if ($dao->isEmailUnique($this->email) === false) { 
     $this->errors['email'] = 'Email address should be unique'; 
    } 
    // ... 

    return !$this->errors; 
} 

但是可能更好的方法是在你的用户模型中使用DAO。添加到模型私有变量$ dao并在构造函数中初始化它。并在模型类中实现所有用于添加/编辑/删除操作的方法。去这个

1

的一种方式,是去除方法用户::完全IsValid的,有利于传递给它的构造函数中需要的一切,从那里运行验证:

class User 
{ 
    public function __construct($email) { 
     if (strpos($email, '@') === false) { 
      throw new \InvalidArgumentException("Invalid email"); 
     } 

     $this->email = $email; 
    } 
} 

如果您认为关于它,是什么让用户有效?如果这是一个有效的电子邮件地址,请确保在构建User对象时传入一个。这使您的用户对象始终有效。

确保这方面的一个更好的办法,是使用的ValueObject它封装了这个验证逻辑,因此您可以在其他对象使用它,避免了大量的冗余和样板代码:

class Email 
{ 
    public function __construct($email) 
    { 
     if (strpos($email, '@') === false) { 
      throw new \InvalidArgumentException("Invalid email"); 
     } 

     $this->email = $email; 
    } 
} 

class User 
{ 
    public function __construct(Email $email) 
    { 
     $this->email = $email; 
    } 
} 

class ProspectiveUser 
{ 
    public function __construct(Email $email) 
    { 
     $this->email = $email; 
    } 
} 

现在,在条款用数据库验证用户,您可以完美地将其封装在您的DAO中。 DAO可以执行检查以确保用户不在数据库中,保持DAO使用者不知道它,除非它应该知道如何处理当用户已经存在于数据库中时发生错误的情况:

class UserDAO 
{ 
    public function recordNewUser(User $user) 
    { 
     if ($this->userExists()) { 
      throw new UserAlreadyExistsException(); 
     } 

     $this->persist($user); 
     $this->flush($user); 
    } 

    private function userExists(User $user) 
    { 
     $user = $this->findBy(['email' => $user->getEmail()]); 

     return !is_null($user); 
    } 
} 

正如你所看到的,DAO为您提供了保存新的用户界面,但如果电子邮件唯一的限制是不信纳操作可能会失败。

1

我会从User类中取出所有验证问题,并转到Controller层(可以调用UserDAO来检查电子邮件唯一性)。最好是保持User类简单地作为一个实体类,并把所有其他的东西在其他类别 - 否则它会不断地成长到不是维护了:)

检查也是国家:https://en.wikipedia.org/wiki/Single_responsibility_principle