如何将基于页面的PHP应用程序转换为MVC?

时间:2009-01-06 19:25:16

标签: php model-view-controller codeigniter controller

我一直在苦苦挣扎一段时间,正是如何使用MVC框架重新编码基于页面的PHP应用程序。仅仅是为了背景,我不得不将应用程序转移到MVC,因为我的老板正在制作我。无论如何,我坐下来打印出目录结构。然后我开始尝试计划如何将这些页面转换为控制器/动作对。有些事情似乎很直接。例如,我有几页专门用于添加/编辑/删除用户。这很容易创建一个“用户”控制器,并添加添加/编辑/删除的方法或操作。我遇到问题的地方在于决定何时实际创建一个控制器,而不是只做一个动作,因为它并不总是那么明确。例如,登录控制器与用户/登录,或寄存器控制器与用户/寄存器。对我来说,如果对象可以做某事,那么做一个动作是完全合理的,但它并不总是那么清晰。

另一个例子是,我有大约12个用于创建“计划”的表单页面。在我脑海中,我认为我需要创建一个“计划”控制器,然后每个旧的页面将成为一个动作。所以我有一个控制器有12个动作(方法)。对我来说问题是,虽然这些页面中的所有12页都是数据输入表格,但最终构成了这个“计划”,这是他们共同拥有的。每个页面在数据库中使用不同的表,并且彼此之间没有任何其他共同点。基本上通过创建一个“计划”控制器,我只是真的使用它作为分组机制;不一定使用它,因为它们有相互关联的东西。至少在上面的“用户”控制器示例中;这些操作中的每一个都使用相同的“用户”表,因此将这些操作组合到一个控制器中是有意义的。我应该让每个数据输入表单都有自己的控制器吗?

我想它只是归结为让我自己使用控制器作为层次结构实体而不是对象/动作。看起来很容易使用控制器以错误的方式陷入陷阱。有谁知道我在说什么?希望它不会太混乱。

编辑:如果我尝试每个视图坚持使用一个控制器;然后,我会将每个请求的代码保持在最低限度。这是最好的方式吗?

编辑:从每个人的意见来看,似乎每个视图的一个控制器不符合我的最佳利益。我仍然有一些担忧,因为看起来控制器可能会匆忙变胖,但这是另一个讨论。我还有一些问题,何时决定使用控制器而不是动作。一个很好的例子就是堆栈溢出本身。在页面顶部,您有一个“问题”选项,我们可以假设将您带到“问题”控制器。我这样说是因为在右侧您可以选择“提出问题”,URL指向“问题/提问”。这是有道理的,你使用问题控制器的ask方法。令我困惑的是,您在菜单上有“未答复”选项。它看起来像是一个控制器。为什么它不仅仅是问题控制下的行动,如“问题/未答复”?这就是我的事情变得混乱的地方。

11 个答案:

答案 0 :(得分:4)

既然你的老板很流行,请告诉他查看“重构”

答案 1 :(得分:2)

对你的问题稍微偏离主题......对于上帝的爱,不要忘记你的旧URL。原因是,当您在新网站上翻转开关及其漂亮的URL时,搜索引擎保存的所有聚合内容将慢慢过期,从而使您的SEO统计数据下降。

  

更新:如果我尝试每个视图坚持使用一个控制器......那么我会将每个请求的代码保持在最低限度,但这是最好的方法吗?

如果我理解正确,每个控制器都会生成一页。这可能是一个非常糟糕的主意,我亲身体验过维护。如果你的内容与OO范例并不完全匹配,那么将它放入一个类别:新闻发布,一次性,信息等,并将这些内容粘贴到一个控制器中,该控制器只提供那些视图或更好的控制器类别,以留下更细粒度的控制。

我处理的“MVC”框架让一个控制器快速查看变成了一个令人费解的混乱,包含大量黑客和意大利面条代码。

  

我有大约12个表单页面用于创建“计划”。

我必须做一些类似的事情,看起来很傻,拥有一个“计划形式”控制器可能是最好的。是的,有多个表可以提供,但你可以做一些简单的事情,比如$ _SESSION ['plannerController'] ['subject | action'] [key] [value]来保持整个表单的各个部分。让每个方法处理多页表单的一部分也是一个好处(例如,如果你的老板说他们只需要12页中的6页,或者如果第7部分变得非常复杂并且需要像autocompletion ajax这样的东西,那该怎么办)。 / p>

  

分组机制不一定使用它,因为它们有相互关联的东西。

我通常喜欢一个类,一个目的范例,但有时尝试这样做是没有意义的。在某些情况下,它可以直接从教科书OO中删除,而其他时候学术BS可以下地狱。你会找到不适合OO模具的东西,所以你可以退后一步,尝试将东西合并到常见的类别中。

