【问题标题】:PHP Data Access ObjectPHP 数据访问对象
【发布时间】:2012-07-01 06:09:47
【问题描述】:

我试图弄清楚我是否正确使用了 DAO 模式,更具体地说,当它到达我的映射器类时,抽象数据库持久性应该是怎样的。我使用 PDO 作为数据访问抽象对象,但有时我想知道我是否试图抽象查询太多。

我刚刚介绍了我如何抽象选择查询,但我已经为所有 CRUD 操作编写了方法。

class DaoPDO {

    function __construct() {

        // connection settings
        $this->db_host   = '';
        $this->db_user   = ''; 
        $this->db_pass   = ''; 
        $this->db_name   = '';


    }

    function __destruct() {

        // close connections when the object is destroyed
        $this->dbh = null;

    } 


    function db_connect() {

        try { 

            /**
             * connects to the database -
             * the last line makes a persistent connection, which
             * caches the connection instead of closing it 
             */
            $dbh = new PDO("mysql:host=$this->db_host;dbname=$this->db_name", 
                            $this->db_user, $this->db_pass, 
                            array(PDO::ATTR_PERSISTENT => true));


            return $dbh;

        } catch (PDOException $e) {

            // eventually write this to a file
            print "Error!: " . $e->getMessage() . "<br/>";
            die();

        }


    } // end db_connect()'



    function select($table, array $columns, array $where = array(1=>1), $select_multiple = false) {

        // connect to db
        $dbh = $this->db_connect();

        $where_columns  = array();
        $where_values   = array();

        foreach($where as $col => $val) {

            $col = "$col = ?";

            array_push($where_columns, $col);
            array_push($where_values, $val);

        }


        // comma separated list
        $columns = implode(",", $columns);

        // does not currently support 'OR' arguments
        $where_columns = implode(' AND ', $where_columns);



        $stmt = $dbh->prepare("SELECT $columns
                               FROM   $table
                               WHERE  $where_columns");


        $stmt->execute($where_values);

        if (!$select_multiple) {

            $result = $stmt->fetch(PDO::FETCH_OBJ);
            return $result;

        } else {

            $results = array();

            while ($row = $stmt->fetch(PDO::FETCH_OBJ)) {

                array_push($results, $row);

            }

            return $results;

        }



    } // end select()


} // end class

那么,我的两个问题:

  1. 这是对 DAO 的正确使用,还是我误解了它的目的?

  2. 将查询过程抽象到这种程度是不必要的,甚至是不常见的吗?有时我觉得我试图让事情变得太容易......

【问题讨论】:

  • 如果您没有以任何方式定义或使用属性this-&gt;dbh,那么让__destruct 执行$this-&gt;dbh = null; 有什么意义?每个$this-&gt;db_connect(); 结果都分配给方法范围级别的本地范围$dbh...

标签: php design-patterns data-access-layer


【解决方案1】:

看起来更像是在 PDO(它本身就是一个持久层)之上构建一个持久抽象层,而不是 data access object。虽然 DAO 可以采用多种形式,但目标是将您的业务逻辑与持久性机制分开。

  Business Logic
        |
        v
Data Access Object
        |
        v
 Persistence Layer

带有db_connectselect 的DAO 过于接近持久层的模型。通用 DAO 的最简单形式是在对象级别提供基本的 CRUD 操作,而不暴露持久性机制的内部结构。

interface UserDao
{
    /**
     * Store the new user and assign a unique auto-generated ID.
     */
    function create($user);

    /**
     * Return the user with the given auto-generated ID.
     */
    function findById($id);

    /**
     * Return the user with the given login ID.
     */
    function findByLogin($login);

    /**
     * Update the user's fields.
     */
    function update($user);

    /**
     * Delete the user from the database.
     */
    function delete($user);
}

如果您的业务对象是底层 PDO 模型对象,您可以从 DAO 中返回它们。请注意,根据您选择的底层持久性机制,这可能并不理想。我没有使用过 PDO,但假设它类似于生成标准 PHP 对象的其他 ORM 工具,而无需将业务逻辑绑定到 PDO API。所以你在这里可能没问题。

如果您通过直接访问mysqli 库来实现持久性,例如,您可能希望将数据复制到/从结果集中复制到您自己的模型对象中。这是 DAO 的工作,将其排除在业务逻辑之外。

通过使用 DAO 的接口,您现在可以为不同的持久性框架实现它:PDO、Doctrine、原始 SQL 等等。虽然您不太可能在项目中期切换方法,但与其他好处相比,使用接口的成本可以忽略不计,例如在单元测试中使用模拟。

【讨论】:

  • 那么域模型中的每个域对象可以/应该有一个相关的 Dao 对象用于 CRUD 操作?
  • @saddog - 每个根级对象,是的。例如,Order 及其LineItem 子级列表可能会通过OrderDao 存储在一起。不幸的是,您很少能完全将业务逻辑与持久化领域模型对象的方式隔离开来。
  • 好吧,这实际上又提出了一个问题,然后我会接受并给你赏金。您提到 LineItem 子项,我想确保在我的应用程序中正确处理这些子项。这些孩子是否会映射到数据库中的表(一个 LineItem 表),并被 Dao 合并到 Order 域对象中?即 DaoUser 中的 createObject 方法可以将这两个对象组合在一起?
  • 编辑:OrderDao 中的 createObject 方法会是合并项目以形成域对象的地方吗?
  • @jerry - 是的,没错。这不是强制性的,但其原因是将订单及其项目视为一个独立的对象(封装)。它还减少了您必须构建的 DAO 的数量,即使您可能可以生成基本操作的代码。
【解决方案2】:
  1. 这不一定是必要的,但它肯定是常见的做法。有许多库比你正在做的更进一步抽象 waaaaay :)

【讨论】:

    猜你喜欢
    • 2011-02-23
    • 2018-11-24
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-01
    • 2020-06-12
    • 1970-01-01
    相关资源
    最近更新 更多