8

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

4

10 回答 10

9

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

甚至从 Sqlite 之类的非常相似的东西转到 MySQL 也是非常罕见的。如果你做的不止这些,那么无论如何你都会遇到更大的问题。

于 2009-04-15T19:11:34.020 回答
8

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

于 2009-04-15T19:11:19.853 回答
3

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

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

于 2009-04-15T19:21:38.940 回答
3

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

  • 易于使用(如 ORM)
  • db-agnostic:适用于 SQL、NoSQL、文件等
  • 如果供应商允许,总是优化查询(sub-select,map-reduce)

结果是敏捷数据 - 数据库访问框架(详细解释请参见视频)。

使用与 DB 无关的代码和敏捷数据解决现实生活中的任务

  1. 从描述商业模式开始。
  2. 创建持久性驱动程序$db(DB 连接的一个花哨的词),它可以是 CSV 文件、SQL 或 LDAP。
  3. 关联模型$db并表达你的Action
  4. 执行Action

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

代码示例

我的下一个代码片段解决了一个相当复杂的问题,即确定我们所有 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

结果查询 if$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

于 2016-06-05T13:20:06.020 回答
2

您应该查看PDO 库。

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

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

于 2009-04-15T19:10:00.763 回答
1

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

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

于 2009-04-15T19:10:34.847 回答
1

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

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

于 2009-04-15T19:20:40.707 回答
1

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

http://adodb.sf.net

于 2009-04-15T21:37:25.867 回答
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());

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

同时你有干净的模型层代码,它独立存在,不知道谁在哪里保存它的对象。

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

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

于 2013-12-29T16:08:34.720 回答
0

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

于 2010-08-23T08:21:48.073 回答