在 MVC 中,我在哪里放置数据库查询?

Posted

技术标签:

【中文标题】在 MVC 中,我在哪里放置数据库查询?【英文标题】:Where do I put a database query in MVC? 【发布时间】:2012-05-27 09:05:19 【问题描述】:

最近几天,我广泛阅读了有关 php 中的 OOP 和 MVC 的书籍和网页,以便我可以成为一个更好的程序员。我对 MVC 的理解遇到了一个小问题:

mysql_query 应该放在哪里?

我应该将它放在控制器中并调用模型上的方法,该方法根据提供的查询返回数据吗?或者我应该把它放在模型本身?我提供的这两个选项都是垃圾吗?

【问题讨论】:

"在哪里放置mysql_query?"你不要:请停止使用古老的mysql_* 函数编写新代码。它们不再维护,社区已经开始 deprecation process 。相反,您应该了解prepared statements 并使用PDO 或MySQLi。如果您无法决定,this article 将帮助您选择。如果你想学习,here is a quite good PDO-related tutorial. 我会在模型中进行查询(mysqli_*PDO)。而且该模型不仅适用于访问数据库。 由于我还不是一个优秀的程序员,所以我没有意识到这一点。但是,无论如何,我还是想选择似乎基于 PDO 的 ORM 样式。但是关于 mvc 的问题仍然不清楚:我从哪里请求数据库中的数据?一个用于读取数据库的通用模型或每个表/任何特定操作的模型?在第二种情况下:我将如何保存代码?我会创建一个“助手”吗? 【参考方案1】:

关于 MVC 主题的材料

您可以列出您正在阅读的书籍,因为大多数(如果不是全部)涉及 MVC 的 php 书籍都是错误的。

如果您想成为一名更好的开发人员,我建议您从 Martining Fowler 的文章开始 - GUI Architectures。其次是同一作者的书 - "Patterns of Enterprise Application Architecture"。然后下一步是让您研究SOLID principles 并了解如何编写遵循Law of Demeter 的代码。这应该涵盖基础知识=]

我可以在 PHP 中使用 MVC 吗?

不是真的。至少不像 defined for Smalltalk 那样的经典 MVC。

在 PHP 中,您有 4 种其他模式以相同的目标为目标:MVC Model2、MVP、MVVM 和 HMVC。同样,我懒得再写一次差异,所以我将链接到我的old comment。

什么是模型?

首先你必须明白,MVC 中的 Model 不是一个类或一个对象。它是一个包含多个类的层。基本上模型层是所有层的组合(尽管第二层应该称为“域对象层”,因为它包含“域模型对象”)。如果您想阅读有关模型层每个部分所包含内容的快速摘要,您可以尝试阅读this old comment(跳至“旁注”部分)。

                            图片取自 Fowler 网站上的 Service Layer 文章。

控制器做什么?

Controller 在 MVC 中有一个主要职责(我将在这里讨论 Model2 的实现):

从模型层(服务或领域对象)对结构执行命令,从而改变所述结构的状态。

它通常有一个次要的责任:将结构从模型层绑定(或以其他方式传递)到视图,但如果您遵循SRP,这将成为一个有问题的做法

SQL相关代码放在哪里?

信息的存储和检索在数据源层处理,通常实现为DataMapper(不要与滥用该名称的 ORM 混淆)。

下面是它的简化用法:

$mapper = $this->mapperFactory->build(Model\Mappers\User::class);
$user = $this->entityFactory->build(Model\Entities\User::class);

$user->setId(42);
$mapper->fetch($user);

if ($user->isBanned() && $user->hasBannExpired())
    $user->setStatus(Model\Mappers\User::STATUS_ACTIVE);


$mapper->store($user);

如您所见,域对象甚至都不知道来自它的信息已被存储。而且它也不是关于你把数据放在哪里的情况。它可以存储在 MySQL 或 PostgreSQL 或一些 noSQL 数据库中。或者可能推送到远程 REST API。或者,映射器可能是用于测试的模拟。要替换映射器,您需要做的就是为这个方法提供不同的工厂。

另外,请参阅以下相关帖子:

understanding MVC Views in PHP testable Controllers with dependencies how should services communicate between each other? MVC for advanced PHP developers

【讨论】:

