【发布时间】:2009-12-23 09:44:49
【问题描述】:
没有任何框架的php如何使用mvc架构?
【问题讨论】:
-
有意分离模型、视图和控制器代码。
-
我想你可能想在这里看到一个类似的问题:stackoverflow.com/questions/1497497/…
标签: php
没有任何框架的php如何使用mvc架构?
【问题讨论】:
标签: php
2020 年 2 月 11 日更新:重构答案以包含一些最佳实践并更接近 PHP 7.4。
千言万语无法与一个干净的例子竞争,所以这里有一个简单的用例:
假设您想显示一个描述假想汽车供应商的“汽车”(给定“汽车 ID”)的页面:http://example.com/car.php?id=42(稍后将是 http://example.com/car/42)。
基本上,您可以使用以下层次结构构建代码:
一个配置目录(这不是 MVC 架构模式的一部分):
+ config/
- database.php
<?php
return new PDO(getenv("DB_DSN"), getenv("DB_USER"), getenv("DB_PASSWORD"));
文档根目录的文件夹(类似于 Controllers 的脚本):
+ htdocs/
- car.php
<?php
$carService = new CarService(require "config/database.php");
$car = $carService->getById($_GET["id"]);
require "car.php";
一个封装您的模型/业务逻辑的文件夹(提示:“瘦控制器,胖模型”):
+ src/
- CarService.php
<?php
class CarService {
private PDO $database;
public function __construct(PDO $database) {
$this->database = $database;
}
public function getById(int $id): CarEntity {
return $this->database->query(
"SELECT model, year, price " .
"FROM car " .
"WHERE id = $id"
)->fetch(PDO::FETCH_CLASS, CarEntity::class);
}
}
包含所有视图(/templates)的最后一个文件夹:
+ views/
- car.php
<!DOCTYPE html>
<html>
<head>
<title>Car - <?= htmlspecialchars($car->model) ?></title>
</head>
<body>
<h1><?= htmlspecialchars($car->model) ?></h1>
Year: <?= htmlspecialchars($car->year) ?>
Price: <?= htmlspecialchars($car->price) ?>
</body>
</html>
要使上述代码正常工作,您需要配置 PHP:
include_path="/the/path/to/src:/the/path/to/views"
您可能需要漂亮的 URL,如果使用 Apache,您可以通过以下方式实现:
RewriteEngine On
RewriteRule ^/car/(\d+)$ /car.php?id=$1 [L]
这可以编写像 http://example.com/car/42 这样的 URL,这些 URL 将在内部转换为 http://example.com/car.php?id=42
在上述解决方案中,car.php 包含在全局范围中,这就是为什么$car 可以直接使用,但$carService 也是如此!
限制模板可以访问的一种方法是将其转换为一个类:
views/CarView.php:
<?php
class CarView {
private CarEntity $car;
public function __construct(CarEntity $car) {
$this->car = $car;
}
public function __invoke(): void {
?>
<!DOCTYPE html>
<html>
<head>
<title>Car - <?= htmlspecialchars($this->car->model) ?></title>
</head>
<body>
<h1><?= htmlspecialchars($this->car->model) ?></h1>
Year: <?= htmlspecialchars($this->car->year) ?>
Price: <?= htmlspecialchars($this->car->price) ?>
</body>
</html>
<?php
}
}
然后调整控制器:
htdocs/car.php:
<?php
$carService = new CarService(require "config/database.php");
$view = new CarView($carService->getById($_GET["id"]));
$view();
使用普通的 PHP 文件作为模板,没有什么能阻止您创建 headers.php、footers.php、menu.php...,您可以通过 include()/require() 重复使用它们以避免重复的 HTML。
使用类,可以通过组合获得复用性,例如一个LayoutView可以负责全局布局,进而调用另一个View组件:
<?php
class LayoutView {
protected string $lang;
public function __construct(string $lang) {
$this->lang = $lang;
}
// __invoke(): for embracing the "Single Responsibility" principle
public function __invoke(View $view): void {
?>
<!DOCTYPE html>
<html lang="<?= $this->lang ?>">
<head>
<meta charset="utf-8" />
<title><?= htmlentities($view->getTitle()) ?></title>
</head>
<body>
<?php ($view)(); ?>
</body>
</html>
<?php
}
}
CarView 可以这样实现:
views/CarView.php:
<?php
class CarView implements View {
private CarEntity $car;
public function __construct(CarEntity $car) {
$this->car = $car;
}
public function getTitle(): string {
return $this->car->model;
}
// __invoke(): for embracing the "Single Responsibility" principle
public function __invoke(): void {
?>
<h1><?= htmlspecialchars($this->car->model) ?></h1>
Year: <?= htmlspecialchars($this->car->year) ?>
Price: <?= htmlspecialchars($this->car->price) ?>
<?php
}
}
反过来,控制器会这样使用它:
htdocs/car.php:
<?php
$carService = new CarService(require "config/database.php");
(new LayoutView("en"))(
new CarView($carService->getById($_GET["id"]))
);
这远不是生产就绪代码,因为这些示例没有解决其他方面:依赖注入/控制反转 (IoC)、输入过滤,类自动加载,命名空间,......这个答案的目标是尽可能多地关注 MVC 的主要方面。
这与 Rasmus Lerdorf 在https://toys.lerdorf.com/the-no-framework-php-mvc-framework 中提到的精神非常相似。
不要忘记 MVC 仍然是一种模式。软件模式是解决常见问题的可重用原则,如果它们是可重用的代码,它们将被命名为“库”。
Zend Framework、Symfony、Laravel、CakePHP 等框架提出了一种采用 MVC 方法但不能强制执行的结构,MVC 作为关注点分离的特殊情况需要学习和理解才能实现。
【讨论】:
我认为通常使用其中一种通用框架可能是可行的方法。原因是许多优秀的开发人员花费了很长时间来编写、修复错误、调整和完善,以创建一些可靠的东西来作为您网站的基础。最好的办法是找到一个你喜欢的,学习它并坚持下去(除非你找到不这样做的理由)。当我使用 PHP 时,我的选择一般是Zend Framework,但也有CodeIgniter、Symfony、CakePHP 和bunch of others。
如果您仍然想在没有现有框架的情况下使用 MVC 模式,您可以选择将自己的关注点放在一起,或者只是在逻辑上将每个关注点彼此分开 - 这是 MVC 的核心原则,框架只是帮助你实现它。
Rasmus Lerdorf wrote about his minimal approach to the MVC pattern in PHP 2006 年。可能值得一读。您可能还对诸如 F3::PHP(仅限 PHP 5.3+)之类的小型框架感兴趣 - 看起来很有前途。
【讨论】:
您可以查看PHP MVC Tutorial,了解如何从头开始使用简单的 MVC 模式,而不是在现有框架中。
【讨论】:
不是。核心 PHP 是一种“开始于全局命名空间语句和面向表达式的语言”。您需要额外的代码(和一个可选的 URL 重写器)来实现任何类型的 MVC 架构。额外的代码就是你的框架。
【讨论】:
要实现 MVC 模式,您只需将数据持久性代码(“模型”,主要是数据库内容)、主要应用程序逻辑(“控制器”)和向外部世界的呈现(“视图”,如 HTML页面或 RSS 源)。
如果您只是不在代码中混合这三个部分,那么您已经拥有了一个非常基本的 MVC 架构。只需为您的模型、视图和控制器层构建不同的类,提出一种结构良好的方式,它们如何相互通信,然后坚持下去!
为了代码的可维护性,您应该始终尝试以这种方式工作。
【讨论】:
尝试在您的核心代码中集成 Pear DB Layer、Smarty、PHP GACL 以实现 MVC 架构。
【讨论】:
通过编写您自己的 MVC 框架,该框架采用 MVC 模式和 OOP 原则 :)
您需要有前端控制器,以便每个 HTTP 请求都通过一个文件,index.php、app.php 或任何您想要的文件。这样您就可以在一个地方配置应用程序。
从那里你需要路由机制来分析 HTTP 请求、当前 URL、HTTP Header 动词/方法,并在此基础上调用适当的控制器方法/动作控制器。
从 Controller,您可以访问将处理“繁重”、处理数据库和域/业务逻辑等的模型。从 Controller,您可以渲染视图。
因此,您至少需要 Front Controller、Router/Dispatcher、Controller、Models 和 Views 才能拥有简单的 MVC 架构。
您可以像其他 MVC Web 框架一样进行这种较量,根据您的偏好略有不同。
看看一些简单的框架,例如 Codeigniter,并阅读它们的源代码以了解它们是如何进行 MVC 的。
并享受构建您的 MVC 的乐趣!毕竟这一切都是为了乐趣:D
【讨论】: