很多网站程序其实都需要有很多模块,比如客户访问的前台和管理员的后台就是大不相同的两个模块。诚然,用一个模块的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,可能会更稳妥一些。