如何在核心php中实现mvc

2023-11-22

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


更新于2020-02-11:重构答案以包含一些最佳实践并更接近 PHP 7.4。

最简单的 PHP MVC 方法

数千个单词无法与一个干净的示例竞争,因此这里是一个简单的用例:

想象一下,您想要显示一个描述来自虚构汽车供应商的“汽车”(给定“汽车 ID”)的页面:http://example.com/car.php?id=42(将http://example.com/car/42 later).

基本上,您可以使用如下层次结构来构建代码:

配置目录(这不是 MVC 架构模式的一部分):

+ config/
  - database.php
        <?php
        return new PDO(getenv("DB_DSN"), getenv("DB_USER"), getenv("DB_PASSWORD"));

文档根目录的文件夹(脚本的作用类似于控制器):

+ htdocs/
  - car.php
        <?php
        $carService = new CarService(require "config/database.php");
        $car = $carService->getById($_GET["id"]);
        require "car.php";

一个文件夹,封装了你的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);
            }
        }

最后一个文件夹包含您的所有内容Views(/模板):

+ 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]

这使得可以编写如下 URL:http://example.com/car/42这将在内部转换为http://example.com/car.php?id=42

视图作为类

在上述解决方案中,car.php包含在全球范围, 因此$car是可以直接使用的,但是$carService too!

限制模板可以访问的内容的一种方法是将其转换为类:

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 仍然是一种pattern。软件模式是解决常见问题的可重用原则(如果它们是可重用的)code,它们将被命名为“图书馆”。

Zend Framework、Symfony、Laravel、CakePHP 等框架提出了一种采用 MVC 方法的结构,但无法强制执行它,MVC 作为一种特殊情况关注点分离需要是learned and 明白了要实现的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在核心php中实现mvc 的相关文章

