如何在核心php中实现mvc [英] how to implement mvc in core php
问题描述
在没有任何框架的情况下,如何在php中使用mvc架构?
how is mvc architecture used in php without any framework?
推荐答案
更新2020-02-11 :重构答案以包含一些最佳实践,并且更接近PHP 7.4.
Updated 2020-02-11: Refactoring the answer to include a few best practices and being closer to PHP 7.4.
成千上万的单词不能与一个干净的例子竞争,所以这是一个简单的用例:
Thousands words does not compete with a clean example, so here is a simple use case:
想象一下,您想显示一个描述来自假想汽车供应商的汽车"(给定汽车ID")的页面: http://example.com/car.php?id=42 (将为
Imagine you want to display a page describing a "car" (given a "car id") from an imaginary car vendor: http://example.com/car.php?id=42 (will be http://example.com/car/42 later).
基本上,您可以使用类似以下的层次结构来构造代码:
Very basically, you can structure your code with an hierarchy like:
配置目录(这不是MVC架构模式的一部分):
A configuration directory (this isn't part of the MVC architectural pattern):
+ config/
- database.php
<?php
return new PDO(getenv("DB_DSN"), getenv("DB_USER"), getenv("DB_PASSWORD"));
文档根目录的文件夹(脚本的作用类似于 Controllers ):
A folder for your document root (scripts acting like Controllers):
+ htdocs/
- car.php
<?php
$carService = new CarService(require "config/database.php");
$car = $carService->getById($_GET["id"]);
require "car.php";
包含模型/业务逻辑的文件夹(提示:瘦控制器,胖模型"):
A folder encapsulating your Model/business logic (hint: "Thin Controllers, Fat model"):
+ 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);
}
}
包含您所有视图(/模板)的最后一个文件夹:
A last folder containing all your Views(/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配置为:
For the code above to work, you will need PHP to be configured with:
include_path="/the/path/to/src:/the/path/to/views"
走得更远
好网址
您可能想要漂亮的URL,如果使用Apache,则可以通过以下方式实现:
To go further
Nice URLs
You might want nice URLs, if using Apache you can achieve this with:
RewriteEngine On
RewriteRule ^/car/(\d+)$ /car.php?id=$1 [L]
这使您可以编写诸如 http://example.com/car/42 之类的网址,内部转换为 http://example.com/car.php?id=42
This enables writing URLs like http://example.com/car/42 which will be internally converted to http://example.com/car.php?id=42
在上述解决方案中,全局范围中包含了car.php
,这就是为什么$car
直接可用,而$carService
也是如此的原因.
In the above solution, car.php
is included from the global scope, that is why $car
is directly available, but $carService
too!
限制模板访问内容的一种方法是将其转换为类:
One way to restrict what the templates may access is to transform it as a class:
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等...,您可以将其与 require()
以避免重复的HTML.
Reusing views
Using plain PHP files as templates, nothing prevents you from creating headers.php, footers.php, menu.php,... which you can reuse with include()
/require()
to avoid duplicated HTML.
使用类,可以通过将它们组合来获得可重用性,例如,LayoutView
可以负责全局布局,然后调用另一个View
组件:
Using classes, re-usability can be obtained by combining them, for example, a LayoutView
can be responsible for the global layout, and, in turn, calls another View
component:
<?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可以像这样实现:
and CarView could be implemented like:
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
}
}
反过来,控制器会像这样使用它:
In turns, the controller would use it like this:
htdocs/car.php
:
<?php
$carService = new CarService(require "config/database.php");
(new LayoutView("en"))(
new CarView($carService->getById($_GET["id"]))
);
结论
这远非生产就绪型代码,因为这些示例未解决其他方面:依赖注入/控制反转(IoC),输入过滤,类自动加载,名称空间等.此答案的目标是尽可能地将重点放在MVC的主要方面.
Conclusion
This is far from being production-ready code, as other aspects aren't addressed by those examples: dependency injection/inversion of control (IoC), input filtering, class autoloading, namespaces,... The goal of this answer is to focus as much as possible on the main aspect of MVC.
这与拉斯穆斯·勒多夫(Rasmus Lerdorf)提到的精神非常相似: https://toys.lerdorf.com/the-no-framework-php-mvc-framework .
This is very much in the same spirit as Rasmus Lerdorf mentioned on: https://toys.lerdorf.com/the-no-framework-php-mvc-framework.
请不要忘记MVC仍然是模式.软件模式是解决常见问题的可重用原理,如果它们是可重用的 code ,则它们将被命名为库".
One should not forget that MVC remains a pattern. Software Patterns are reusable principles to solve common problems, if they would be reusable code, they would have been named "libraries" instead.
诸如Zend Framework,Symfony,Laravel,CakePHP之类的框架提出了一种采用MVC方法但不能强制实施的结构,MVC是关注点分离的特殊情况. 学习并了解以实现.
Frameworks like Zend Framework, Symfony, Laravel, CakePHP and the likes proposes a structure to adopt an MVC approach but can't enforce it, MVC, as a special case of Separation of concerns needs to be learned and understood to be achieved.
这篇关于如何在核心php中实现mvc的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!