最佳做法:表示单个数据库表的类 [已关闭]

Best practice: A class which represents a single database table [closed]

提问人:root66 提问时间:8/23/2017 更新时间:8/23/2017 访问量:281

问:


想改进这个问题吗?更新问题,以便可以通过编辑这篇文章用事实和引文来回答。

6年前关闭。

我有一个用户数据库表,其中包含以下字段:id、用户名、密码、电子邮件、角色、密码恢复令牌和过期时间等。

现在我需要一个类来读取和写入用户数据。

为每个数据库字段创建类变量并像这样对它们进行读/写是否更好:

class User {
    private $db;
    private $user_id;
    private $username;
    private $email;

    [...]

    public function getUsername() {
        return $this->username;
    }

    public function setUsername($name) {
        $this->username = $name;
    }

    public function readDB() {
         $sth = $this->db->query("SELECT * FROM users WHERE id = ?", 
         array((int)$this->user_id));
         $user = $sth->fetch();
         if($user !== false && is_array($user)) {
             $this->username = $user['username'];
             $this->email = $user['email'];
             [...]
    }

    public function writeDB() {
        [...]
        $this->db->insert('users', array(
            'username' => $this->username
            'email' => $this->email), array('user_id' => $this->user_id)); 
    }
}

或者不带类变量,像这样做:

class User {
    private $db;

    [...]

    public function readDB($userID) {
         $sth = $this->db->query("SELECT * FROM users WHERE id = ?", 
         array((int)$userID));
         $user = $sth->fetch();
         return $user;
    }

    public function writeDB($userID, $username, $email ...) {
        $this->db->insert('users', array(
            'username' => $username
            'email' => $email), array('user_id' => $userID)); 
    }
}

我看不出第一种解决方案的优点,除了以后更改数据库字段名称很容易,但这可以通过在较低的 readDB 版本中进行简单更改轻松完成。

第一个版本的缺点是,我需要编写大量的 get 和 set 方法,并且方法调用需要更多的处理时间,并且我必须首先检查每个类变量,在插入/更新数据之前是否已经设置。如果缺少参数,第二个版本将自动生成方法参数错误。

例如,有时我只需要用户的电子邮件地址,而第一个版本总是需要先从数据库中读取所有字段,然后调用 getEmail 方法。第二种方法允许编写一种特殊方法来返回单个数据库字段(SELECT email FROM user WHERE user_id = ?)。这将减少数据库负载。

您更喜欢哪种方式,为什么?

PHP 哎呀

评论

4赞 Iłya Bursov 8/23/2017
第一种方法违反了 SOLID 的 S(用户类同时是 DTO 和 DAO),第二种方法违反了 SOLID 的 O(它返回打开以供修改并关闭以供扩展的数组)
2赞 nerdlyist 8/23/2017
您的第一种方法进展顺利,但您需要删除从实体获取数据的工作。创建一个执行持久性和数据访问的数据库类是一个不错的选择。或者给自己找一个很好的老式数据库框架。有很多选择,都有自己特别的优点和痛点。
0赞 root66 8/23/2017
我之所以使用 Doctrine DBAL,是因为开销非常轻量级。此类应仅用于读取和保存用户数据库表的数据以及用户管理工具。所有其他逻辑都由安全提供程序处理。

答:

1赞 tereško 8/23/2017 #1

应将域实体逻辑与持久性逻辑分开。不要有一个包含所有内容的类,因为这样你基本上会得到活动记录反模式的美化版本。

一个更好的选择是让域对象处理业务逻辑(如验证和数据转换),并将存储逻辑保留在单独的数据映射器实例中。

在实践中,该代码看起来有点像这样:

$user = new Entity\User;
$user->setUsername('Drunk Lizard');

$mapper = new Mapper\User($pdo);
$mapper->fetch($user);

if (!$user->hasStatus(Entity\User::UNVERIFIED)) {
    throw new InvalidAction;
}

if (!$user->hasToken($request->get('token'))) {
    throw new WrongToken;
}

$user->setStatus(Entity\User::VERIFIED);
$mapper->store($user);

评论

0赞 root66 8/23/2017
这已经是某种数据映射器类,仅用于数据库操作。用户实体由 Silex/Symfony 的安全提供程序处理。