【问题标题】:Using Dependency Injection in MVC PHP在 MVC PHP 中使用依赖注入
【发布时间】:2017-07-20 08:44:57
【问题描述】:

对于加载类,我使用 PSR-4 自动加载。在我的index.php 中,我使用 FastRoute 组件。在那个index.php 中,我创建了 $db 连接并将其传递给控制器​​。

call_user_func_array([new $class($db), $method], $vars);

在控制器中,我收到它并将其传递给模型并在那里使用它。 我知道这是不好的方法。在index.php 中创建的数据库连接如何在我的模型中使用它而不是通过控制器等传递它并且没有连接的单例实例?如何使用DI或DIC在index.php建立db连接并进入所有模型?

index.php:

<?php

require_once 'vendor/autoload.php';

$db = new DbConnection(new DbConfig($config));

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    $r->addRoute('GET', '/users', 'UserController/actionGetAllUsers');
    $r->addRoute('GET', '/users/{id:\d+}', 'UserController/actionGetUserById');
});

// Fetch method and URI from somewhere
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];

// Strip query string (?foo=bar) and decode URI
if (false !== $pos = strpos($uri, '?')) {
    $uri = substr($uri, 0, $pos);
}
$uri = rawurldecode($uri);

switch ($routeInfo[0]) {
    case FastRoute\Dispatcher::NOT_FOUND:
        // ... 404 Not Found
        break;
    case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
        $allowedMethods = $routeInfo[1];
        // ... 405 Method Not Allowed
        break;
    case FastRoute\Dispatcher::FOUND:
        $handler = $routeInfo[1];
        $vars = $routeInfo[2];
        list($class, $method) = explode("/", $handler, 2);
        $module = strtolower(str_replace('Controller', '', $class));
        $class = 'Vendorname\\' . $module . '\\controllers\\' . $class;
        $routeInfo = $dispatcher->dispatch($httpMethod, $uri);
        call_user_func_array([new $class($db), $method], $vars);
        break;
} 

控制器:

class UserController extends BaseApiController
{
    protected $db;

    public function __construct($db)
    {
        $this->db = $db;
        parent::__construct();
    }

    public function actionGetAllUsers()
    {
        $model = new User($this->db);
        echo json_encode($model->getAllUsers());
    }

型号:

class User
{
    protected $db;

    public function __construct($db)
    {
        $this->db = $db;
    }

    public function getAllUsers()
    {
        $stmt = $this->db->prepare('SELECT * FROM user');
        $stmt->execute();
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
    }

【问题讨论】:

  • 研究使用一些依赖注入容器,例如PimpleIlluminate/Containerleague/container 或类似名称。这就是当今大多数框架所做的。
  • @MagnusEriksson 但我也应该将其注入控制器,并在我的示例中从控制器将其注入模型?我对 DIC 的理解正确吗?
  • 没有。您应该只注入特定类本身需要的依赖项。假设“Controller1”需要“UserClass”而“UserClass”需要“Database”,控制器只会获得“UserClass”,但容器会确保您获得了“UserClass”的实例,该实例已经注入了“Database” .控制器不应该知道或关心“UserClass”依赖项。
  • 对于大多数 DiC(除了 Illuminate),您将在开始时将所有类及其依赖项注册到容器中。然后,当您通过容器获取实例时,容器将确保您获得所有正确的实例,它们各自的依赖项已经注入(您已经注册到容器中)。看看上面提到的容器,他们有一些例子可能会更清楚。

标签: php design-patterns model-view-controller dependency-injection fastroute


【解决方案1】:

首先你要明白,“依赖注入”和“依赖注入容器”是两个不同的东西:

  • DI 是一种技术,用于在 OOP 中分隔两个类。
  • DIC 是一款用于组装对象图的软件

看来,您已经是,您传递数据库连接的方式很糟糕。确切的技术原因可以描述为violation of LoD。基本上,您的 $db 变量跨越了两层边界。

但是,解决方案是不是:“只需添加 DI 容器”。那不会解决问题的。相反,您应该改进模型层的设计。你可以快速阅读here,但我会在这里给出一个更短的版本:

  • 将持久性逻辑与域逻辑分开
  • 控制器应该依赖于服务,这将是控制器(存在于 UI 层中)与业务逻辑交互的方式。
  • 分离业务逻辑的最简单方法分为三个部分:数据映射器(处理持久性逻辑)、域对象(用于业务规则)和服务(域对象和持久性对象之间的交互)

数据库应该是数据映射器的依赖项,专门用于处理用户实例(更多 here)。该映射器应该是您的“帐户管理服务”的直接依赖项(想一个好名字),或者使用该服务内部的工厂构建(然后这个工厂将成为服务的依赖项。

P.S.至于“选择哪个DIC”,我个人选择Symfony's独立容器,但这纯粹是个人喜好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-14
    • 2021-11-07
    相关资源
    最近更新 更多