【发布时间】:2010-10-16 08:17:27
【问题描述】:
在一个 PHP 项目中,我们已经将业务逻辑与数据库访问分离。所有数据库任务都封装在按数据库和主题分组的不同数据库类中。 这些类看起来很可怕,一半的源代码是 SQL 字符串,里面充满了参数等等。我们考虑将 SQL 放在“其他”位置,例如资源文件或其他位置。什么被认为是最佳实践?您知道 PHP 的任何支持工具/库吗?
亲切的问候
斯蒂芬
【问题讨论】:
在一个 PHP 项目中,我们已经将业务逻辑与数据库访问分离。所有数据库任务都封装在按数据库和主题分组的不同数据库类中。 这些类看起来很可怕,一半的源代码是 SQL 字符串,里面充满了参数等等。我们考虑将 SQL 放在“其他”位置,例如资源文件或其他位置。什么被认为是最佳实践?您知道 PHP 的任何支持工具/库吗?
亲切的问候
斯蒂芬
【问题讨论】:
我不懂 PHP,但根据我使用其他语言的经验,我可以告诉你这么多:数据访问层是 architecture astronauts 的主要目标。有这么多“最佳实践”,没有一个是真正最好的。在设计 DAL 时,很容易陷入过度抽象的陷阱。走多远就走多远。
我几乎总是使用存储过程来避免意大利面条式代码并简化数据库中的授权,而不是出于性能原因;由于数据库引擎何时以及如何准备它们的复杂性,存储过程的性能提升可能很难确定。另一方面,如果我需要编写一个非常灵活的数据库操作(例如在具有许多输入的搜索屏幕上),我有时会将 SQL 直接放入代码中。有时无论你把它放在哪里,它都会变得难以理解。你必须在某个地方完成这项工作。
如果您没有(不必要地)混合 SQL 和过程代码,请将 SQL 放在对您的应用程序的范围和规模最有意义的地方。抱歉,我无法回答您关于 PHP 工具和库的问题,但希望对您有所帮助。
【讨论】:
您的数据库访问层中几乎不应该有任何 SQL,因为它应该只是抽象与数据库的通信,而不管它正在通信的实际 SQL 是什么。
在现在著名的 MVC 模式中,您的业务逻辑通常包含形成模型层的 SQL。
抛开所有这些“宗教”定义,您现在拥有的内容还算正常,最终会得到一堆 SQL。 SQL 必须存在于某个地方。根据您的优先级和性能要求,我会这样做(按性能折衷排序):
如果 SQL 中有明显的重复,我会进行快速重构迭代以隐藏方法中的所有常见 SQL。这些方法不一定要执行它,而只是构建它。这完全取决于您的应用程序以及 SQL 的复杂程度。如果您没有与数据库进行实际通信的底层,则重构的一部分可能是添加它。
我会考虑使用查询构建器。这是性能和灵活性之间的一个很好的平衡。您只能找到查询构建器作为 ORM 或数据库访问层(如 Zend_Db 及其子组件)、Propel 和/或 Doctrine 的一部分。因此,您可以将其中一个查询构建器移植到您的项目中,而无需使用整个层(这真的不应该很难,因为它们都是基于 PDO 的)。这不会增加任何明显的性能问题。
我会考虑 Doctrine ORM。但是,这对性能有相当大的影响。但是,您最终会得到非常可维护的代码。
最后,我永远不会考虑将 SQL 放入资源或类似的东西中。
【讨论】:
您应该尽可能使用存储过程。这样您就可以提高性能、安全性和代码维护。这应该是您的第一种方法。
如果您仍想将 SP 查询与 DAL 分开,为什么不将它们存储在数据库中?将 SQL 查询存储在数据库中以进行抽象可能看起来很奇怪,因为需要一个查询来提取其他查询。这实际上是一种非常常见的方法,您可以在其中选择符合特定条件的查询,并可能(如果需要)动态构建查询。
另一种方法可能是创建动态构建查询的查询类;
class FruitQuery {
...
public function addTypeCriteria($type) {
$this->internalSQLCriterias[] = "fruit=:type";
$this->internalSQLParameters[] = array(':type', $type);
}
...
public function create() {
$this->internalSQLQuery = "SELECT ... FROM Fruits";
if (sizeof($this->internalSQLCriterias) > 0) {
$this->internalSQLQuery .= " WHERE ";
$moreThanOne = '';
foreach ($this->internalSQLCriterias as $criteria) {
$this->internalSQLQuery .= $moreThanOne . $criteria;
$moreThanOne = " AND ";
}
}
}
...
public function execute() {
/* Bind the parameters to the internalSQLQuery, execute and return results (if any) */
}
...
这个类在任何方面都绝对不完整,你可能想重新考虑它的结构 - 但你可能明白我想要表达的观点。 :) 当然,您必须过滤 Query-builder 的输入以避免安全漏洞!
【讨论】:
好吧,您始终可以使用 PDO 来实现跨不同数据库的一致 API,并编写 portable SQL statements。
另一种选择是使用数据库抽象层,例如Zend_DB。
【讨论】: