mycjk31 2017-07-31 21:19:51 8313次浏览 1条评论 9 3 0

▪ 环境

基于《Yii2 之 frontend 子模块实践之一:添加前后台子模块》。

基于《Yii2 之 frontend 子模块实践之二:构建子模块的独立配置》。

基于《Yii2 之 frontend 子模块实践之三:布局和语言配置》。

▪ 前言

到目前为止,我们都是通过以下的 URL 访问 子模块

# 前台 index 子模块
http://frontend.domain.com/index.php?r=index/site/index
http://frontend.domain.com/index.php?r=index/about/index
http://frontend.domain.com/index.php?r=index/product/view&id=1
...

# 后台 admin 子模块
http://frontend.domain.com/index.php?r=admin/site/index
http://frontend.domain.com/index.php?r=admin/admin/login
...

很明显,这些 URL 格式对于一些稍微有 SEO 要求的项目就不行了。在下面的内容中我们将介绍利用 Yii2 内置的重写功能来不断美化 URL。

▪ 子模块和 URL 美化联动问题

对于一个项目,也许你可能只想美化 前台子模块 部分的 URL 格式,而对于 后台子模块 没有美化的要求,用原始的 URL 即可。

但是很不幸,Yii2 的 URL 美化是 应用主体 级别的。也就是说一旦你启用了 URL 美化,那么 应用主体 下的全部 子模块 都将启用 URL 美化。没有办法单独为一个 子模块 启用 URL 美化。

这里不探索具体为什么会这样,不过大概原因就是:URL 美化是在应用主体构建前期的 bootstrap 阶段就会完成,而子模块都是应用主体后期才构建的,所以无法为子模块单独配置 URL 美化。

▪ URL 美化之简易版

要想启用 URL 美化,只需要编辑 /frontend/config/main.php,添加如下代码即可:

// URL
$configs['components']['urlManager']                    = array();
$configs['components']['urlManager']['rules']           = array();
$configs['components']['urlManager']['suffix']          = '.html';
$configs['components']['urlManager']['showScriptName']  = false;
$configs['components']['urlManager']['enablePrettyUrl'] = true;

美化之后,项目的一些 URL 变化如下:

# 前台 index 子模块
http://frontend.domain.com/index/site/index.html
http://frontend.domain.com/index/index/index.html
http://frontend.domain.com/index/product/view/id/1.html
...

# 后台 admin 子模块
http://frontend.domain.com/admin/site/index.html
http://frontend.domain.com/admin/admin/login.html
...

▪ URL 美化之高级版

URL 高级美化,其实就是利用 urlManagerrules 对 URL 再做进一步的美化;rules 可以为单独的一条 URL 做定制美化,也可以为有相同规则的 URL 做批量定制美化。

1. 去除控制器中 Action 的 index

编辑 /frontend/config/main.php,在 $configs['components']['urlManager']['rules'] 中添加如下代码:

# 在通过 URL 访问时,Yii2 能将 URL 按照左边的规则匹配出右边的数据并进行路由
# 而在利用 urlManager 构建输出 URL 时,Yii2 能根据右边的规则匹配出左边的数据
'<module>/<controller>' => '<module>/<controller>/index',

对于访问控制器的 URL,如果没有指明控制器的 Action,那么 Yii2 默认就是使用 actionIndex。此处我们将这条规则强制写到规则 rules 里面,看上去也许可能没什么作用,但是当你使用 Url::toRoute('index/site/index') 的生成 URL 时候其作用就体现了出来了:

# 没有使用规则那么会生成以下 URL
/index/site/index.html

# 使用了规则的则会生成以下 URL
/index/site.html

最后我们来看下使用上面的规则以后,项目的一些 URL 变化如下:

# 前台 index 子模块
http://frontend.domain.com/index/site.html  // Change
http://frontend.domain.com/index/index.html  // Change
http://frontend.domain.com/index/product/view/id/1.html
...

# 后台 admin 子模块
http://frontend.domain.com/admin/site.html  // Change
http://frontend.domain.com/admin/admin/login.html
...

▪ URL 美化之子模块伪独立配置

之前我们说过 子模块 的是无法独立配置 URL 美化的,的确没错。但是接下来我们将利用 rules 规则的优先级来模拟一个 子模块伪独立配置,虽然不能完全独立,但是也基本满足日常需求。

在上面的案例中,我们发现 子模块 的名称都是存在于 URL 中的:

http://frontend.domain.com/index/...  // 前台 index 子模块
http://frontend.domain.com/admin/...  // 后台 admin 子模块

我们的目标很简单,就是 前台子模块 的名称 index 不要存在于 URL 中,例如:

// 直接访问 
// 前台 index 子模块
http://frontend.domain.com/site.html
http://frontend.domain.com/about.html

// 访问 后台 admin 子模块
http://frontend.domain.com/admin/site.html
http://frontend.domain.com/admin/about.html

这在我们日常的网站中非常常见,没有见过哪个网站的前台是 index 开头,所以如果不能实现上面的 URL 规则,那么 《Yii2 之 frontend 子模块》 这个系列教程也就基本没有意义了。

注意:该方案仅仅 前台子模块 的 可以省略模块名称( index),其他的 子模块 名称还是必须要存在于 URL 中。

基本思路:

