【发布时间】:2011-04-16 01:57:09
【问题描述】:
我有一个使用 MySQL 数据库后端用 PHP 编写的 webapp。这个问题可以很容易地应用于任何尝试使用 SQL 数据库和 MVC OOP 设计的语言和应用程序。
如何将 SQL 代码限制在模型中?
这个问题背后有一个相当长的故事,专门针对我的案例。如前所述,我正在开发一个 PHP/MySQL/AJAX 网站。我使用 OOP 和 MVC 设计原则设计了它——带有模型、视图和控制器。我设法将视图元素(例如标记和样式)完全限制在视图中,并使它们可以很容易地重用。我以为我对 SQL 代码做了同样的事情。但随着工作的进展,很明显模型需要进行一些认真的重构。
我发现将 SQL 保留在模型中的方法是将每个 SQL 查询封装在其自己的 Query 对象中。然后当我需要在视图或控制器中调用一些 SQL 时,我会通过工厂访问查询。控制器或视图中不存在 SQL 代码。
但这变得异常乏味。我认为我实际上并没有通过这样做获得任何东西,并且花费了太多时间来创建名称为“SelectIdsFromTableWhereUser”的查询。查询的工厂接近数千行。在 Eclipse 中进行一些搜索后发现,这些查询中的绝大多数都在一个或两个地方使用,并且再也不会使用。不好。
我知道,在好的 MVC 中,您希望将 SQL 与 Controller 或 View 完全分开。但在这一点上,在我看来,将 SQL 放在代码中需要它的地方会更好,而不是试图将它和数据库代码埋在模型的深处。这些查询只使用一次,为什么还要封装它们呢?
将 SQL 与 Controller 或 View 分开有那么重要吗?这样做有什么收获?让它传播会失去什么?你如何解决这个问题?
编辑根据要求,这里是关于我的模型的更多细节。
它有两个部分。表格部分和查询部分。表部分包含域对象——主要设计为类对象的包装器,类对象与数据库中的表完全类似。例如,可能有一个数据库表Foo,其中包含字段id、name 和type。将有一个 Table 对象 (class FooTable),其中包含一个包含字段“id”、“name”和“type”的数组。它看起来像这样:
class FooTable extends MySQLTable {
private $id;
private $data;
private $statements;
public function __construct($id, $data=NULL, $populate=false) {
// Initialize the table with prepared statements to populate, update and insert. Also,
// initialize it with any data passed in from the $data object.
}
public function get($field) {}
public function set($field, $value) {}
public function populate() {}
public function update() {}
public function insert() {}
}
如果有一个fooBar 数据库表具有一对多关系(一个Foo 多个Bars)与字段id、fooID 和bar,那么将有一个FooBar表格对象 (class FooBarTable) 看起来与上面的 FooTable 几乎完全相同。
FooTable 和许多 FooBarTable 对象都将包含在 Foo 对象中。为Foo 对象工厂提供Foo 表的ID,它会使用Foo 的数据及其所有Bars 及其数据填充自己。
Query 对象用于按需要的顺序提取那些Foo id。因此,如果我想要按日期、投票或姓名排序的 Foo 对象,我需要一个不同的查询对象来执行此操作。或者,如果我想选择在一定范围内具有Bar 的所有Foo 对象。我需要一个查询对象。
大多数时候,我使用 Table 对象(包装器,而不是基表)与数据库进行交互。但是,当涉及到选择哪些表对象时,这就是查询的用武之地。
在最初的设计过程中,我认为不会有太多查询,我认为它们将成为可以重用的东西。因为可能有几个地方我希望Foos 按日期顺序排列。但事情并没有这样发展。它们的数量比预期的要多,其中大多数是一次性的,在某些视图或命令中使用过一次,然后就再也不会了。我还认为查询可能封装了相当复杂的 SQL,将它们作为对象会很好,这样我就可以始终确保为它们提供所需的数据,这将是一个相对净化的环境,可以在其中测试 SQL 查询本身.但同样,它并没有那样做。它们中的大多数都包含非常简单的 SQL。
【问题讨论】:
-
我想你已经回答了你自己的问题。您遵循了看似明智的“最佳实践”,却发现这种做法对于边际收益而言已成为负担。 “正确”固然好,但“有效”更好。
-
@Robert 了解,但我想知道是否可能正是我遵循“最佳实践”的方式导致了我的问题。除了封装对大量一次性查询更好的查询之外,还有其他方法可以将 SQL 限制在模型中吗?
-
@Robert 我认为仅仅因为 Daniel 没有做出好的抽象就将抽象抛诸脑后还为时过早。
-
@timedev:哦,我完全赞成好的抽象,如果这就是问题所在。
-
@OMG Ponies:如果仅从一个地方调用任何函数(不仅仅是查询),则表明您的抽象不是很好。它可能已经足够好了。但是,如果每个新视图都需要将新功能添加到模型中,就像模型上的功能通常只从一个视图中使用的情况一样,那么您不会从“封装”中获得太多收益尽可能的模型。
标签: mysql model-view-controller design-patterns language-agnostic refactoring