如何将基于页面的 PHP 应用程序转换为 MVC?
Posted
技术标签:
【中文标题】如何将基于页面的 PHP 应用程序转换为 MVC?【英文标题】:How Do You Convert a Page-Based PHP Application to MVC? 【发布时间】:2010-09-29 21:17:35 【问题描述】:我一直在努力研究如何使用 MVC 框架重新编码基于页面的 php 应用程序。仅作为背景,我不得不将应用程序移至 MVC,因为我的老板正在制作我。不管怎样,我坐下来,打印出目录结构。然后我开始尝试计划如何将这些页面转换为控制器/动作对。有些事情看起来很直接。例如,我有几个页面专门用于添加/编辑/删除用户。这很容易创建一个“用户”控制器,并为添加/编辑/删除添加方法或操作。我遇到的问题是决定何时实际创建控制器而不是仅将某些东西作为动作,因为它并不总是那么明确。例如,登录控制器与用户/登录,或注册控制器与用户/注册。对我来说,如果对象可以做某事,那么作为一个动作是非常有意义的,但它并不总是那么明确。
另一个例子是,我有大约 12 个用于创建“计划”的表单页面。在我的脑海中,我认为我需要创建一个“计划”控制器,然后每个旧的页面都会变成一个动作。所以我有一个控制器,有 12 个动作(方法)。对我来说,问题是,尽管所有 12 个页面都是数据输入表单,但最终构成了这个“计划”,这就是它们的共同点。每个页面使用数据库中的不同表,并且彼此之间没有任何共同之处。基本上通过创建一个“计划”控制器,我只是将其用作分组机制;不一定要使用它,因为它们彼此相关。至少在上面的“用户”控制器示例中;这些动作中的每一个都使用相同的“用户”表,因此将这些动作分组到一个控制器中是有意义的。我应该让这些数据输入表单中的每一个都有自己的控制器吗?
我想这归结为让我自己使用控制器作为层次结构实体而不是对象/动作。似乎很容易以错误的方式使用控制器陷入这个陷阱。有人明白我在说什么吗?希望不要太混乱。
编辑:如果我尝试坚持每个视图使用一个控制器;然后,我会将每个请求的代码保持在最低限度。这是最好的方法吗?
编辑:从每个人的说法来看,每个视图一个控制器似乎不符合我的最大利益。我仍然有些担心,因为控制器似乎很快就会变胖,但这是另一个讨论。我还有一些关于何时决定使用控制器而不是动作的问题。一个很好的例子是 stackoverflow 本身。在页面顶部,您有一个“问题”选项,我们可以假设它会将您带到“问题”控制器。我这样说是因为在右侧您可以选择“提问”,URL 指向“问题/提问”。这是有道理的,你使用问题控制器的 ask 方法。让我感到困惑的是,菜单上有“未回答”选项。看起来这本身就有一个控制器。为什么它不像“问题/未回答”那样只是问题控制器下的一个动作?这就是我变得混乱的地方。
【问题讨论】:
我真的希望这个要求是因为他们计划在未来用这个应用程序做大事,而不仅仅是因为 MVC 是最新的开发流行语。如果他们这样做是出于正当理由,那就是在浪费大量时间,并且会产生大量新错误。 我认为这个小丑(乔)“元”太多,“做”不够 【参考方案1】:在采取这样的行动时,您不仅改变了特定网站的实施,而且改变了人们感知它的方式。无论如何,您将不得不进行大量的重新编码,但是您具有理解概念并具有如何做到这一点的经验的优势。
我的建议是坐下来,假装您对如何实施该网站一无所知,但要了解与您正在开发的网站类型相关的所有问题——它的要求和陷阱。创建一个基于 MVC 的关于如何做站点的概念化,然后尝试尽可能多地重用您拥有的代码。代码重用最终可能不可行,但从好的方面来说,至少您之前已经解决了许多相同的问题,因此实施起来应该不会花很长时间。
不要忘记,您始终可以使用路由来为使用不同控制器的一系列表单页面维护合理的 URL 结构,从而允许您使用不同的控制器、操作和视图,同时仍然可以从相同的布局或模板。
【讨论】:
【参考方案2】:我遇到的最大问题是决定何时实际创建控制器而不是仅将某物制作为动作,因为它并不总是那么明确。
尽可能为每个视图使用一个控制器。那样:
-
您没有加载超出每个请求所需的代码量
控制器和视图之间存在明确的 1:1 链接(尤其是当它们共享相同名称时)。
【讨论】:
我想过……但是从我在互联网上看到的所有示例中,我很少看到它完成的方式。尤其是在 ROR 中,控制器可能有 5-10 种或更多方法。我确实同意将这么多代码放入控制器中,而每次请求只使用其中的一小部分是很奇怪的。 我的一些控制器有 10 多个公共操作方法。取决于视图中有多少操作。【参考方案3】:既然你的老板喜欢流行语,就告诉他查一下“重构”
【讨论】:
【参考方案4】:您的问题有点偏离主题...看在上帝的份上,请不要忘记您的旧网址。原因是,在您打开新网站及其漂亮网址的那一刻,搜索引擎持有的所有聚合内容都会慢慢过期,从而降低您的 SEO 统计数据。
更新:如果我尝试在每个视图中使用一个控制器...然后我会将每个请求的代码保持在最低限度,但这是最好的方法吗?
如果我理解正确的话,每个控制器都会产生一个页面。这可能是一个非常糟糕的主意,我在维护职位上亲身经历过。如果您的内容与 OO 范例不太匹配,则将其归入一个类别:新闻稿、一次性、信息等,并将它们粘贴到仅提供这些视图的控制器中,或者更好的是每个控制器的数量类别以留下更细粒度的控制。
我处理的“MVC”框架让一个控制器查看很快就变成了一个令人费解的混乱,包含大量的黑客和意大利面条代码。
我有大约 12 个表单页面用于创建“计划”。
我不得不做一些类似的事情,并且看起来很愚蠢,拥有一个“planningform”控制器可能是最好的。是的,有多个表格可供输入,但您可以执行简单的操作,例如 $_SESSION['plannerController']['subject|action'][key][value] 来检查表单的每个部分。让每个方法处理多页表单的一部分也可能是一个好处(例如,如果你的老板说他们只需要 12 页中的 6 页,或者如果第 7 部分变得非常复杂并且需要自动完成 ajax 之类的东西怎么办)。
分组机制不一定使用它,因为它们之间有一些相关的东西。
我通常喜欢一个类,一个目的的范例,但有时尝试这样做是没有意义的。在某些情况下,从教科书OO和其他时候学术BS可能会下地狱。你会发现不符合 OO 模式的东西,所以你可以退后一步,尝试将它们合并到常见的类别中。
【讨论】:
【参考方案5】:每个控制器有一个视图有什么意义?当一切都只是配对匹配时,有什么需要分离关注点?
我更倾向于编写一个控制器来控制以某种方式相关的多个视图。例如,前面提到的用户的添加/查看/编辑。您希望将类似的功能保持在一起,而不是在许多文件中搜索您想要的代码。在一个地方定义所有方法(针对特定对象)也很方便。使维护更容易。
我使用 PHP 框架处理了一个项目,该框架为每个“操作”创建了一个单独的文件。命名为“对象(动作)”,它变成了一个需要维护的噩梦。
我使用 Django 已经有一段时间了,它将所有模型保存在一个文件中,将所有视图(控制器)保存在一个文件中,并将模板(视图)分开保存。 [Django 不是 MVC,但出于这些目的,让我们假装它是]。这使您可以将通用代码组合在一个地方,并且维护变得更加容易。
我唯一的建议是 - 不要试图根据 MVC 的某些理想来组织您的项目。组织您的项目如何对您和您的领域有意义。提出 MVC 是为了限制复杂性,而不是增加复杂性。
对于您的计划示例,我会将其制成控制器。 www.example.com/plan/1 .. /plan/2 等等。这是有道理的,因为这些操作在逻辑上组合在一起以形成一些任务。
在我看来,控制器应该用于管理任务或对象。它的方法应该是完成该任务或修改/使用对象所需的特定操作。
【讨论】:
【参考方案6】:每个控制器有一个视图有什么意义?
再次:
-
您没有加载超出每个请求所需的代码。你会加载你不需要的库、包、dll吗?当然不是。所以不要创建一个庞大的控制器文件,每个请求只会执行一小部分代码。此外,较小的文件更易于维护 imo,就像较小的方法和模块化代码一样。
控制器和视图之间有明确的 1:1 链接(特别是如果它们共享相同的名称)。这是一个约定。这是清晰和一致的。如果我正在查看视图模板,我确切地知道仅根据文件名加载它的控制器。不涉及任何思考。没有决定。没有妥协。
当一切都只是配对匹配时,有什么需要分离关注点?
关注点分离有什么需要?如果您想对相关页面(控制器/视图)进行分组,请将它们集中在目录中。
我更倾向于编写一个控制器来控制以某种方式相关的多个视图。例如,前面提到的用户的添加/查看/编辑。您希望将类似的功能保持在一起,而不是在许多文件中搜索您想要的代码。在一个地方定义所有方法(针对特定对象)也很方便。使维护更容易。
我会恭敬地不同意。如果我有一个负责添加/查看/编辑用户的视图,那么按照 1:1 约定,我可以确切地知道负责的控制器。另一方面,使用您对类似功能进行分组的建议,如果我有一个管理器控制器和一个用户控制器,哪个包含管理器的添加/查看/编辑?用户还是管理员?现在你必须思考或搜索。
我使用 PHP 框架处理了一个项目,该框架为每个“操作”创建了一个单独的文件。命名为“对象(动作)”,它变成了一个需要维护的噩梦。
我不是在暗示。
我使用 Django 已经有一段时间了,它将所有模型保存在一个文件中,将所有视图(控制器)保存在一个文件中,并将模板(视图)分开保存。 [Django 不是 MVC,但出于这些目的,让我们假装它是]。这使您可以将通用代码组合在一个地方,并且维护变得更加容易。
我现在感觉很糟糕。我不了解 Django,我假设单个文件是可选的,但我无法维护一个包含数万行的文件。
我唯一的建议是 - 不要试图根据 MVC 的某些理想来组织您的项目。组织您的项目如何对您和您的领域有意义。
不,不,不。这是非常危险的建议。设计模式、编码约定和框架是为一个目的而设计的——最佳实践和一致性。只有大师才能超越常规,并且只有在他/她独自工作的情况下。即使在框架的范围内,我也会不断努力提高一致性,以便在编写或维护代码时不必考虑。
【讨论】:
【参考方案7】:如果我理解正确的话,每个控制器都会产生一个页面。这是我在维护岗位上亲身经历过的一个非常糟糕的主意。
我处理的“MVC”框架让一个控制器查看很快就变成了一个令人费解的混乱,包含大量的黑客和意大利面条代码。
这是因为框架、开发人员还是两者兼而有之?维护起来有多难?涉及哪些黑客攻击,为什么?
1 控制器到 1 视图在 php MVC 框架 Zend、CodeIgniter 和 Kohana 中非常适合我。在 ASP.NET 中,虽然不是 MVC,但 1 个 webform/view 映射到 1 个代码隐藏文件。
【讨论】:
【参考方案8】:去年我经历过类似的事情。我将我的大部分静态 PHP 网站页面转换为使用 Kohana PHP Framework。我将每个网站部分都设置为控制器,其中包含各个页面的视图。主页视图包括页眉和页脚的其他视图。一些视图,例如文章视图,可以被不同的控制器重用。结果是一个 MVC 站点,其页面 URL 与原始网站相同。
编辑:URL 的格式为 /controller/method?arguments。例如,在我的网站上,/computer/article.php?id=# 的 URL 使用 computer 控制器中的 article 函数。 computer 控制器依次使用 article 模型将数据加载到具有嵌套 paragraph 的 article 视图中意见。这也说明即使网站的早期版本在 URL 中具有带有 .php 扩展名的页面名称,这仍然可以转换为控制器类方法,并且相同的 URL 在基于 MVC 的网站版本中有效。这应该让您了解 Kohana 如何为您的网站工作。
【讨论】:
【参考方案9】:我还有一些关于何时决定使用控制器而不是操作的问题。一个很好的例子就是这个 *** 网站。在页面顶部,您有一个“问题”选项,我们可以假设它会将您带到“问题”控制器。我这样说是因为在右侧您可以选择“提出问题”,URL 指向“问题/询问”。因此,您使用问题控制器的 ask 方法是有道理的。
好吧,我们不知道“询问”是否是“问题”控制器的动作。该站点可能将“问题/询问”网址路由到不同的控制器,或者“询问”可能是“问题”目录中的控制器。此外,如果您打开“问题/询问”页面,您会注意到表单被发布到“问题/询问/发布”。现在“post”可能是“ask”操作方法的参数,但我猜它是“ask”控制器的“post”操作方法。
谁知道呢。但请考虑这一点:“问题”页面需要的代码比“”页面多几倍。加载“”页面时加载“问题”页面代码是否有意义。我认为 *** 的人会比这更聪明。
但让我感到困惑的是,菜单上有“未回答”选项。看起来这本身就有一个控制器。为什么不只是问题控制器下的一个动作?就像“问题/未回答”一样?这就是我变得混乱的地方。
我认为“”页面有自己的控制器的另一个原因。 “问题”页面与“未回答”页面有很多共同之处。
【讨论】:
【参考方案10】:Nemetral 的三篇博文让我更清楚了整个概念 它解释了从传统网页脚本演变而来的模式,因此 PHP 逻辑与表示逻辑分离,因此就 MVC 而言,模型如何与视图分离。 根据 Nemetral 的说法:
-
第一步是将到目前为止与 html 标记相结合的所有 PHP 代码移动到页面的顶部。
第二步是将所有 HTML 标记移动到一个单独的文件中,并通过 PHP 包含访问它。因此,当发出请求时,它会被定向到 PHP 代码(控制器和模型),然后此代码会请求 HTML 标记或演示文稿(视图)。
我在我的论文中写了更多关于这个的内容:http://kreus-cms.com/kreus/pages/written_work
【讨论】:
【参考方案11】:假设您手头有一些测试,涵盖了应用程序的大部分(全部?)功能。
-
充实您的 C 部分外观的真正基本结构。
忽略 M 和 V 实现此结构,或多或少只是将 sn-ps 代码从面向页面的文件复制到控制器中
测试一切是否仍按预期工作
遍历每个控制器,提取一个“M”和一个“V”。 “M”可能只是一个 Active Record(我知道,这不是一个真正的模型)或一个行表网关或一些可以很快实现的东西。控制器现在应该越来越薄了
测试一切是否仍按预期工作
现在您已了解有关应用程序的所有信息,如果适用,请再次重构以提取域模型。在某些情况下,这可能只是浪费时间(仅限 CRUD/密集型应用程序)
测试一切是否仍按预期工作。
【讨论】:
以上是关于如何将基于页面的 PHP 应用程序转换为 MVC?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring MVC (JPA) 中将实体对象列表转换为页面对象?
如何使用 PHP 将 html 页面转换为单个 .chm 文件?