随机推荐

  • 如何构建谷歌breakpad

    我完全不知道如何构建谷歌的breakpad 有一个 sln 文件 但它依赖于一个似乎没有关联 sln 的库 它似乎使用了一种叫做 gyp 的东西 我还没弄清楚如何开始工作 我试过 python gyp breakpad src client
  • Angular 7 项目无法在 Internet Explorer 11 中运行

    我已经通过互联网完成了不同帖子中提到的所有操作 但我的问题仍然存在 我取消了 IE 9 10 11 所需的 polyfill 的注释 IE9 IE10 and IE11 requires all of the following polyf
  • 如何在 OpenLayers-3 中使用 SVG 图像作为地图标记?

    我正在尝试使用 SVG 图像在 OpenLayers 3 OL3 中创建地图 针滴 即地图标记 目前 我使用 PNG 图像作为引用 ol style Icon 源 src 属性的 pindrop 效果很好 但是 使用 SVG 图像时会失败
  • 在 C++ 中搜索并插入具有 3 个元素的映射

    我需要一张这样的地图 typedef std map
  • 在 swagger 中过滤 API 部分

    我有一个 REST API 和 springfox swagger v2 6 1 并正在运行 但现在 我不想总是显示我拥有的所有控制器 因为其中一些控制器技术性很强 不适合普通用户 但我希望能够选择我显示的内容 而无需重新编译代码 页面顶部
  • laravel Livewire 线:单击不触发该功能

    我想用 laravel livewire 做一个 SPA 我想使用wire click 来触发组件中的一个函数 但它不起作用 如果代码混乱 请原谅 这是我第一次在这里发布 我不确定要发布什么这是我的代码来解决这些问题谢谢 main blad
  • 使用 h5py 将光栅图像添加到 HDF5 文件

    如果这是一个新手问题 我很抱歉 但我对 Python 和 HDF5 相当陌生 我正在使用 h5py numpy 和 Python 2 7 我有来自各种文件的数据需要导入到一个 HDF5 文件中 每个文件的数据将存储在不同的组中 每个组都需要
  • SKView 和 SKScene 有什么区别

    来自苹果文档 SK视图 SKView 对象是显示 Sprite Kit 内容的视图 此内容由 SKScene 对象提供 SK场景 SKScene 对象代表 Sprite Kit 中的内容场景 有什么不同 SKScene 类似于视图控制器 S
  • GCM 如何使用 GCM 和第 3 方服务器取消注册设备

    我有一个使用 GCM 推送通知的应用程序 它工作正常 我的设备注册并接收推送消息 如果我从设备上卸载该应用程序 我将不再像您期望的那样收到消息 在我卸载应用程序后 您在服务器上发送消息的文本框仍然存在 这也是我所期望的 我查看了有关取消注册
  • 经典ASP超级菜鸟问题

    好的 在花了几年时间使用一种非常简单的专有语言进行编码之后 我最近进入了 Web 开发领域 我的第一份工作就是为我们的一个客户调整一个旧的经典 ASP 页面 我正在使用 Visual Studio 2008 尝试调试遇到的一些问题 但页面根
  • 如何在 Ag-Grid 中隐藏排序顺序指示器?

    我正在使用 AgGrid 和 ag grid angular 15 0 0 我的所有专栏都是可排序的 Ag grid 在每个标题中打印一个数字 指示排序顺序 如何隐藏这个 我正在使用suppressMultiSort true 因为我只想按
  • Rails 使用 Twitter Bootstrap 设计 I18n Flash 消息

    你好 我是 Ruby on Rails 的新手 我正在努力理解 I18n 的 Flash 消息 我正在使用 devise rails 4 和 twitter bootstrap 我知道 devise 仅使用flash notice and
  • Swift 4 计时器因 NSException 崩溃

    我一直在寻找一种在 Swift 4 中使用计时器的方法 并查看了this文章 我在 xcode 中测试了我的代码 当计时器第一次计时 在本例中是 10 秒后 时 应用程序崩溃并收到错误 尽管构建成功 2017 11 20 19 54 42
  • 扩展设备注册控制器

    我有一个Accounts使用 Devise 设置模型 Devise 使用一些属性 例如电子邮件 密码等 但我还创建了一些其他属性 注册时 我想以某种方式设置它们 如何扩展注册控制器 我知道我需要实际创建一个像这样的新控制器 class Ac
  • Linux 中如何找出哪些进程正在使用交换空间?

    在Linux下 如何找出哪个进程使用交换空间更多 我发现的最好的脚本在此页面上 http northernmost org blog find out what is using your swap 这是脚本的一种变体 不需要 root b
  • 如何将 Python 框架构建与 Anaconda 结合使用

    我无法在 Mac 计算机上显示 matplotlib 动画 我已经多次尝试安装 ffmpeg 但都失败了 我开始怀疑这是否是解决问题的错误方法 I found this page 由于我从 Anaconda 中的 Spyder 应用程序运行
  • 随机数和下取整函数与轮函数

    为什么如果我使用随机数生成器和范围 0 9 我不会得到与下限函数组合相同的均匀分布 Math floor Math random 10 给出相当均匀的分布 同时Math round Math random 10 没有 Math floor
  • 在 Startup.cs 中添加 DbContextOptions 不注册数据存储

    我的问题是下面的代码在启动期间没有注册数据存储 这是我在应用程序响应中得到的特定 错误 语句 An unhandled exception occurred while processing the request InvalidOpera
  • 以真实分辨率将 MKMapView 渲染为 UIImage

    我正在使用此函数将 MKMapView 实例渲染为图像 implementation UIView Ext UIImage renderToImage UIGraphicsBeginImageContext self frame size
  • 如何在核心php中实现mvc

    没有任何框架的php如何使用mvc架构 更新于2020 02 11 重构答案以包含一些最佳实践并更接近 PHP 7 4 最简单的 PHP MVC 方法 数千个单词无法与一个干净的示例竞争 因此这里是一个简单的用例 想象一下 您想要显示一个描