如何在核心php中实现mvc [英] how to implement mvc in core php

查看:80
本文介绍了如何在核心php中实现mvc的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在没有任何框架的情况下,如何在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