2. 基于 Web/PHP 的 MVC 开发方法
本文提出了一种符合MVC架构的Web/PHP应用程序开发方法。此方法仅供参考,读者可根据自身喜好和需求进行调整。
- 首先,我们将定义应用程序的所有视图。这些视图即呈现给用户的网页。在设计视图时,我们将站在用户的角度出发。视图主要分为三类:
- 用于收集用户信息的输入表单。该表单通常配有一个按钮,用于将输入的信息发送至服务器。
- 响应页面,仅用于向用户提供信息。该页面通常包含一个或多个链接,允许用户通过其他页面继续操作。
- 混合页面:控制器向客户端发送了一个包含其生成的信息的页面。该页面将用于客户端向控制器提供来自用户的新信息。
- 每个视图都会生成一个名为 PHP 的页面。对于每个页面:
- 我们将设计页面的外观
- 我们将确定其中的动态部分:
- 控制器需要作为参数传递给视图 PHP 的、供用户使用的信息。一个简单的解决方案如下:
- 控制器将需要提供给视图 V 的信息放入字典 $dReponse 中
- 控制器调用视图 V 进行显示。如果该视图对应源文件 V.php,则只需通过 include V.php 语句即可实现显示。
- 上述包含操作是在控制器内部进行的代码包含。由控制器填充的字典 $dReponse 可通过代码 V.php 直接访问。
- 需传输至主程序进行处理的输入数据。这些数据应属于表单 HTML(标签 <form>)的一部分。
- 控制器需要作为参数传递给视图 PHP 的、供用户使用的信息。一个简单的解决方案如下:
- 我们可以将每个视图的输入/输出进行示意图化
![]() |
- 输入是控制器需提供给页面 PHP 的数据
- 输出是页面 PHP 需提供给应用程序控制器的数据。 它们属于表单 HTML,控制器将通过类型为 $_GET["param"] (方法 GET)或 $_POST["param"](方法 POST)来获取这些数据。
- 通常,发送给客户端的最终页面并非单个视图,而是多个视图的组合。例如,发送给用户的页面可能具有以下形式:
![]() |
区域 1 可以是标题栏,区域 2 是菜单栏,区域 3 是内容区。在 PHP 中,可以通过以下 HTML/PHP 代码实现该组合:
<table>
<tr>
<td><?php include zone1.php ?></td>
</tr>
<tr>
<td><?php include zone2.php ?></td>
<td><?php include zone3.php ?></td>
</tr>
</table>
可以通过以下方式使该代码动态化:
<table>
<tr>
<td><?php include $dReponse['urlZone1'] ?></td>
</tr>
<tr>
<td><?php include $dReponse['urlZone2'] ?></td>
<td><?php include $dReponse['urlZone3'] ?></td>
</tr>
</table>
这种视图组合可能是向用户返回的唯一响应格式。在这种情况下,每次向客户端返回响应时,都必须先将三个 URL 加载到三个区域中,然后才能显示响应页面。我们可以将这个示例推广,假设响应页面有多种可能的模板。因此,向客户端返回的响应必须:
- 确定要使用的模板
- 确定其中应包含的元素
- 请求显示该模板
- 我们将编写每个响应模板的代码 PHP/HTML。其代码通常很简单。上例中的代码可以是:
<?php
// 用于无控制器测试的初始化
...
?>
<html>
<head>
<title><?php echo $dReponse['titre'] ?></title>
<link type="text/css" href="<?php echo $dReponse['style']['url'] ?>" rel="stylesheet" />
</head>
<body>
<table>
<tr>
<td><?php include $dReponse['urlZone1'] ?></td>
</tr>
<tr>
<td><?php include $dReponse['urlZone2'] ?></td>
<td><?php include $dReponse['urlZone3'] ?></td>
</tr>
</table>
<body>
</html>
在可能的情况下,应使用样式表,以便在不修改代码 PHP/HTML 的前提下更改响应的“外观”。
- 我们将为每个基本视图编写代码 PHP/HTML。该代码通常采用以下形式:
需要注意的是,一个基本视图会嵌入到一个模板中。其代码 HTML 会嵌入到该模板的代码内部。通常情况下,该模板中已经包含了 <html>、<head>、<body> 这些标签。因此,在基本视图中很少会看到这些标签。
- 可以对各种响应模型和基本视图进行测试
- 每个响应模板都会被测试。如果某个模板名为 modele1.php,则需通过浏览器请求 URL http://localhost/chemin/modele1.php。该模板需要接收来自控制器的参数。 此处是直接调用该模型,而非通过控制器。模型将无法收到预期的参数。为了仍能进行测试,我们将使用常量在模型的 PHP 页面中自行初始化预期的参数。
- 每个模型以及所有基本视图都将以此方式进行测试。此时也是制定所用样式表初始内容的时机。
- 接下来编写应用程序的业务逻辑:
- 控制器(或主程序)通常管理多个操作。传入的请求中必须明确指定要执行的操作。这可以通过请求参数实现,此处我们将该参数命名为 action:
- 如果请求来自表单(<form>),该参数可以是表单的隐藏参数:
<form ... action="/C/main.php" method="post" ...>
<input type="hidden" name="action" value="uneAction">
...
</form>
- (续)
- 如果请求来自一个链接,可以对其进行配置:
控制器可以先读取该参数的值,然后将请求的处理委托给负责处理此类请求的模块。这里我们假设一切都由一个名为 main.php 的脚本控制。如果应用程序需要处理 action1、action2、...、 actionx,可以在控制器内为每个操作创建一个函数。如果操作数量众多,可能会导致控制器变得臃肿。也可以创建 action1.php、action2.php、...、actionx.php 等脚本,分别负责处理各个操作。需要处理操作 actionx 的控制器只需通过类似 include "actionx.php" 的语句加载相应的脚本代码。这种方法的优势在于,开发工作是在控制器代码之外进行的。 因此,开发团队的每位成员都可以相对独立地处理 actionx 操作的脚本。在运行时将脚本代码 actionx.php 包含到控制器代码中,还具有减轻内存中加载代码量的优势。 只有当前操作的处理代码会被加载。这种代码包含方式会导致控制器变量与操作脚本中的变量发生冲突。我们将看到,我们可以将控制器变量限制为几个明确定义的变量,并避免在脚本中使用这些变量。
- 我们将系统地将业务代码或访问持久化数据的代码隔离在独立的模块中。控制器就像一位团队负责人,接收来自客户(Web客户端)的请求,并将其分配给最合适的人员(业务模块)执行。 在编写控制器时,需确定待开发的业务模块接口。此步骤仅适用于需新建业务模块的情况。若业务模块已存在,则控制器将适配这些现有模块的接口。
- 我们将编写控制器所需业务模块的框架。例如,如果控制器使用一个返回字符串数组的模块 getCodes,那么起初只需编写:
- 随后即可开始测试控制器及相关的 PHP 脚本:
- 控制器、操作脚本、模型、视图以及应用程序所需的资源(图片等)都放置在与应用程序上下文 C 关联的 DC 文件夹中。
- 完成上述操作后,将对应用程序进行测试并修正初始错误。若main.php为控制器且C为应用程序上下文,则需调用URL http://localhost/C/main.php。 该阶段结束时,应用程序架构即可投入运行。鉴于若不使用通常需要付费的高级开发环境,可用的调试工具十分有限,因此此测试阶段可能较为棘手。 我们可以借助 echo "message" 语句,这些语句会写入发送给客户端的 HTML 数据流中,因此也会显示在浏览器呈现的网页上。
- 最后编写控制器所需的业务类。这通常是经典的 PHP 类开发,通常独立于任何 Web 应用程序。首先将在该环境之外对其进行测试,例如使用控制台应用程序。 当业务类编写完成后,将其集成到Web应用程序的部署架构中,并测试其集成是否正确。我们将对每个业务类都进行此操作。