答案 2 :(得分:2)

  

每个控制器有一个视图有什么意义?

再次:

  1. 您没有加载超过每个请求所需的代码。你会加载一个你不需要的库,包,DLL吗?当然不是。因此,不要创建一个庞大的控制器文件,其中每个请求只执行一小部分代码。此外,较小的文件更易于维护imo,就像较小的方法和模块化代码一样。
  2. 控制器和视图之间有一个明确的1:1链接(特别是如果它们共享相同的名称)。这是一个惯例。它清晰而一致。如果我正在查看视图模板,我确切地知道仅根据文件名加载它的控制器。没有任何想法。没有决定。没有妥协。
  3.   

    当一切都是配对匹配时,有什么需要分离关注点?

    有什么需要分离关注点?如果要对相关页面(控制器/视图)进行分组,请将它们放在目录中。

      

    我更倾向于编写一个控制器来控制几个以某种方式相关的视图。例如,上述添加/查看/编辑用户。您希望将类似的功能保留在一起,而不是在许多文件中搜索所需的代码。在一个地方定义所有方法(对于特定对象)也很方便。使维护更容易。

    我会恭敬地不同意。如果我有一个负责添加/查看/编辑用户的视图,那么使用1:1约定我完全知道控制器负责。另一方面,使用您对类似功能进行分组的建议,如果我有一个管理员控制器和一个用户控制器,哪一个包含管理员的添加/查看/编辑?用户还是经理?现在你必须思考或搜索。

      

    我使用PHP框架开发了一个项目,该框架根据'action'创建了一个单独的文件。命名为'object(action)',它成为了一个可以维护的NIGHTMARE。

    我不是在暗示。

      

    我一直在使用Django一段时间,它将所有模型保存在一个文件中,所有视图(控制器)保存在一个文件中,模板(视图)分开保存。 [Django不是MVC,但出于这些目的,让我们假装它是]。这允许您将公共代码组合在一起,维护变得更加容易。

    我现在感觉很尴尬。我不知道Django,我假设单个文件是可选的,但是我没办法维护一个包含数万行的文件。

      

    我唯一的建议是 - 不要试图根据一些理想的MVC来组织你的项目。组织您的项目对您和您的域有意义。

    不,不,不。这是非常危险的建议。设计模式,编码约定和框架是为了一个目的而设计的 - 最佳实践和一致性。只有一个大师应该走出公约,并且只有他/她独自工作。即使在框架的范围内,我也不断努力提高一致性,以便在编写或维护代码时不必考虑。

答案 3 :(得分:1)

在这样的举动中,你不仅要改变特定网站的实现,而且要改变它的感知方式。无论如何,你将不得不进行大量的重新编码,但你有理解这个概念的经验并且有如何去做的经验。

我的建议是坐下来假装你对如何实施网站一无所知,但是关于哪些问题与你正在处理的网站类型相关的一切 - 它的要求和问题。创建一个基于MVC的概念化方法,然后尝试重用尽可能多的代码。代码重用可能最终不可行,但从好的方面来说,至少你之前已经解决了许多相同的问题,因此实现起来不应该花费很长时间。

不要忘记,您始终可以使用路由来维护一个合理的URL结构,例如使用不同控制器的一系列表单页面,允许您使用不同的控制器,操作和视图,同时仍然可以从相同的布局或模板。

答案 4 :(得分:1)

每个控制器有一个视图有什么意义?当一切都是配对匹配时,有什么需要分离关注点?

我更倾向于编写一个控制器来控制几个以某种方式相关的视图。例如,上述添加/查看/编辑用户。您希望将类似的功能保留在一起,而不是在许多文件中搜索所需的代码。在一个地方定义所有方法(对于特定对象)也很方便。使维护更容易。

我使用PHP框架开发了一个项目,该框架根据'action'创建了一个单独的文件。命名为'object(action)',它成为了一个可以维护的NIGHTMARE。

我一直在使用Django一段时间,它将所有模型保存在一个文件中,所有视图(控制器)保存在一个文件中,模板(视图)分开保存。 [Django不是MVC,但出于这些目的,让我们假装它是]。这允许您将公共代码组合在一起,维护变得更加容易。

我唯一的建议是 - 不要试图根据一些理想的MVC来组织你的项目。组织您的项目对您和您的域有意义。提出MVC是为了限制复杂性,而不是增加它。

对于您的计划示例,我会将其转换为控制器。 www.example.com/plan/1 ../ plan / 2等它是有道理的,因为这些行为在逻辑上组合在一起形成一些任务。

在我看来,控制器应该用于管理任务或对象。它的方法应该是完成该任务或修改/使用对象所需的特定操作。

