Phalcon学习笔记(7)多模块

很多网站程序其实都需要有很多模块,比如客户访问的前台和管理员的后台就是大不相同的两个模块。诚然,用一个模块的Controller/Action也能实现类似功能,但是毕竟还是多模块用起来好。从Phalcon的启动顺序来看

https://docs.phalconphp.com/en/latest/reference/applications.html#manual-bootstrapping

其模块功能较弱。


实际上笔者亲身使用下来,感觉多模块做得不太好,不过既然有需要,还是要介绍一下的。


首先,为了开发的需要,文件和目录的结构有必要调整,这是官方文档中的一个案例

multiple/
  apps/
    frontend/
       controllers/
       models/
       views/
       Module.php
    backend/
       controllers/
       models/
       views/
       Module.php
  public/
    css/
    img/
    js/
首先入口文件仍然只有一个index.php,然后不同的模块放置在不同的目录,每个模块目录下有其对应的控制器、模型、视图目录,不过最重要的是每个模块目录下有一个Module.php文件。其实因为加载器的存在,除了Module.php必须以外,其他的目录其实都是可以随意放置的,只要在加载器中注册就好。


首先在入口的index.php文件中必须要有2个特殊的部分,一个是特殊的router路由器,这样才能识别模块。其实非常简单,就是把URL中对应模块的部分让路由器识别出来就ok了

$di->set('router', function () {
    $router = new \Phalcon\Mvc\Router();
    $router->notFound(array(
        'module' => 'entrance',
        'controller' => 'index',
        'action' => 'index',
    ));
    $router->add('/:module', array(
        'module' => 1,
        'controller' => 'index',
        'action' => 'index',
    ));
    $router->add('/:module/:controller', array(
        'module' => 1,
        'controller' => 2,
        'action' => 'index',
    ));
    $router->add('/:module/:controller/:action', array(
        'module' => 1,
        'controller' => 2,
        'action' => 3,
    ));
    $router->add('/:module/:controller/:action/:params', array(
        'module' => 1,
        'controller' => 2,
        'action' => 3,
        'params' => 4,
    ));
    $router->setDefaultModule('entrance');
    $router->setDefaultController('index');
    $router->setDefaultAction('index');
    return $router;
});
再一个就是要在Application启动类中注册模块

$application->registerModules(array(
        'entrance' => array(
            'className' => 'Multiple\Entrance\Module',
            'path' => ROOT_PATH . '/apps/entrance/Module.php',
        ),
        'backend' => array(
            'className' => 'Multiple\Backend\Module',
            'path' => ROOT_PATH . '/apps/backend/Module.php',
        ),
    ));


另外笔者推荐在设置DI和Application类之前设置一下加载器,主要是要注册不同的模块所对应的命名空间以及各个不同的Module所共同需要的类库。

$loader = new \Phalcon\Loader();
$loader->registerDirs(
    array(
        ROOT_PATH . '/apps/library/',
    )
)->registerNamespaces(
    array(
        'Multiple\Backend' => ROOT_PATH . '/apps/backend/',
        'Multiple\Backend\Controllers' => ROOT_PATH . '/apps/backend/controllers/',
        'Multiple\Backend\Controllers\Model' => ROOT_PATH . '/apps/backend/models/',
        'Multiple\Entrance' => ROOT_PATH . '/apps/entrance/',
        'Multiple\Entrance\Controllers' => ROOT_PATH . '/apps/entrance/controllers/',
        'Multiple\Entrance\Models' => ROOT_PATH . '/apps/entrance/models/',
    )
)->register();

这样Application类就可以找到各个不同的Module所对应的Module.php文件,在官方文档中这样说:当 Phalcon\Mvc\Application Module注册后,每个匹配的route都必须返回一个有效的module。注册的module都有一个相关的类,用于设置module本身提供的功能。每个module类都必须实现 registerAutoloaders() 和 registerServices() 这两个方法,Phalcon\Mvc\Application 将调用它们执行要执行的module


