PHPDemeter定律与Router、Controller和Model的MVC DI实例

PHP MVC DI practical exaple of Law of Demeter with Router, Controller and Model

在 PHP 中与 MVC 作斗争。 看完这个后,我的担忧变得更大: https://www.youtube.com/watch?v=RlfLCWKxHJ0

根据 LoD,我的路由器 class 应该只知道请求 Uri 以加载正确的控制器 class。但是我的控制器应该知道它应该使用哪个模型 class 和一个视图 class 来呈现数据。或者更好的是,Controller 应该知道 ModelFactory 将使用选定的数据存储处理对象创建。

这一切都打破了我的 LoD。

所以我的问题是:

  1. 路由器应该如何初始化控制器class不知道哪个 它需要的参数?就算是DI容器我们也不知道 要传递给路由器内部构造函数的对象参数。如果我们将 DI 容器传递给路由器构造函数(或任何其他 class),我们将返回到服务定位器。应该怎么做?

也许这都是错误的,但我的出发点是:

// ... retrieve settings, available languages, start session,...

$router     = new Router($settings);
$router->loadController();

Router.php

class Router 
{
    public function __construct(Settings $settings)
    {
        $this->settings = $settings;
    }

    // some other methods

    public function loadController()
    {   
        try
        {
            // Loading controller
            $controller = $this->getController();

            if (is_callable(array($controller, $this->method)) == false)
                $this->method = 'init';

            // Running controller
            $controller->{$this->method}();
        }
        catch (Exception $e)
        {
        $e->displayMessage();
        }
    }
}

从现在开始,我无法在我的 Controller class 中执行任何操作,因为我需要调用 new Modelnew View 并且必须在构造函数或方法中明确地执行它,这是不好的。

更多问题:

  1. 我应该如何在 Controller 中获取模型 class 的实例?我应该使用静态方法加载视图吗?

首先,模型是一层,而不是具体的class。控制器本身将启动 class 开始处理您的应用程序的业务逻辑所必需的。

看看这些答案。第二个有一些例子说明如何称呼这些 classes...

How should a model be structured in MVC?

Properly calling the database from Model in an MVC application?

其次,路由器只是将请求的 url 路由到特定的控制器和视图。有不同的方法可以做到这一点,但我的路由器会根据现有资源映射进行匹配检查。基于其他一些事情,它将 return 成功的 "resource" 页面名称、未找到页面的资源名称或重定向资源等。

来自 bootstrap 的一些基本代码用于说明...

//snip

$routeLoader = new \Routing\RouteLoader();
$match = $routeLoader->getMatchedRoute( $request['pageName'] );

$router = new \Routing\Router( $request );
$resource = $router->getResource( $match );

//snip

$viewName       = '\View\' . $resource . 'View';
$controllerName = '\Controller\' . $resource . 'Controller';

$view = new $viewName();
$controller = new $controllerName( $view, $request );
$controller->{$router->getCommand()}();

$view->response();

所以,直接回答你的问题..

1) 不启动控制器,查看路由器内部class。在 bootstrap 中进行。

此外,对于 Web 应用程序,您可能需要也可能不需要 DI、服务工厂,甚至发现有必要使用特定的设计模式。对于大多数人来说,我认为它矫枉过正并增加了不必要的复杂性。

2) 控制器将启动 classes 以开始处理业务逻辑(如果有必要的话)。

附加

要将数据库连接添加到上面并将其注入控制器,下面是一种方法..

$DCM = new \Database\DatabaseConnectionManager( new \Config\DatabaseConfig() );
$AppCache = new \Cache\AppCache();
$DAM = new \DataAccess\DataAccessManager( $AppCache, $DCM );

在上面的代码中,DataAccessManager 对象将负责首先从缓存中检索数据,然后再从数据库中检索数据。现在可以将这个 $DAM 对象注入到控制器中,就像这样...

$controller = new $controllerName( $DAM, $view, $request );

与其在 bootstrap 中连接到数据库并在应用程序周围传递连接,我更喜欢使用 DataAccessManager,它只会在实际需要时建立连接。一旦需要,就会启动一个 PDO 对象(或其他对象)并将其存储在对象中,以便在必要时再次检索和使用。如有必要,我还可以连接到其他数据库..

// method from DataAccessManager class

private function connectToDatabase( $server = 'slave' )
{
    if (!array_key_exists( $server, $this->dbObject )) {
        // use the DataConnectionManager to connect and store the connection here
        $this->dbObject[$server] = $this->DCM->connect( $server );
    }

    return $this->dbObject[$server];
}