Introduction
When you are developing an application with pluggable module , you need to somehow inject your module-specific rules into an existing application.
Set-up your application
1. Create new application using yiic webapp.
2. Add .htaccess to your webroot.
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
3. Add ‘showScriptName’ => false to your URL manager configuration.
4. Generate the page module using Gii.
5. Add your new module to the modules list in your application configuration.
The Yii code generator is shown in the following screenshot:
Let’s do it
Create file ModuleUrlManager.php in your protected/components directory and paste the below lines:
<?php
class ModuleUrlManager
{
static function collectRules()
{
if(!empty(Yii::app()->modules))
{
foreach(Yii::app()->modules as $moduleName => $config)
{
$module = Yii::app()->getModule($moduleName);
if(!empty($module->urlRules))
{
Yii::app()->getUrlManager()->addRules ($module->urlRules);
}
}
}
return true;
}
}
2. Add the following line in to your application configuration:'onBeginRequest' => array('ModuleUrlManager', 'collectRules'),
3. Now You can add custom rules in your page module. Open PageModule.php and add:public $urlRules = array(
'test' => 'page/default/index',
);
4. To test if it works, open your browser and go to http://<yourdomain.com>/test. This page should look like shown in the following image:
This is the view content for action "index". The action belongs to the controller "DefaultController" in the "page" module.
5. You still can override URL rules from your main application configuration file. So, what you specify in module’s urlRules is used only when the main application rules are not matching
How it’s working
Have a look in to the ModuleUrlManager::collectRules method.
If there are modules defined in our application, then we are checking if urlRules public property exists. If it does, then there are some rules defined in the module and they are added using CUrlManager::addRules.
CUrlManager::addRules description says "In order to make the new rules effective, this method must be called before CWebApplication::processRequest".
Now, let’s check how our application works. In our index.php, we have the following line:Yii::createWebApplication($config)->run();
After being initialized with configuration, we are calling CWebApplication::run():public function run()
{
if($this->hasEventHandler('onBeginRequest'))
$this->onBeginRequest(new CEvent($this));
$this->processRequest();
if($this->hasEventHandler('onEndRequest'))
$this->onEndRequest(new CEvent($this));
}
As we can see, there is an onBeginRequest event raised just before calling processRequest. That is why we are attaching our class method to it.
Caching Module Rules
As instantiating all application modules on every request is not good for performance, it is good to cache module rules. Caching strategy can vary depending on your application. Let’s do a simple one:
<?php
class ModuleUrlManager
{
static function collectRules()
{
if(!empty(Yii::app()->modules))
{
$cache = Yii::app()->getCache();
foreach(Yii::app()->modules as $moduleName => $config)
{
$urlRules = false;
if($cache)
$urlRules = $cache->get('module.urls.'.$moduleName);
if($urlRules===false){
$urlRules = array();
$module = Yii::app()->getModule($moduleName);
if(isset($module->urlRules))
$urlRules = $module->urlRules;
if($cache)
$cache->set('module.urls.'.$moduleName, $urlRules);
}
if(!empty($urlRules))
Yii::app()->getUrlManager()->addRules($urlRules);
}
}
return true;
}
}
This implementation caches URL rules per module. So, adding new modules is not a problem but changing existing ones requires you to flush cache manually using Yii::app()->cache->flush().