在PHP中如何实现MVC模式

3

介绍

自从假期之前,我一直在思考模型-视图-控制器(MVC)方法,并且我真的需要在我的使用PHP创建的Web应用程序中熟练掌握它。

到目前为止,我理解了一般的MVC概念及其原因,但我需要帮助。这是一个大学项目,当我问关于MVC的问题时,我的项目顾问并不是非常有帮助。

应用程序

这是一个任务管理系统或待办事项列表。目标是:用户可以使用他们的Facebook账户登录,创建、修改或删除任务,然后注销,该应用程序由JavaScript和PHP创建,拥有一个非常简单的界面。

事实

我有一个静态的PHP页面here,其中包含像“top_div”和“main_div”之类的div。目标是将“controller.php”包含(我还没有创建此文件)以在视图和模型之间进行服务。

重要提示:页面上的所有内容都使用jQuery,因此用户永远不会看到页面刷新。我有一个函数,可以淡出div中的内容,获取新请求的内容,将其放置在div中(隐藏,因为div已经淡出),然后再次淡入div。

目标是控制器从模型请求,然后发送回(例如)Facebook登录按钮到视图。这个Facebook登录按钮是HTML。还将有其他的内容,如文本和html,并且如果用户已经登录,“Welcome”将使用PHP、jQuery和cURL发送到#main_div中。

我正在使用对象,所以根据我理解,我需要实例化对象来做数据库连接和查询。在哪里实例化对象?我读到它们应该在控制器中创建,但从我读到的所有其他内容来看,控制器应该简单明了,只告诉模型视图想要什么,并充当中间人。我认为(并且这对我更有意义)是在模型中实例化对象。

1)因此,请解释一下在模型中实例化对象是否不正确。如果不是这种情况,那么对我的MVC方法的理解来说会更好。

假设用户已经登录。我将在视图(index.php)上显示他们的姓名和Facebook图片。如果您访问上面链接中的网站,则可能会看到一个小白框,在那里将显示此图片。用于用户图片的PHP代码如下:

<img src="https://graph.facebook.com/'.$fbid.'/picture" width="79" height="64" align="center" style="opacity:0.8;">

2) 由于这个HTML将被传递给视图,使用上面描述的jQuery函数在DIV中显示,所以我的控制器可以有这种信息输出吗?我的控制器可能会有以下代码:

如果用户已登录(通过模型检查),则 {发送以上HTML到#main_div};

我可能会在路上想到更多问题,但请有人帮助我更好地理解我正在做什么吗?


3
在这里你会得到广泛的回应。事实上,MVC是一种相当简单的模式,可以通过无数种方式来实现。 - dqhendricks
7个回答

1
1) 所以,请解释一下在模型中实例化对象为什么会是错误的。
这并不是完全错误的。目标是控制器轻,模型厚,因此我在控制器中进行对象创建,并让模型/服务来完成繁重的工作。
注意:最好在其他地方进行对象创建,比如在引导程序中,并使用依赖注入将这些对象传递给控制器。话虽如此,我的大部分对象创建都发生在需要的控制器中。
2) 由于这个HTML将被传递给视图,并使用上述jQuery函数显示在一个DIV中,所以我的控制器是否可以有这种信息输出?
是可以的。控制器将从模型中传递数据到视图。
MVC框架推荐
除非你只是想要或必须自己开发MVC框架,否则选择一个现成的框架,进行一些学习,然后取得成功。

1
啊,我还记得当时写这个时一无所知,现在我自己写了,对MVC有更好的理解,使用了Symfony和真正的自动装配依赖注入容器,如[Auryn](https://github.com/rdlowrey/Auryn)。现在我拥有你暗示的胜利,干杯! :) - Jimbo
我不喜欢你关于“臃肿模型”的说法。模型根本不应该臃肿,它们只是代表数据,没有其他的作用... - Pinoniq
完全取决于人们对“模型”的定义,这通常是一个含糊的词。如果将模型定义为数据表示,我完全同意你的观点。然而,将模型用于涵盖应用程序的业务逻辑也不是不合适的。创建薄控制器和厚模型的忠告只是意味着应该将业务逻辑从控制器中分离出来。当我说“模型/服务”应该完成“重活”时,我试图传达这一点。 - Jeremy Kendall
嗯,模型是一个“层”。你没有“模型”。你有一个臃肿的模型和瘦削的控制器,因为控制器只是模型层内经过单元测试的对象之间的粘合剂 - 因此你实际上永远不需要测试控制器。 - Jimbo

1
通常情况下,您的代码应该是基于对象的。 "控制器" 部分将处理用户请求。假设一个人请求 "www.you.com/?login",控制器应该获取到 "login" 请求。控制器(有时称为路由器)将实例化适当的 "模型" 对象来处理登录。在这种情况下,它可能只是创建没有数据告诉控制器显示 "view-login" 的模型(登录)对象。然后,控制器转到视图对象代码并给它正确的视图以显示,视图对象返回呈现的带有登录表单的 HTML 页面。
一旦用户提交了他的凭据,请求将是 "POST:action=login,username=blah,password=12345"。控制器将看到它需要调用登录对象并将参数用户名/密码传递给登录 "模型"。登录模型逻辑将处理参数,然后发送到控制器显示 "view-login,success"。控制器将告诉视图对象显示此视图并附加参数,视图对象将呈现页面并输出它。

这可以在您的应用程序编码期间继续扩展。使用autoload函数,您甚至可以让控制器扫描目录以查找模型/视图并动态实例化请求的对象或回退到默认对象(如果不存在)。这样,您就不需要每次添加模型/视图逻辑时修改控制器。另一个好处是将所有URLS作为应用程序的单个入口点,例如www.you.com/?login或www.you.com/?task=2&action=delete ...等等。多读一些相关资料,在编写自己的MVC时不要感到沮丧。您可能会在真正了解如何使MVC行为符合您的预期之前多次放弃您的工作。

好的阅读材料: http://oreilly.com/php/archive/mvc-intro.html


0

我建议您查看一些其他的库,以使在PHP中使用MVC更容易(至少作为一个示例,让您了解其他人是如何做的)。 一些我想到的是:

  • Zend框架
  • CakePHP

...然后当您准备真正改进您的代码时 ;) ,请查看这些Ruby库:


0

控制器类应该处理服务器的所有传入请求,然后根据请求,在所需的任何模型对象上执行任何操作,然后将任何需要的数据传递给视图对象,然后呈现页面。

有无数种方法可以设置这个。基本思想是将业务逻辑与视图渲染(HTML)分开。

一些提示:

在许多人看来,您不应允许模型自己加载其他模型。相反,您将使用依赖注入模式来减少强耦合。这基本上意味着通过构造函数或方法参数插入任何所需的对象。

很多人会使用所谓的依赖注入容器。这通常是一个类,其中有一个公共方法,用于实例化除控制器类以外的所有其他类。在 PHP 5.3 中,您可以使用闭包来帮助延迟加载从 DI 容器请求的任何类。DIC 类还可以存储对象以供以后重用,如果该对象是将被共享的对象(如单例),则无论如何都应该从 DIC 请求相同的类。这样做的目的是,如果您需要更改类的实例化方式,或者想要将其替换为扩展类或其他使用相同接口的类,则可以在代码中的一个地方完成所有这些修改,而不是分散在许多地方。它还有助于保持您的控制器“轻量级”。

0

让我们从基础知识开始。要构建一个合适的MVC框架,首先需要一个引导程序。这个程序将确定应用程序需要做什么。在完成这一点后,它需要将数据传递给适当的函数(类)。一个好的方法是使用某种路由表,例如正则表达式和一些默认值,当表达式不匹配时指向这些默认值。例如可以是:/module/controller/action/params。引导程序应该处理所有初始化、读取配置文件、确定需要传递到哪里的工作。为了简化编码,你可以创建一个带有名称空间的结构。例如,你需要将控制器放在一个地方,而将模型和视图放在其他地方。由于PHP < 5.3不支持名称空间,所以你可以通过目录结构名称空间轻松处理它。这意味着一个目录是你的名称空间,其中的所有文件都遵循某种模式。这样你就可以轻松地自动加载类。在你有了适当的名称空间之后,将你的类分离在它们各自的名称空间中。你的库类可以放在library/中,你的助手类可以放在helper/等中。所有将处理实际应用逻辑的控制器都可以放在模块命名空间下。例如frontend/和backend/。它们都需要扩展你的基本控制器类(从library/名称空间加载),它将处理视图初始化。


另一种选择是让Apache处理路由,并让控制器自行“启动”。但是,许多“框架”都会通过单个前端控制器运行每个请求,并根据请求路由到适当的控制器。 - dqhendricks

0

首先,你的静态php页面不包含控制器。相反,你的控制器包含你的静态php页面(视图)。

为了保持简单,添加一些

<!--parsableTags!-->

将相应的模型加载到您的视图中以填充这些标记。让控制器将结果返回给调用浏览器。
补充: 控制器中是否应有DB逻辑?这里没有绝对的真理,您的应用程序可能大多数情况下与LDAP通信,那么为什么要加载db服务呢?您想要的可能是一个配置,在启动时由控制器读取以加载初始组件。
一些人认为只有视图应该生成HTML,这是一个好原则,但可能会让你在1点抓狂。
但最终有很多不同的方法来做到这一点。

0

关注点分离。

将渲染HTML的逻辑放在PHP视图文件中。

将JavaScript和CSS放在它们自己的文件中并引用外部文件。

将业务逻辑放在DAO (data access objects)中,只有它们与数据库交互,而不是视图文件。

将控制器创建为普通的PHP页面,例如index.php或controller.php。每个请求都通过索引或控制器文件处理。控制器文件将包含对JavaScript和CSS文件的外部引用。

每次回调控制器都会包括一个参数和变量,用于确定将控制传递到哪里。控制器可能会具有switch-case或if-else结构,用于处理特定请求,类似于以下内容:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script src="assets/ajax.js" type="text/javascript"></script>

<link rel="stylesheet" type="text/css" href="assets/adminApp.css" media="screen" />
<link rel="stylesheet" href="assets/print.css" type="text/css" media="print" />

<?php
  require_once('../../config.php');
  require_once('../connect_db.php');
  require_once('../custom_boces_dao.php');
  require_once('../custom_boces_gateway.php');

  if(isset($_REQUEST['action'])){
    $action = $_REQUEST['action'];  
  }else{
    $action = "viewUserAttributes";
  }

 echo "<div id='leftNav'>";
   require_once('views/global_navigation.php');
 echo "</div>";

 echo "<div id='mainArea'>";

    switch ($action) {

      case 'home':
        echo "something to say";
      break;

      case 'viewCourseAttributes':
        require_once('views/course/frmCourses.php');
      break;

      case 'editCourseAttributes':
        require_once('views/course/frmCourses.php');
        require_once('views/course/frmCourseAttributes.php');

       break;
      .
      .
      .
 }      

 echo "</div>";

?>          

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接