也就是说,我们必须要在Module.php文件中要声明这个类,然后实现registerAutoloaders() 和 registerServices() 这两个方法。类的声明很简单

class Module extends \Phalcon\DI\Injectable implements \Phalcon\Mvc\ModuleDefinitionInterface {}

如果特殊需求也不需要指定
extends \Phalcon\DI\Injectable
然后是registerAutoloaders()方法

public function registerAutoloaders(\Phalcon\DiInterface $dependencyInjector = NULL) {

        $loader = new \Phalcon\Loader();
        $loader->registerNamespaces(array(
            'Multiple\Entrance\Controllers' => __DIR__ . '/controllers/',
            'Multiple\Entrance\Models' => __DIR__ . '/models/',
        ))->registerDirs(array(
            __DIR__ . '/controllers/',
            __DIR__ . '/models/',
        ))->register();
    }
要注意,这个样例代码中给这个函数一个输入变量,默认值是NULL,也就是说它不是必需的。在Phalcon-1.3.x中是不需要这个变量的,在Phalcon-2.0.x中会需要,否则会出错,这似乎是一个bug,不过官方到2.0.2版本尚未解决。在这个函数中,主要就是设定一个该模块自己的加载器,在Application启动类加载完成各模块公用的文件之后,针对这个模块加载这个模块专用的文件。


然后是registerServices()方法

原则上讲,Application启动类中的依赖注入器DI不应该加载任何专属于模块的功能,而今应该加载公共的功能,比如config、session之类,而且还应该使用setShared()函数来注册。各模块专属的部分应该在各模块的Module.php中通过其专属DI来设定,而这个设定就是在registerServices()方法中完成的,所以说这个函数中的输入参数是必不可少的。

在这段样例代码中,对于数据库的部分就读取了公共DI中关于数据库的部分。

public function registerServices(\Phalcon\DiInterface $di) {
        //Registering a dispatcher
        $di->set('dispatcher', function () {
            $dispatcher = new \Phalcon\Mvc\Dispatcher();

            //Attach a event listener to the dispatcher
            $eventManager = new \Phalcon\Events\Manager();
            $eventManager->attach('dispatch:beforeDispatch', new \SecurityPlugin(__CLASS__));

            $dispatcher->setEventsManager($eventManager);
            $dispatcher->setDefaultNamespace('Multiple\Entrance\Controllers\\');
            return $dispatcher;
        });

        //Registering the view component
        $di->set('view', function () {
            $view = new \Phalcon\Mvc\View();
            $view->setBasePath(ROOT_PATH . '/apps/entrance/');
            $view->setViewsDir('./views/');
            $view->registerEngines(array(
                '.volt' => 'Phalcon\Mvc\View\Engine\Volt',
                ".phtml" => 'Phalcon\Mvc\View\Engine\Php',
            ));
            return $view;
        });
        //You can set a different connection in each module
        $config = $this->di->get('config');
        $di->set('db', function () use ($config) {
            $dbadapter = '\Phalcon\Db\Adapter\Pdo\\' . $config->database->adapter;
            return new $dbadapter(array(
                "host" => $config->database->host,
                "username" => $config->database->username,
                "password" => $config->database->password,
                "dbname" => $config->database->dbname,
                "options" => $config->database->options,
            ));
        });
    }

然后开发者就可以在各个不同的模块所注册的MVC目录里添加自己的代码了。在浏览器中访问

http://yourwebsite/module/controller/action/params

就可以访问各个模块的功能了。


要注意的是分发器不能跨模块跳转,如果想跨模块跳转,只能使用

$this->response->redirect('entrance/index/login');

而这会带来多一次的http访问,给服务器带来额外负担,具体可以由开发者权衡。


另外笔者实践检验,router有时会出错,如果开发者把url给全,而不是让router自动分配默认的Module/Controller/Action,可能会更稳妥一些。



你可能感兴趣的:(Phalcon,Phalcon)