PHP MVC DI具有路由器,控制器和模型的Demeter法的实例

时间:2015-02-10 23:27:13

标签: php model-view-controller dependency-injection law-of-demeter

在PHP中使用MVC挣扎。 看完之后我的担忧变得更大了: https://www.youtube.com/watch?v=RlfLCWKxHJ0

根据LoD,我的Router类应该只知道Request Uri加载适当的Controller类。但是我的Conroller应该知道它应该使用哪个Model类,以及View类来呈现数据。或者更好的是,Controller应该知道将使用选定的数据存储来处理对象创建的ModelFactory。

这一切都打破了我的责任。

所以我的问题是:

  1. 路由器如何初始化Controller类不知道哪个 它需要的参数?即使它是DI容器,我们也不知道 要传递给Router内部构造函数的对象参数。如果我们将DI容器传递给Router构造函数(或任何其他类),我们将返回Service Locator。应该怎么做?
  2. 也许这都错了,但我的出发点是:

    // ... 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类中无能为力,因为我需要调用 new Model new View ,并且必须在构造函数或方法中明确地执行它,这很糟糕。

    更多问题:

    1. 我应该如何在Controller中获取Model类的实例?我应该使用静态方法加载View吗?

1 个答案:

答案 0 :(得分:1)

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

看看这些答案。第二部分有一些关于如何调用这些类的例子......

How should a model be structured in MVC?

Properly calling the database from Model in an MVC application?

其次,路由器只是将请求的URL路由到特定的控制器并查看。有不同的方法,但我的路由器检查匹配现有的资源图。基于其他一些事情,它将返回一个成功的资源"页面名称,未找到页面的资源名称或重定向资源等。

来自引导程序的一些基本代码用于说明......

//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)不要启动控制器并查看路由器类内部。在引导程序中执行此操作。

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

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

<强> 附加

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

$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 );

我没有连接到引导程序中的数据库并在应用程序周围传递连接,而是使用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];
}