问题 在PHP中设计通用数据库接口


我正在为PHP中的Web项目创建一个小框架,所以我不必为每个新网站反复做基础工作。创建第二个CakePHP或Codeigniter并不是我的目标,我也不打算用任何可用的框架构建我的网站,因为我更喜欢使用我自己创建的东西。

当涉及核心结构,请求处理等部分时,我在设计和编码框架方面没有任何问题,但是我已经陷入了为我的模块设计数据库接口的困难。

我已经考虑过使用MVC模式,但发现对于我这个相当小的项目来说这会有点过分。

所以我面临的确切问题是我的框架模块(viewCustomers 可能是一个模块,例如)应该与数据库进行交互。

  • 将SQL直接混合到PHP代码中(仍然)是一个好主意吗? (将是“老路”: mysql_query( 'SELECT firstname, lastname(.....))?

  • 我怎样才能抽象出如下的查询?

    SELECT firstname, lastname FROM customers WHERE id=X
    

MySQL的“助手”功能会不会像

$this->db->customers->getBy( 'id', $x ); 

是个好主意?

我不太确定,因为它们在处理更复杂的查询时会变得毫无用处,例如上面几乎无关紧要的查询。

  • MVC的“模型”模式是解决这个问题的唯一真正选择吗?

  • 您目前使用什么来解决上面显示的问题?


10556
2018-03-17 18:46


起源

“......因为我更喜欢使用我自己创造的东西”......那么这就是可以解决的问题。认真。你会做自己,你的同事和你的客户。 - back2dos
如果你很幸运,你最终会得到一个错误的,功能较差的CakePHP克隆。世界需要的是另一个PHP包装器。对。 - Seva Alekseyev
当我第一次构建我的ORM然后将我的代码移植到它时,我无法相信它。我从我的模型中删除了数百行代码,而这些代码只是不再需要了。现在一切都变小了。当然,我的ORM只有一个文件大小。对于那些浪费的+ 500kb库,我不会说同样的事情。 - Xeoncross
@lamas:你可能会患上'Not invented here'syndrom。一个综合症我也经常受到影响: en.wikipedia.org/wiki/Not_Invented_Here - Decent Dabbler
不知何故,似乎越来越多的人如果有人在SO上提出一个问题而不能用一个词来回答他可能会得到一个免费的心理分析(我的意思是这个)。这不像是我只使用我自己出现的东西,或者你是否看到我现在上传我自己的,更好的SO克隆? fireeyedboy:不,我没有患上“未发明在这里”的综合症。 我现在不想用2到5MB的Doctrine ORM炸毁我的项目 - lamas


答案:


如果你需要速度,那么使用原始查询(但你应该真的使用PDO 准备好的查询)。

如果你想要更多的OOP,你可以 - 如你所知 - 用帮助器设计它。

有一次,我设计了类似的东西,它有以下概念:

  1. 数据库连接/处理程序类(处理到不同数据库和不同服务器的多连接,如MySQL,Oracle等);
  2. 每个动作的类(即SELECT,DELETE等);
  3. 过滤类(例如RangeFilter);

代码看起来像这样:

$select = new Select('field1', 'field2', );
$result = $select->from('myTable')
                 ->addFilter(SQLFilter::RangeFilter, 'field2')
                 ->match(array(1, 3, 5))
                 ->unmatch(array(15, 34))
                 ->fetchAll();

这是一个如何构建它的简单示例。

您可以更进一步实现表关系的自动处理,字段类型检查(使用表上的内省),表和字段别名支持等。

这似乎是一项漫长而艰苦的工作,但实际上,制作所有这些功能(≈1个月)并不会花费你那么多时间。


3
2018-03-28 09:14





我相信您只想从您的模块访问您的数据库。我会避免直接从代码中使用mysql_query。相反,使用抽象的数据库访问的简单模型将是简单和直接的。

例如,您可以使用以下代码获得类似models / Customers.php的文件:

<?php

class Customers {

    public function getById($id) {
        $sql = "SELECT first_name, last_name FROM customers WHERE id='$id'";
        $res = $DB::getRow($sql);
        return ($res);
    }
}

我假设某种数据库助手已经实例化并可用作$ DB。 这里 是一个使用PDO的简单的。

现在,您应该在模块中包含它并使用以下方法:

<?php

include_once "models/Customers.php";

$customers = new Customers();
$theCustomer = $customers->getById(intval($_REQUEST['cust_id']));


echo "Hello " . $theCustomer['first_name']

干杯。


6
2018-03-22 17:47





你看过了吗? http://www.doctrine-project.org/ 或其他php orm框架(想到zend_db)?


5
2018-03-17 18:50



你的意思是我应该看看他们是如何制作/工作或使用它们的? - lamas
使用它们。在我看来再次重新发明它们是浪费时间。我个人非常喜欢zend框架的方式。您编写自己的应用程序/框架,但插入一些模块以执行特定任务。 - roman
问题是这些内存占多少。 PHP不是为了加载这么多文件和类而构建的。因此,您需要在需要强大的服务器或简单编码之间进行权衡。 - Xeoncross
“高性能MySQL”的作者高度劝阻学说和其他人 mysqlperformanceblog.com - Dor
正如xeoncross已经说过的那样 - 它始终归结为生产力/安全性与原始性能......我的方式并没有发现任何文章通常不鼓励使用orm的mysqlperformanceblog.com啊,以及apc以及docrtine的方式'将所有内容编译成一个文件也有帮助 - roman


三个提示:

  • 使用存储过程(这样你可以将php与db分开)
  • 使用PDO /库MySQLi 准备好的陈述 CALL NEWS_LIST(?, ?)
  • 为数据库使用静态类。允许您在任何模块中访问它。

2
2018-03-17 18:50



+1:PDO,PDO,rah rah rah。 PDO,PDO,shish-boom-bah! (抱歉,在工作中很无聊) - Powerlord
对DB使用静态类并不比使用mysql_query好多少。全局可变对象(作为静态DB类)是坏样式,因为它们通常是非常高耦合的来源。 - back2dos
高耦合?我严重怀疑这一点。使用存储过程的静态自定义DB类允许您在所有模块之间使用DB,同时保持更改底层数据库服务(mysql,postgres)或添加缓存(memcache)而无需修改模块的能力。这会产生较低的耦合。 - St. John Johnson
我引述:“允许您在任何模块中访问它。”这引入了“任何模块”和DB类之间的依赖关系。应该并且可以容易地注入的依赖性。这样,“任何模块”都与DB类紧密耦合。如果您想将一些提到的优化/自定义或事物作为分片引入,那么您最终也会对DB类进行屠杀。将所有内容抽象为每个类别的驱动程序关系是您可以做的最少的事情。 - back2dos


原始SQL仍然是我的赢家,我喜欢控制我发送到服务器的内容(对于索引使用,复杂的JOIN子句等情况),所以我通常远离辅助函数。

您应该使用已经提供了大量功能的PDO,如果这还不够,您可以扩展它(可能使用您自己的功能,例如在实际查询数据库之前检查Memcached / APC上的命中)。您还可以扩展该类以实现您自己的SQL函数,如:

function getUser($user_id) {
    return $this->query("SELECT * FROM users WHERE id = " . (int) $user_id);
}

当然,从模型中你仍然可以发送:

$this->db->query("SELECT * FROM users WHERE id = " . (int) $user_id);

并得到相同的结果。这些函数应仅仅作为一种捷径,扩展类不应该包含在框架中,因为它将依赖于站点。

MVC模式很适合这个,因为您只能将数据库用作驱动程序,然后您的模型可以将数据转换为您需要的数据。创建一个简单的MVC结构并不难,它会在以后为您带来好处。


1
2018-03-17 18:56



数据库抽象是一个好主意,但PDO只是解决问题的一种方式。 PHP还带有dbx_开箱即用。然后是ADOdb,ORM,元数据库....但我同意,通过尝试抽象SQL语法,没有什么可以获得并且有很多东西要丢失。 - symcbean


你听起来像我。你见过吗? http://github.com/Xeoncross/micromvc 和一个文件ORM http://github.com/Xeoncross/database?挖掘我的代码,我想你会找到你想要的东西。

解决方案是使用某些查询的完整原始功能 - 同时仍然允许ORM和查询构建器(如codeigniter的AR)用于其他事情。

两者都很好。


1
2018-03-27 19:14





不是我知道确定的答案(我认为它也不存在),但我想我可以分享我在这里的内容。我使用自己的db'框架',轻量级(当前约1000行)并且易于使用。我的主要目标是简化sql的使用,而不是从程序员(我:)中“隐藏”它。一些例子:

 // row() is 'query' + 'fetch' in one
 $user = $db->row("select * from users where id=25");

 // the same, injection safe
 $user = $db->row("select * from users where id=?", $_GET['id']);

 // ? placeholders are smart
 $someUsers = $db->rows("select * from users where id IN(?)", array(1, 2, 10));

 // ...and even smarter
 $data = array('name' => 'Joe', 'age' => 50);
 $id = 222;
 $db->exec("update users set ?a where id=?", $data, $id);

 // 'advanced' fetch functions
 $topNames   = $db->vlist("select name from users order by name limit 10");
 $arrayOfIds = $db->nlist("select id from users where age > 90");

 // table() returns a Table Gateway
 $db->table('users')->delete('where id=?', 25);

 // yes, this is safe
 $db->table('users')->insert($_POST);

 // find() returns a Row Gateway object
 $db->table('users')
     ->find('where name=?', 'Joe')
     ->set('status', 'confirmed')
     ->save();

1
2018-03-27 23:08



当我在你的网站上看到_array和其他位时,我成了你代码的粉丝。你有可能发布上述内容吗? - goat
嘿,谢谢。这几乎是正在进行中的工作,我认为我不会很快发布任何现成的代码。可在此处找到过时的版本 code.google.com/p/mria/source/browse/trunk/yo/db.inc.php 如果您对此有任何问题/建议,请与我们联系。 - user187291