从PHP应用程序中抽象数据库的最佳方法是什么?

时间:2009-04-15 19:05:40

标签: php database design-patterns orm abstraction

我的问题是如何从应用程序的模型层中抽象数据库连接?主要关注点是能够轻松地从不同类型的数据库进行更改。也许你从一个平面文件,逗号分隔的数据库开始。然后,您想要移动到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类生成查询的方式。好处?缺点是什么?可预见的问题?好的,坏的,丑的?

10 个答案:

答案 0 :(得分:8)

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

甚至从像Sqlite这样非常相似的东西转到MySQL也是非常罕见的。如果你确实做了更多,那么无论如何你都会遇到更大的问题。

答案 1 :(得分:7)

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

答案 2 :(得分:3)

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

  • 简单易用(如ORM)
  • db-agnostic:适用于SQL,NoSQL,文件等
  • 如果供应商允许(sub-select,map-reduce)
  • ,则始终优化查询

结果为Agile Data - database access framework(有关详细说明,请参阅视频)。

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

  1. describing business models开始。
  2. 创建持久性驱动程序$db(数据库连接的一个奇特的词),可以是CSV文件,SQL或LDAP。
  3. 将模型与$db关联并表达您的Action
  4. 执行Action
  5. 此时框架将根据数据库的功能确定最佳策略,为您绘制字段声明,准备和执行查询,以便您不必编写它们。

    代码示例

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

    enter image description here

    接下来是与供应商无关的代码:

    $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

答案 3 :(得分:2)

您应该查看PDO library.

  

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

     

PDO不提供数据库抽象;它不会重写SQL或模拟缺少的功能。如果需要该工具,则应使用完整的抽象层。

答案 4 :(得分:2)

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

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

答案 5 :(得分:1)

理论上听起来不错,但很有可能{{3p>

最好使用YAGNI之类的SQL库,直到达到目标为止,不要担心LDAP。

答案 6 :(得分:1)

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

从一个数据库系统转移到另一个数据库系统非常罕见。唯一一次,如果你正在编写一种用于大规模消费的松散耦合系统或框架(如Zend Framework或Django),你可能会真实地考虑一个值得实现的特性。

答案 7 :(得分:1)

我总是喜欢使用ADOdb。从我所看到的情况来看,它看起来能够在截然不同的平台之间切换。

http://adodb.sf.net

答案 8 :(得分:1)

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

像:

具有一些业务逻辑的模型层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());

在这个示例中,很明显您可以随时更改数据访问类链接,该链接将对任何内容运行查询(这意味着您可以在更改链接时将用户保存在任何服务器上)。

与此同时,您拥有独立生活的清晰模型层代码,并且不知道保存其对象的人和位置。

此处的数据库类似乎也没有用,但实际上它可以产生很好的想法,例如在一个项目中为许多数据库连接收集许多链接。

还有一个名为db.phphttp://dbphp.net)的单个文件最简单,最全能的框架,它基于我在这里描述的模式构建,甚至可以自动创建表,能够完全控制其标准的sql字段/表每次您希望设置和同步数据库结构到您的模型。

答案 9 :(得分:0)

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