答案 5 :(得分:1)

去年我经历过这样的事情。我将大多数静态PHP网站页面转换为使用Kohana PHP Framework。我将每个网站部分都设为一个控制器,其中包含各个页面的视图。主页面视图包括页眉和页脚的其他视图。某些视图(例如文章)可由不同的控制器重复使用。结果是一个MVC站点,其页面URL与原始网站相同。

编辑:URL的格式为/ controller / method?arguments。例如,在我的网站上,/ computer /article.php?id =#的URL使用计算机控制器中的文章功能。反过来,计算机控制器使用文章模型将数据加载到具有嵌套文章视图中观点。这也说明即使网站的先前版本在URL中具有带.php扩展名的页面名称,这仍然可以转换为控制器类方法,并且相同的URL在基于MVC的站点版本中工作。这应该可以让您了解Kohana如何为您的网站工作。

答案 6 :(得分:1)

  

我还有一些问题,何时决定使用控制器而不是动作。一个很好的例子就是这个stackoverflow网站。在页面顶部,您有一个“问题”选项,我们可以假设将您带到“问题”控制器。我这样说,因为在右侧你可以选择“提出问题”,URL指向“问题/提问”。因此,使用问题控制器的ask方法是有道理的。

好吧,我们不知道“问”是否是“问题”控制器的动作。该站点可以将“问题/询问”URL路由到不同的控制器,或者“ask”可以是“questions”目录中的控制器。此外,如果您打开“问题/询问”页面,您会注意到表单会被发布到“问题/询问/发布”。现在“post”可以是“ask”动作方法的参数,但我猜它是“ask”控制器的“post”动作方法。

谁知道呢。但请考虑一下:“问题”页面需要的代码数量是“提问”页面的几倍。加载“提问”页面时加载“问题”页面代码是否有意义。我认为stackoverflow的人会比那更聪明。

  

但令我困惑的是,你在菜单上有“未答复”选项。它看起来像是一个控制器。为什么它不会只是问题控制者的行动?如“问题/未答复”?这就是我的事情变得混乱的地方。

我认为“提问”页面有自己的控制器的另一个原因。 “问题”页面与“未答复”页面有更多共同之处。

答案 7 :(得分:0)

  

如果我理解正确,每个控制器都会生成一页。这可能是一个非常糟糕的主意,我亲身经历过维修工作。

     

我处理的“MVC”框架让一个控制器快速查看变成了一个令人费解的混乱,包含大量黑客和意大利面条代码。

这是因为框架,开发人员还是两者兼而有之?维护起来有多难?涉及什么黑客,为什么?

1个控制器到1视图在php MVC框架Zend,CodeIgniter和Kohana中适合我。在ASP.NET中,虽然不是MVC,但是1个webform / view被映射到文件后面的1个代码。

答案 8 :(得分:0)

Nemetral的一系列三篇博客文章让我更清楚了整个概念 这解释了模式从传统的Web页面脚本演变而来,因此PHP逻辑与表示逻辑分离,因此就MVC而言,模型如何与视图分离。 根据Nemetral的说法:

  1. 第一步是将所有到目前为止的所有PHP代码与HTML标记一起移动到页面的头部。
  2. 第二步是将所有HTML标记移动到单独的文件,并通过PHP include访问它。因此,当发出请求时,它将被定向到PHP代码(控制器和模型),然后此代码将请求HTML标记或表示(视图)。
  3. 我在论文中写了更多关于此的内容:http://kreus-cms.com/kreus/pages/written_work

答案 9 :(得分:0)

假设您手头有某种测试,涵盖了应用程序的大多数(全部?)功能。

  1. 充实了一个关于C部件外观的基本结构。
  2. 实现此结构忽略M和V,或多或少只是将代码片段从面向页面的文件复制到控制器中
  3. 测试一切是否仍按预期工作
  4. 浏览每个控制器,提取“M”和“V”。 “M”可能只是一个活动记录(我知道,这不是一个真正的模型)或行表网关或实现起来非常快的东西。控制器现在应该变得更薄更薄
  5. 测试一切是否仍按预期工作
  6. 现在您已了解应用程序的所有内容,请再次重构以提取域模型(如果适用)。在某些情况下,这可能只是浪费时间(仅限CRUD /密集型应用程序)
  7. 测试一切是否仍按预期工作。

答案 10 :(得分:-1)

  

我遇到的问题最多的是决定何时实际创建一个控制器而不仅仅是一个动作,因为它并不总是如此清晰。

尽可能在每个视图中使用一个控制器。那样:

  1. 您没有加载超过每次请求所需的代码
  2. 控制器和视图之间有一个明确的1:1链接(特别是如果它们具有相同的名称)。