您提供的代码应该在服务中实现? @dios231 是的。或者至少是它的要点(请注意,那里的代码已经有将近 4 年的历史了.. 你 4 年的代码怎么样了)。这些天,我将使用 DI 容器为他们提供必要的依赖项服务,而不是使用工厂。而且如果我仍然以这种方式使用工厂,域对象和数据映射器将具有单独的工厂,::class 用于输入所需的产品。 使用::class 概念意味着会有一个带有静态方法的类(如build Domain()buildMapper()),这些方法将分别返回domain objectsdata mappers 实例? 不,::class 我的意思是 PHP 5.5 中的 this new feature。它适用于命名空间和别名。当你写foo(ClassName::class) 时,函数接收的是一个完整的类名作为一个字符串。这样,您的代码就不会被硬编码的字符串填充。 所以基本上,MVC中视图和控制器的区别是模型的访问器和修改器之间的区别?视图是向用户呈现模型数据的 UI 对象,而控制器是接受用户命令并使用它们来改变模型的对象。然而,在许多 GUI 应用程序中,这些东西似乎混合在一起,例如我们可能会根据模型状态显示一组按钮(命令采购)。这些按钮是什么?它们是视图还是控制器的一部分,是什么决定了显示哪些按钮?我经常听到类似【参考方案2】:

模型和实体类代表应用程序的数据和逻辑,许多人称之为业务逻辑。通常,它负责:

    存储、删除、更新应用程序数据。通常它包括数据库操作,但实现调用外部 Web 服务或 API 的相同操作一点也不稀奇。 封装应用程序逻辑。这是层 应该实现应用程序的所有逻辑

这是 MVC 序列图,它显示了 http 请求期间的流程:

在这种情况下,模型是实现访问数据库的代码的最佳位置。

【讨论】:

【参考方案3】:

模型包含代表应用程序状态的域对象或数据结构。 [wikipedia]。所以模型将是进行数据库调用的地方。

在“经典”(没有更好的词 atm)MVC 模式中,视图将从模型中获取当前状态。

不要误以为模型是用于访问数据库的。这不仅仅是访问数据库。

【讨论】:

【参考方案4】:

一方面,不要使用mysql_query() 和家人;它们已被弃用,因此请考虑学习 PDO 和/或 mysqli。

模型负责数据处理;它为控制器提供一个接口,通过该接口检索和/或存储信息。所以这将是数据库操作发生的主要地方。

更新

要回答 OP 在 cmets 中提出的问题:“一个通用模型用于整个数据库还是一个模型用于每个表/动作?”

模型旨在抽象出单个表(尽管有些模型专门处理单个表);例如,与其询问所有文章然后查询作者的用户名,不如使用如下函数:

function getArticles()

    // query article table and join with user table to get username

您将创建多少模型很大程度上取决于项目的规模以及数据的相互关联程度。如果您可以识别独立的数据组,您可能会为每个组创建一个模型;但这不是硬性规定。

数据操作可以是同一个模型的一部分,除非您想明确区分只读模型和只写模型(我不知道有什么情况值得这样做,但谁知道)。

【讨论】:

【参考方案5】:

更进一步,您的模型不应包含数据库访问代码。这属于模型/视图/控制器之外的另一层:这称为持久层,可以使用Object-Relational Mapper 来实现,例如PHP 中流行的Doctrine 2。

这样,您永远不会接触任何(我的)SQL 代码。持久层会为您解决这个问题。 我真的建议你看看 Doctrine 教程,这是一种非常专业的创建应用程序的方法。

您无需使用从数据库加载的原始数据,而是创建对象来保存您的数据以及与之相关的行为。

例如,您可能有一个User 类,例如:

class User

    protected $id;
    protected $name;
    protected $privileges;

    public function setName($name)  ... 
    public function getName()  ... 

    public function addPrivilege(Privilege $privilege)  ... 
    public function getPrivileges()  ... 

您的控制器只会与对象交互:

class UserController

    public function testAction()
    
        // ...

        $user = $em->getRepository('User')->find(123); // load User with id 123
        $user->setName('John'); // work with your objects,
        echo $user->getName();  // and don't worry about the db!
        $em->flush(); // persist your changes
    

在幕后,ORM 负责发出SELECT 查询、实例化您的对象、检测对您的对象的修改以及发出必要的UPDATE 语句的所有低级工作!

【讨论】:

以上是关于在 MVC 中,我在哪里放置数据库查询?的主要内容,如果未能解决你的问题,请参考以下文章

MVC,课程去哪儿了?

Symfony 中公共数据存储的数组放置在哪里

在哪里放置与 Eloquent 无关的 SQL 查询

在 DDD 架构中,我应该将与按角色用户过滤数据相关的查询逻辑放在哪里

良好实践:API 和 MVC - 我应该将方法/函数与 API 的 SQL 查询放在哪里

你在哪里“负载平衡”一个 PHP MVC 应用程序中的 ORM