创建模块

如果你创建了一个复杂的应用部分,并希望他有一些可自定义的自由度,并用于下一个项目中,很可能你需要创建一个模块。在这个小节中,我们将会看到如何创建一个应用日志查看模块。

准备

按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的yii2-app-basic应用。

如何做…

首先我们来做一些计划。

yii2-app-basic中使用缺省配置,所有的日志被存放在runtime/logs/app.log文件中。我们可以使用正则表达式将所有的消息从文件中提取出来,然后将他们展示在GridView小部件上。此外,我们允许用户为日志文件配置自定义路径。

执行如下步骤:

  1. 创建modules/log文件夹,并创建Module类文件:
  1. <?php
  2. namespace app\modules\log;
  3. class Module extends \yii\base\Module
  4. {
  5. public $file = '@runtime/logs/app.log';
  6. }
  1. 创建一个简单的模型,用于从日志文件中转换每行内容:
  1. <?php
  2. namespace app\modules\log\models;
  3. use yii\base\Object;
  4. class LogRow extends Object
  5. {
  6. public $time;
  7. public $ip;
  8. public $userId;
  9. public $sessionId;
  10. public $level;
  11. public $category;
  12. public $text;
  13. }
  1. 写一个日志文件读取类,它会解析文件每行内容,逆序排列,返回LogRow模型的实例向量:
  1. <?php
  2. namespace app\modules\log\services;
  3. use app\modules\log\models\LogRow;
  4. class LogReader
  5. {
  6. public function getRows($file)
  7. {
  8. $result = [];
  9. $handle = @fopen($file, "r");
  10. if ($handle) {
  11. while (($row = fgets($handle)) !== false) {
  12. $pattern =
  13. '#^' .
  14. '(?P<time>\d{4}\-\d{2}\-\d{2}\d{2}:\d{2}:\d{2}) ' .
  15. '\[(?P<ip>[^\]]+)\]' .
  16. '\[(?P<userId>[^\]]+)\]' .
  17. '\[(?P<sessionId>[^\]]+)\]' .
  18. '\[(?P<level>[^\]]+)\]' .
  19. '\[(?P<category>[^\]]+)\]' .
  20. ' (?P<text>.*?)' .
  21. '(\$\_(GET|POST|REQUEST|COOKIE|SERVER) = \[)?' .
  22. '$#i';
  23. if (preg_match($pattern, $row, $matches)) {
  24. if ($matches['text']) {
  25. $result[] = new LogRow([
  26. 'time' => $matches['time'],
  27. 'ip' => $matches['ip'],
  28. 'userId' => $matches['userId'],
  29. 'sessionId' =>
  30. $matches['sessionId'],
  31. 'level' => $matches['level'],
  32. 'category' => $matches['category'],
  33. 'text' => $matches['text'],
  34. ]);
  35. }
  36. }
  37. }
  38. fclose($handle);
  39. }
  40. return array_reverse($result);
  41. }
  42. }
  1. 添加一个帮助类,用于为日志等级展示美化的HTML-badges:
  1. <?php
  2. namespace app\modules\log\helpers;
  3. use yii\helpers\ArrayHelper;
  4. use yii\helpers\Html;
  5. class LogHelper
  6. {
  7. public static function levelLabel($level)
  8. {
  9. $classes = [
  10. 'error' => 'danger',
  11. 'warning' => 'warning',
  12. 'info' => 'primary',
  13. 'trace' => 'default',
  14. 'profile' => 'success',
  15. 'profile begin' => 'info',
  16. 'profile end' => 'info',
  17. ];
  18. $class = ArrayHelper::getValue($classes, $level,
  19. 'default');
  20. return Html::tag('span', Html::encode($level), ['class' => 'label-' . $class]);
  21. }
  22. }
  1. 创建一个模块控制器,它会从读取器中获取行的数组,并将他们传递给ArrayDataProvider
  1. <?php
  2. namespace app\modules\log\controllers;
  3. use app\modules\log\services\LogReader;
  4. use yii\data\ArrayDataProvider;
  5. use yii\web\Controller;
  6. class DefaultController extends Controller
  7. {
  8. public function actionIndex()
  9. {
  10. $reader = new LogReader();
  11. $dataProvider = new ArrayDataProvider([
  12. 'allModels' => $reader->getRows($this->getFile()),
  13. ]);
  14. return $this->render('index', [
  15. 'dataProvider' => $dataProvider,
  16. ]);
  17. }
  18. private function getFile()
  19. {
  20. return \Yii::getAlias($this->module->file);
  21. }
  22. }
  1. 现在,创建modules/log/default/index.php视图文件:
  1. <?php
  2. use app\modules\log\helpers\LogHelper;
  3. use app\modules\log\models\LogRow;
  4. use yii\grid\GridView;
  5. use yii\helpers\Html;
  6. /* @var $this yii\web\View */
  7. /* @var $dataProvider yii\data\ArrayDataProvider */
  8. $this->title = 'Application log';
  9. $this->params['breadcrumbs'][] = $this->title;
  10. ?>
  11. <div class="log-index">
  12. <h1><?= Html::encode($this->title) ?></h1>
  13. <?= GridView::widget([
  14. 'dataProvider' => $dataProvider,
  15. 'columns' => [
  16. [
  17. 'attribute' => 'time',
  18. 'format' => 'datetime',
  19. 'contentOptions' => [
  20. 'style' => 'white-space: nowrap',
  21. ],
  22. ],
  23. 'ip:text:IP',
  24. 'userId:text:User',
  25. [
  26. 'attribute' => 'level',
  27. 'value' => function (LogRow $row) {
  28. return LogHelper::levelLabel($row->level);
  29. },
  30. 'format' => 'raw',
  31. ],
  32. 'category',
  33. 'text',
  34. ],
  35. ]) ?>
  36. </div>
  1. 在文件config/web.php中附加模块到你的应用中:
  1. $config = [
  2. 'id' => 'basic',
  3. 'basePath' => dirname(__DIR__),
  4. 'bootstrap' => ['log'],
  5. 'modules' => [
  6. 'log' => 'app\modules\log\Module',
  7. ],
  8. 'components' => [
  9. ],
  10. //...
  11. ];
  1. views/layouts/main.php文件中添加一个到这个控制器的链接:
  1. echo Nav::widget([
  2. 'options' => ['class' => 'navbar-nav navbar-right'],
  3. 'items' => [
  4. ['label' => 'Home', 'url' => ['/site/index']],
  5. ['label' => 'Log', 'url' => ['/log/default/index']],
  6. ['label' => 'About', 'url' => ['/site/about']],
  7. ['label' => 'Contact', 'url' => ['/site/contact']],
  8. //...
  9. ],
  10. ]);
  11. NavBar::end();
  1. 访问/index.php?r=log,确保这个模块可以正常工作:

创建模块 - 图1

工作原理…

你可以通过独立的模块来组织你的控制器、模型、视图和其它组件,并将他们附加到你的应用中。你可以使用Gii或者手动生成一个模块模板。

没一个模块包含一个主模块类,我们可以定义可配置的属性,定义修改路径,附加控制器等等。默认情况下,使用Gii生成的模块会运行默认控制器的index动作。

参考