urlManagerrules 规则匹配是从上到下,一旦匹配到那么即刻终止。利用该规律,我们可以先把所有的 其他子模块 通用规则写全:这样访问 其他子模块 的 URL 将优先被rules匹配,直接路由进入对应的子模块。

然后我们将 前台 index 子模块 的规则写入到 rules 的最后,同时省略模块名称 index,这样就实现上面的需求。

代码实现:

编辑 /frontend/config/main.php,在 $configs['components']['urlManager']['rules'] 中添加如下代码:

'admin' => 'admin/index/index',
'admin/<controller>' => 'admin/<controller>/index',
'admin/<controller>/<action>' => 'admin/<controller>/<action>',

'' => 'index/index/index',
'index' => 'index/index',
'<controller>' => 'index/<controller>/index',
'<controller>/<action>' => 'index/<controller>/<action>',

▪ URL 子模块伪独立配置高级写法

到此为止,我们已经完成了 子模块伪独立配置,系统也能非常成功的运行了。

也许你有洁癖,你发现将全部子模块的 rules 写在 应用主体 的配置文件中很是不爽。

在之前的系列文章 《Yii2 之 frontend 子模块实践之二:构建子模块的独立配置》 中我们已经为每个子模块分配了独立的配置文件,那么我们能不能将每个子模块的 rules 写到各自的独立配置文件中呢?这些无论是结构上还是逻辑上都将更加清晰。

不要尝试把 URL 美化的启动配置写到每个子模块的独立文件中,这个其实是无效的,因为 Yii2 在还没构建子模块实例时就已经加载了URL路由的配置并实例化路由对象

A. 分离 rules

  1. 编辑 '/frontend/modules/index/Config.php',添加 rules 代码:

    // URL
    $configs['components']['urlManager']           = array();
    $configs['components']['urlManager']['rules']  = [
     '' => 'index/index/index',
     'index' => 'index/index',
     '<controller>' => 'index/<controller>/index',
     '<controller>/<action>' => 'index/<controller>/<action>',
    ]
    
  2. 编辑 '/frontend/modules/admin/Config.php',添加 rules 代码:

    // URL
    $configs['components']['urlManager']           = array();
    $configs['components']['urlManager']['rules']  = [
     'admin' => 'kernel/index/index',
     'admin/<controller>' => 'kernel/<controller>/index',
     'admin/<controller>/<action>' => 'kernel/<controller>/<action>',
    ]
    
  3. 删除 /frontend/config/main.php 中的 $configs['components']['urlManager']['rules'] ...

B. 在应用主体的 bootstrap 阶段整合 rules 配置

由于 Yii2 的在构建 子模块 之前就已经构建好了路由对象,所以想要 子模块配置文件 中的 rules 规则应用于 Yii2 的路由对象,那么必须在 bootstrap 阶段就要整合 rules

  1. 新建引导文件 /frontend/modules/Bootstrap.php,引导文件内容如下:
    <?php
    namespace frontend\modules;
    

use yii\base\BootstrapInterface;

/**

  • 前端引导类
    /
    class Bootstrap implements BootstrapInterface
    {
    /
    *

    • @param \yii\base\Application $application
      */
      public function bootstrap( $application )
      {
      // 初始化
      $urlManagerRules = array();

      // 获取模块集
      $modules = $application->getModules();

      // 配置 UrlManager 的规则集
      // 没有使用正则匹配的规则优先级高
      // 通用性高的正则匹配的规则优先级越低
      foreach( $modules as $key => $module ){

       // 过滤系统内置模块
       if( is_array($module) == false ) continue;
       if( strpos($module['class'],'frontend\modules') !== 0 ) continue;
      
       // 获取自定义模块的配置
       $config = __DIR__ .'/'. $key . '/Config.php';
       if( is_file($config) == false ) continue; $config = require($config);
      
       // 配置 UrlManager 组件的规则
       $rules = $config['components']['urlManager']['rules']; if( empty($rules) ) continue;
       $urlManagerRules = array_merge($urlManagerRules, $rules);
      

      }

      foreach( $urlManagerRules AS $k=>$rule ) if( strpos($k,'<') === false ) $application->getUrlManager()->addRules(array($k=>$rule));
      foreach( $urlManagerRules AS $k=>$rule ) if( strpos($k,'<') !== false && strpos($k,'<') > 0 ) $application->getUrlManager()->addRules(array($k=>$rule));
      foreach( $urlManagerRules AS $k=>$rule ) if( strpos($k,'<') !== false && strpos($k,'<') == 0 ) $application->getUrlManager()->addRules(array($k=>$rule));
      }
      }

      > 在上面整合 `ruels` 的代码中,我并没有将 admin 和 index 两个子模块的名称写死。而是换了另外一种思路:越通用的 `rules` 项排在最后。这样既能实现之前的URL美化规则,同时也能大大提高程序的通用性。就算以后新加子模块此处的代码也无需修改。
      

注:上面的代码中 越通用的 rules 项排在最后 的算法并没有真正实现,但是基本够用,以后有时间继续完善

  1. 将新的引导文件加入到 应用主体 的配置,编辑 /frontend/config/main.php,添加如下代码:
    $configs['bootstrap'] = array('frontend\modules\Bootstrap', 'log');
    

至此 URL 子模块伪独立配置高级写法 完成。

觉得很赞
您需要登录后才可以评论。登录 | 立即注册