PHP MVC 原理
Posted
技术标签:
【中文标题】PHP MVC 原理【英文标题】:PHP MVC Principles 【发布时间】:2012-01-02 10:53:30 【问题描述】:我没有使用现成的框架,也不是特别想要(我也不想详细说明原因......)。无论如何,关于我的问题,我希望它有意义....
我试图弄清楚模型中应该包含什么以及控制器中应该包含什么。最初我的印象是模型类应该代表一个实际对象(例如 - 数据库汽车表中的汽车),模型属性应该反映数据库字段。但是我现在感觉我的想法是错误的——模型类的实例应该代表一个实际的项目,还是它应该包含一些做事的方法——有时是一辆车,有时是多辆车根据我之前的例子。
例如,我想从数据库中获取所有汽车并在视图中显示它们。我认为它应该是这样的吗?
控制器文件
function list()
$cars = $this->model->get_all();
$this->view->add($cars);
$this->view->render('cars-list');
模型文件
function get_all()
// Use a database interaction class that I've written
$cars = Database::select();
return $cars;
现在,如果汽车有一个“状态”字段作为整数存储在数据库中,我想将其更改为字符串,应该在哪里完成?通过循环模型中get_all()方法中的SQL结果数组?
另外,表单验证应该在哪里进行?我写了一个验证类,有点像这样:
$validator = new Validator();
$validator->check('field_name', 'required');
如果检查失败,它会在 Validator 中的数组中添加一条错误消息。然后这个错误消息数组将被传递给视图。我的验证器类应该在模型还是控制器中使用?
提前感谢任何人提供的任何帮助。如果您知道任何指向处理基本 CRUD 的简单 MVC 示例/开源应用程序的链接,我们将不胜感激。
【问题讨论】:
如果您不想使用“现成框架”,为什么要将此标记为 codeigniter? 在 CI 中,您的表单验证通常在您的控制器中完成。这对我来说很有意义,因为您的控制器应该处理表单输入、POST、GET 等。 【参考方案1】:MVC中职责的大致划分一般如下:
模型:业务逻辑。很多人认为模型只是数据库的接口,但这是不正确的。系统中的模型应该描述系统中的所有实体、它们的行为方式和交互方式。数据访问只是其中的一小部分(甚至可以说应该划分为独立于模型、视图或控制器的自己的数据访问层)。例如,在博客应用程序中,博客将包含一组帖子,并且每个帖子可能具有(取决于博客应用程序实现的功能)一组 cmets。这意味着实现一个博客类、一个帖子类和一个评论类。如果您选择删除帖子,则帖子有责任确保它不会留下任何孤立的 cmets。您可以通过在 post 类中使用一个用于删除帖子的方法以及在 comment 类中使用类似的方法来做到这一点。当帖子被发送删除消息时,作为删除过程的一部分,它将向与被删除的帖子关联的所有 cmets 发送删除消息。 视图:表示逻辑。视图基本上是一个网页、一个 RSS 提要、一个 CSV 文件,实际上是您的应用程序可能输出给最终用户的任何类型的数据。它应该只显示已分配给它的变量。 (可以在视图中实现一些逻辑,只要它与输出数据有关。视图不必完全愚蠢) 控制器:粘合逻辑。控制器基本上有两个工作要做:1)从模型中获取数据,将其注入视图并将其呈现给用户。 2)从用户那里获取输入,将其发送到适当的模型进行处理,并将结果呈现给用户。这意味着控制器应该包含非常少的代码,这正是实现上面列出的两个目标所需要的。正如我已经提到的,许多人认为模型只不过是一个数据访问层。您通常可以知道人们何时遵循了这种设计理念,因为结果是模型做的不多,而控制器做的太多(所谓的胖控制器反模式)。这种方法的问题在于,如果您想在其他项目中重用部分代码库,那么您不能将模型转移到新项目中,而无需同时使用大量控制器。控制器通常被认为是特定于应用程序的,因此不可重用。另一方面,模型在您的应用程序中体现实体,并且您可能有多个应用程序需要使用相同类型的实体(博客软件、电子商务店面和留言板都是非常不同的应用程序,但它们都需要实现用户)。
通常,您需要胖模型(无需复制模型本身未实现的代码即可轻松重用)和瘦控制器(当您需要模型执行其他操作时可以轻松替换)。
验证有点麻烦,因为这就是所谓的横切关注点。 MVC 的想法是关注点分离之一,因为它旨在将代码描述为属于 3 个主要类别(业务逻辑、表示逻辑、胶合逻辑)之一,但验证并不容易适合其中任何一个。可以为它既是业务逻辑又是胶合逻辑提出很好的论据,如果你想向用户显示验证错误,那么它也有一个表示逻辑的元素。
通常,我在我的模型中实现了一定程度的验证,通常是相当基本的健全性检查(例如要求整数实际上是内部值等),并实现一组单独的类来对输入进行全面验证。我将在我的控制器中调用验证类,如果验证失败,我会将结果呈现给视图。如果成功,则将数据发送到相关模型进行处理。我想您可以将验证称为“实用程序逻辑”,这是您需要定期执行的操作,而您无法真正绑定到任何一个模型、视图或控制器。
【讨论】:
【参考方案2】:我强烈建议您看看Martin Fowler 的"Patterns of Enterprise Application Architecture"。它很好地解释了每个 MVC 组件的职责分离(加上 很多 其他有用的模式)。
在紧要关头,***对MVC 的解释还不错。
简而言之,您设置的结构是正确的。您的控制器正在通知模型发生了某些事件,模型正在对该事件做出反应,然后视图正在使用模型向最终用户表示系统的状态。验证应该在您的模型中执行,因为它负责维护系统的正确状态。您可能还需要进行一些客户端验证,以减少网络流量并为用户提供更好的界面。
要记住的一件事是,您希望将业务逻辑保留在模型中。你想尽量避免所谓的anemic domain model。当您的模型只是数据库的薄薄层时,这很典型。
我推荐的另一本书是 Eric Evans 的 "Domain-Driven Design: Tackling Complexity in Software"。 DDD 的重点是“主要关注领域和领域逻辑”。
【讨论】:
我认为 hafichuk 为您提供了一些非常好的资源,这些资源也被推荐给了我。本质上,有很多方法可以解决这个问题(来自存储库、数据映射器等),所有这些都取决于您的解决方案需要有多复杂。对我来说,您发布的解决方案非常有效。如果您想完全实现 OOP,您可以尝试使用数据映射器模式(如 Doctrine)......但这些令人头疼的事情不值得上面概述的简单性。【参考方案3】:如果您希望在您的应用程序中将状态字段视为字符串,您不希望在您的数据库中将其作为整数的持久性细节从汽车类中爬出。在其界面中,它是一个字符串,因此应该对其进行处理。
所以,我会在 Car 类中使用 getStatus() 方法,您可以在 getAll() 之后调用,只要您需要它。您将在项目级别委派演员。最好不要让应用程序的其余部分知道您在最低级别有一个整数。从 car 中公开一个字符串,并在应用程序的其余部分继续使用它。
至于验证,我会在控制器中进行。您从视图中获取内容并确保它对您的模型足够好。除非是特别需要在模型中进行的检查(IE 业务相关约束)。
【讨论】:
【参考方案4】:首先,如果您不想使用任何 php 框架,您可以查看它们的源代码并获得一些关于某些功能实现的好主意。
那么验证呢,我认为它是一个业务逻辑特性,所以它必须放在控制器层。(已编辑:这是个坏主意)
【讨论】:
呃……我不同意你的第二个说法。如果它是一个业务逻辑特性,它可能更适合模型,而不是控制器。 IE 如果您需要验证 SSN,您可以在控制器内检查字符串是否传递给模型,但在模型内检查代码是否正确。恕我直言,有 2 个验证级别。只是我的 2 美分... 是的,我同意你的看法。我错了。例如,现在我正在查看 Yii 的源代码,并确信在这个框架中,验证位于模型类中。我犯了一个错误。 不用担心。错误孕育进步。我为过去犯的所有错误感到高兴。除非我重复它们:-D以上是关于PHP MVC 原理的主要内容,如果未能解决你的问题,请参考以下文章