2. Web/PHP 的 MVC 开发方法
在此,我们提出了一种开发遵循MVC架构的Web/PHP应用程序的方法。该方法仅作为入门参考,读者应根据自身偏好和需求进行调整。
- 我们将首先定义应用程序的所有视图。这些视图即呈现给用户的网页。在设计视图时,我们将采用用户的视角。视图主要有三种类型:
- 输入表单,用于收集用户信息。通常包含一个按钮,用于将输入的信息提交至服务器。
- 响应页面,仅用于向用户提供信息。该页面通常包含一个或多个链接,允许用户在其他页面上继续使用应用程序。
- 混合页面:控制器已向客户端发送了一个包含其生成的信息的页面。客户端将使用该页面向控制器提供来自用户的新信息。
- 每个视图都会生成一个 PHP 页面。针对每个视图:
- 我们将设计页面的布局
- 我们将确定其中哪些部分是动态的:
- 即控制器必须作为参数传递给 PHP 视图、供用户查看的信息。一个简单的解决方案如下:
- 控制器将要提供给视图 V 的信息放入字典 $dResponse 中
- 控制器渲染视图 V。如果该视图对应源文件 V.php,则只需通过 include V.php 语句即可实现渲染。
- 上述包含操作是在控制器内部进行的代码包含。由控制器填充的 $dResponse 字典可被 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
// initializations for testing without a controller
...
?>
<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 代码。这些代码通常采用以下形式:
<?php
// a few initializations may be necessary, particularly in the debugging phase
...
?>
<balise>
...
// here we'll try to minimize the php code
</balise>
请注意,基本视图是嵌入在模板中的。其 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 应用程序的部署架构中,并测试其是否正确集成。我们对每个业务类都按此方式进行操作。

