【问题标题】:how to implement mvc in core php如何在核心php中实现mvc
【发布时间】:2009-12-23 09:44:49
【问题描述】:

没有任何框架的php如何使用mvc架构?

【问题讨论】:

标签: php


【解决方案1】:

2020 年 2 月 11 日更新:重构答案以包含一些最佳实践并更接近 PHP 7.4。

最简单的 PHP MVC 方法

千言万语无法与一个干净的例子竞争,所以这里有一个简单的用例:

假设您想显示一个描述假想汽车供应商的“汽车”(给定“汽车 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 作为关注点分离的特殊情况需要学习理解才能实现。

【讨论】:

  • 哇!有人可以给这家伙一枚奖章或什么的吗?认真的,优秀的帖子!我想知道为什么总是年轻的新人 - 是的,我真的这么说 - 好看的人比书呆子做出更好的答案。哥们,尊重!
  • 非常感谢@Panique :)
【解决方案2】:

我认为通常使用其中一种通用框架可能是可行的方法。原因是许多优秀的开发人员花费了很长时间来编写、修复错误、调整和完善,以创建一些可靠的东西来作为您网站的基础。最好的办法是找到一个你喜欢的,学习它并坚持下去(除非你找到不这样做的理由)。当我使用 PHP 时,我的选择一般是Zend Framework,但也有CodeIgniterSymfonyCakePHPbunch of others

如果您仍然想在没有现有框架的情况下使用 MVC 模式,您可以选择将自己的关注点放在一起,或者只是在逻辑上将每个关注点彼此分开 - 这是 MVC 的核心原则,框架只是帮助你实现它。

Rasmus Lerdorf wrote about his minimal approach to the MVC pattern in PHP 2006 年。可能值得一读。您可能还对诸如 F3::PHP(仅限 PHP 5.3+)之类的小型框架感兴趣 - 看起来很有前途。

【讨论】:

  • 您建议 PHP 不是 Web 框架,并且 MVC 是通过使用一个来绑定/促进的。当您了解 MVC 只是“关注点分离”的特定情况时,它就实现了。框架不会阻止您在控制器/视图中实现业务逻辑。在大多数其他通用语言中:您没有任何本机来解析 HTTP 请求、操作标头、cookie、会话、缓冲输出...... PHP 的核心包含上述所有内容。邪恶是重新发明(方形)轮子,这可以通过重新使用 libraries 而不是 frameworks 来防止。
【解决方案3】:

您可以查看PHP MVC Tutorial,了解如何从头开始使用简单的 MVC 模式,而不是在现有框架中。

【讨论】:

    【解决方案4】:

    不是。核心 PHP 是一种“开始于全局命名空间语句和面向表达式的语言”。您需要额外的代码(和一个可选的 URL 重写器)来实现任何类型的 MVC 架构。额外的代码就是你的框架。

    【讨论】:

    • 我不同意。 MVC 是一个抽象的概念。你不需要使用面向对象的编程风格来实现它。
    【解决方案5】:
    【解决方案6】:

    要实现 MVC 模式,您只需将数据持久性代码(“模型”,主要是数据库内容)、主要应用程序逻辑(“控制器”)和向外部世界的呈现(“视图”,如 HTML页面或 RSS 源)。

    如果您只是不在代码中混合这三个部分,那么您已经拥有了一个非常基本的 MVC 架构。只需为您的模型、视图和控制器层构建不同的类,提出一种结构良好的方式,它们如何相互通信,然后坚持下去!

    为了代码的可维护性,您应该始终尝试以这种方式工作。

    【讨论】:

    • 模型只是或主要是数据库!该模型是您的实际应用程序。它包含不属于控制器和视图责任的所有内容。这可以是领域对象、服务层、持久层、一切
    • 是的,没错。你可以填满整本书,甚至是关于模型层的书店。但为了简单起见,我认为说“这主要是你的数据库抽象”就足够了,因为 90% 的模型实现实际上都在处理数据库。
    【解决方案7】:

    尝试在您的核心代码中集成 Pear DB Layer、Smarty、PHP GACL 以实现 MVC 架构。

    【讨论】:

    • 1. Pear DB 只处理关系数据库,您的数据可能来自非关系数据库甚至来自 Web 服务,它不涉及“模型”。 2. Smarty是一个模板引擎,不需要有“视图”的原理。 3. PHP GACL 是关于访问列表的,这与 MVC 模式的基础无关。 (不是说不能玩)
    【解决方案8】:

    通过编写您自己的 MVC 框架,该框架采用 MVC 模式和 OOP 原则 :)

    1. 您需要有前端控制器,以便每个 HTTP 请求都通过一个文件,index.php、app.php 或任何您想要的文件。这样您就可以在一个地方配置应用程序。

    2. 从那里你需要路由机制来分析 HTTP 请求、当前 URL、HTTP Header 动词/方法,并在此基础上调用适当的控制器方法/动作控制器。

    3. 从 Controller,您可以访问将处理“繁重”、处理数据库和域/业务逻辑等的模型。从 Controller,您可以渲染视图。

    因此,您至少需要 Front Controller、Router/Dispatcher、Controller、Models 和 Views 才能拥有简单的 MVC 架构。

    您可以像其他 MVC Web 框架一样进行这种较量,根据您的偏好略有不同。

    看看一些简单的框架,例如 Codeigniter,并阅读它们的源代码以了解它们是如何进行 MVC 的。

    并享受构建您的 MVC 的乐趣!毕竟这一切都是为了乐趣:D

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多