【问题标题】:What's the best way to abstract the database from a PHP application?从 PHP 应用程序中抽象数据库的最佳方法是什么?
【发布时间】:2010-10-19 16:32:02
【问题描述】:

我的问题是如何从应用程序的模型层抽象数据库连接?主要关注的是能够轻松地从不同类型的数据库进行更改。也许您从一个平面文件、逗号分隔的数据库开始。然后你想移动到一个 SQL 数据库。然后稍后您决定 LDAP 实现会更好。一个人怎么能轻松地计划这样的事情?

举个简单的例子,假设您的用户有名字、姓氏和电子邮件。一个非常简单的 PHP 类表示它可能看起来像这样(请忽略公共实例变量的问题):

<?php

class User {
  public $first;
  public $last;
  public $email;
}

?>

我经常看到人们有一个 DAO 类,其中嵌入了 SQL:

<?php

class UserDAO {
  public $id;
  public $fist;
  public $last;
  public $email;

  public function create( &$db ) {
    $sql = "INSERT INTO user VALUES( '$first', '$last', '$email' )";
    $db->query( $sql );
  }
}

?>

我对此类策略的问题是,当您想要更改数据库时,您必须更改每个 DAO 类的创建、更新、加载、删除函数来处理您的新型数据库。即使你有一个程序可以为你自动生成它们(我不是特别喜欢它),你也必须编辑这个程序才能让它现在工作。

您对如何处理有什么建议?

我目前的想法是为 DAO 对象创建一个超类,具有自己的创建、删除、更新、加载功能。但是,这些函数将采用 DAO 的属性数组并生成查询本身。这样,唯一的 SQL 就在 SuperDAO 类中,而不是分散在几个类中。然后,如果你想改变你的数据库层,你只需要改变 SuperDAO 类生成查询的方式。优点?缺点?可预见的问题?好,坏,丑?

【问题讨论】:

    标签: php database design-patterns orm abstraction


    【解决方案1】:

    我提出了一个有趣的概念,它允许开发人员创建与数据库无关的代码,但与 ORM 不同的是,它不会牺牲性能

    • 使用简单(如 ORM)
    • db-agnostic:适用于 SQL、NoSQL、文件等
    • 在供应商允许的情况下始终优化查询(子选择、map-reduce)

    结果是Agile Data - database access framework(详细解释见视频)。

    使用与 DB 无关的代码和敏捷数据解决实际任务

    1. describing business models开始。
    2. 创建持久性驱动程序$db(DB 连接的一个花哨的词),可以是 CSV 文件、SQL 或 LDAP。
    3. 将模型与$db 关联并表达您的Action
    4. 执行Action

    此时,框架将根据数据库的功能确定最佳策略,映射您的字段声明,为您准备和执行查询,这样您就不必编写它们了。

    代码示例

    我的下一个代码 sn-p 解决了一个相当复杂的问题,即确定我们所有 VIP 客户的当前总债务是多少。架构:

    接下来是独立于供应商的代码:

    $clients = new Model_Client($db);
    // Object representing all clients - DataSet
    
    $clients -> addCondition('is_vip', true);
    // Now DataSet is limited to VIP clients only
    
    $vip_client_orders = $clients->refSet('Order');
    // This DataSet will contain only orders placed by VIP clients
    
    $vip_client_orders->addExpression('item_price')->set(function($model, $query){
        return $model->ref('item_id')->fieldQuery('price');
    });
    // Defines a new field for a model expressed through relation with Item
    
    $vip_client_orders->addExpression('paid')
      ->set(function($model, $query){
        return $model->ref('Payment')->sum('amount');
    });
    // Defines another field as sum of related payments
    
    $vip_client_orders->addExpression('due')->set(function($model, $query){
        return $query->expr('{item_price} * {qty} - {paid}');
    });
    // Defines third field for calculating due
    
    $total_due_payment = $vip_client_orders->sum('due')->getOne();
    // Defines and executes "sum" action on our expression across specified data-set
    

    如果$db 是 SQL,则生成的查询:

    select sum(
      (select `price` from `item` where `item`.`id` = `order`.`item_id` )
      * `order`.`qty`
      - (select sum(`payment`.`amount`) `amount` 
         from `payment` where `payment`.`order_id` = `order`.`id` )
    ) `due` from `order` 
    where `order`.`user_id` in (
      select `id` from `user` where `user`.`is_client` = 1 and `user`.`is_vip` = 1 
    )
    

    对于其他数据源,执行策略可能会增加一些数据,但会始终如一地工作。

    我认为我的方法是抽象数据库的好方法,我正在努力在 MIT 许可下实现它:

    https://github.com/atk4/data

    【讨论】:

      【解决方案2】:

      最好的方法是使用 ORM(对象关系映射)库。 PHP有很多。我个人用过,可以推荐doctrine orm(我已经和silex结合使用过,这是一个极简的php框架)。

      这是一个关于 PHP ORM 的 StackOverflow 线程,如果您愿意,可以在其中找到一些替代方案:Good PHP ORM Library?

      【讨论】:

        【解决方案3】:

        实际上解决了“在哪里实现数据访问逻辑?”主题的解决方案。并不复杂。您只需要记住,您的模型代码必须与数据访问代码分开。

        喜欢:

        具有一些业务逻辑 User::name() 方法的模型层

        class User 
        {
          public $first;
          public $last;
          public $email;
          public function name ()
          {
              return $this->first." ".$this->last;
          }
        }
        

        数据访问层:

        class Link
        {
            $this->connection;
            public function __construct ()
            {
                $this->connection = PDO_Some_Connect_Function();
            }
            public function query ($query)
            {
                PDO_Some_Query ($this->connection, $query);
            }
        
        }
        
        class Database
        {
            public $link;
            public function __construct ()
            {
                $this->link = new Link();
            }
            public function query ($query)
            {
                $this->link->query ($query);
            }
        }
        
        class Users
        {
            public $database;
            public function __construct (&$database)
            {
                 $this->database = &$database;
            } 
            public save ($user)
            {
                $this->database->link->query ("INSERT INTO user VALUES( '$user->first', '$user->last', '$user->email' ))";
            }
        

        用法:

        $database = new Database();
        
        $users = new Users();
        
        $users->save (new User());
        

        在这个例子中,很明显你总是可以改变你的数据访问类 Link 它将在任何东西上运行查询(这意味着你可以在你改变链接时将你的用户保存在任何服务器上)。

        同时,您拥有干净的模型层代码,这些代码独立存在,不知道谁在哪里保存其对象。

        这里的数据库类似乎没有必要,但实际上它可以产生一些很棒的想法,例如在一个项目中为多个数据库连接收集多个链接一个实例。

        还有一个名为 db.php(http://dbphp.net) 的单一文件最简单和全能的框架,它建立在我在这里描述的模式之上,甚至可以自动创建表,能够完全控制其标准 sql 字段/表设置和同步数据库每次都可以根据需要构建模型。

        【讨论】:

          【解决方案4】:

          Axon ORM 自动检测架构中的更改,而无需您重新构建代码。

          【讨论】:

            【解决方案5】:

            使用ORM 通常是抽象数据库的首选方式。 Wikipedia 上提供了 PHP 实现的不完整列表。

            【讨论】:

              【解决方案6】:

              我一直喜欢使用 ADOdb。从我所见,它似乎能够在截然不同的平台之间切换。

              http://adodb.sf.net

              【讨论】:

              • 我们也使用 ADOdb 来处理抽象和 ORM(在自定义 MVC 框架中),然后将其包装在我们的应用程序特定 API 中 - 这很棒。无法想象现在还有其他方式。
              【解决方案7】:

              一般来说,如果您在使用数据库时遇到了麻烦,那么您的应用程序将受益于使用特定于数据库“品牌”的功能,并且对于它来说将是一个更可靠的应用程序。

              从一个数据库系统迁移到另一个数据库系统是非常罕见的。只有在编写某种松散耦合的系统或面向大众消费的框架(如 Zend Framework 或 Django)时,您才会真正认为值得实现的功能。

              【讨论】:

                【解决方案8】:

                您可以使用各种框架,例如 PDO、PEAR::MDB2 或 Zend_Db,但老实说,在 12 年的 PHP 开发中,我从未需要从一种数据存储基础架构过渡到另一种。

                从 Sqlite 这样非常相似的东西转向 MySQL 是非常罕见的。如果你做的比这更多,无论如何你都会遇到更大的问题。

                【讨论】:

                • 我使用 SQLite 进行开发,使用 MySQL 进行生产(使用 Propel ORM),但目前我的个人项目很小;我同意你说的。
                • 就我而言,PDO 对抽象数据库层没有帮助。我需要使用 mysql 开发一个应用程序,客户将他的数据库更改为 oracle。 PDO 没有帮助,因为两个数据库的 SQL 语法不同。为此,我遇到了很多麻烦。例如,如果我使用两种类型的 daos 开发系统,mysqldaos 和 oracledaos,我可以设法通过工厂(模式)应用我想要的一种。主要问题是现在我在 mysql 中以开发模式运行该软件,而在生产中它在 oracle 中运行。
                【解决方案9】:

                理论上听起来不错,但很可能YAGNI

                您最好使用诸如PDO 之类的 SQL 库,并且在您到达那里之前不要担心 LDAP。

                【讨论】:

                • 这是一个常见的维护问题,最终很可能需要。数据库模式很少一成不变。
                • 他不是在谈论架构,而是在谈论 CSV 和关系数据库之间的无缝切换。你多久这样做一次?
                • 或 LDAP 或任何发明的新型数据库。如果我没有需要,我不会问这个问题。
                • 即使更改数据库引擎也很少见(例如,从 MySQL 到 Oracle),但这可以通过 PEAR::MDB2 等数据库抽象层来解决(尽管可能有一个 CSV 驱动程序,或者可以写成...)
                【解决方案10】:

                您应该查看PDO library.

                PDO 提供了一个数据访问抽象层,这意味着无论您使用哪个数据库,您都可以使用相同的函数来发出查询和获取数据。

                PDO 不提供数据库抽象;它不会重写 SQL 或模拟缺失的功能。如果您需要该功能,您应该使用成熟的抽象层。

                【讨论】:

                • 这并没有抽象出数据库,只是访问它。可以使用 PDO 来创建抽象。
                • 在他的示例中,他并没有抽象出数据库。只需通过预先创建的函数访问它。
                • 我使用 PDO。不幸的是,它只处理我已经可以处理的 SQL 数据库。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-09-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多