PHP中的模型验证,需要与数据库进行交互

时间:2016-02-13 23:08:41

标签: php validation dao

我们说我有这个型号。 (我为演示目的而非常简单。)

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负责与数据库的所有交互。那么如何让模型从内部使用数据库?

7 个答案:

答案 0 :(得分:2)

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

如果User可以自我验证,它应该能够孤立地进行验证;其验证不应与任何外部互动有关。

在您尝试将电子邮件地址插入数据库的瞬间,唯一重要的是电子邮件地址是否唯一。考虑到多个并发用户的可能性,理论上可以验证地址的唯一性,并且在您尝试插入地址时不再具有唯一性。

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

答案 1 :(得分:1)

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

此外,类似的答案也存在类似的问题:Best Place for Validation in Model/View/Controller Model?

答案 2 :(得分:1)

保持User课程原样,它本身就是一个好公民。

我会使方法isEmailUnique 私有 (如果仅用于此目的)并检查是否存在User addUser内的电子邮件。另一方面,这会将逻辑的责任转移到DAO(参见:Responsibilities and use of Service and DAO Layers

因此,如果您更改isValid的行为以检查用户是否已经进入数据库,那么您的设计就会破坏。

答案 3 :(得分:1)

解决这个问题的一种方法是完全删除User :: isValid方法,支持在构造函数中传递所需的一切,从那里运行验证:

Jackson2JsonRedisSerializer

如果你考虑一下,是什么让用户有效?如果这是一个有效的电子邮件地址,请确保在构造User对象时传入一个。这使得您的User对象始终有效。

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

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

        $this->email = $email;
    }
}

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

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为您提供了一个用于保存新用户的界面,但如果不满足电子邮件唯一性的约束,该操作可能会失败。

答案 4 :(得分:1)

我会从User类中获取所有验证问题并转移到Controller层(例如,可以调用UserDAO来检查电子邮件的唯一性)。最好将User类简单地保存为实体类,并将所有其他内容放在其他类中 - 否则它将会增长并增长到不再可维护的状态:)

同时检查:https://en.wikipedia.org/wiki/Single_responsibility_principle

答案 5 :(得分:0)

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

答案 6 :(得分:0)

我认为您可以使用DAO作为验证功能的参数。

ws.set_column('A:C', None, format)

ws.set_column(0,0, 10.2)
ws.set_column(1,1, 25.2)
ws.set_column(2,2, 15.2)

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