第二章 路由,控制器和视图

在本章中,我们将会讨论如下话题:

  • 配置URL规则
  • 生成URL
  • 在URL规则中使用正则表达式
  • 使用一个基础控制器
  • 使用独立动作
  • 创建一个自定义过滤器
  • 展示静态页面
  • 使用flash消息
  • 在视图中使用控制器上下文
  • 部分复用视图
  • 使用blocks
  • 使用装饰器
  • 定义多个布局
  • 翻页和数据排序

介绍

本章将会帮助你学习一些方便的东西:Yii URL路由,控制器和视图。你将能够使你的控制器和视图更加灵活。

配置URL规则

在本小节中,我们将会学习如何配置URL规则。在开始以前,我们创建一个应用。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 使用如下代码创建@app/controllers/TestController.php控制器:
  1. <?php
  2. namespace app\controllers;
  3. use yii\helpers\Html;
  4. use yii\web\Controller;
  5. class TestController extends Controller
  6. {
  7. public function actionIndex()
  8. {
  9. return $this->renderContent(Html::tag('h2',
  10. 'Index action'
  11. ));
  12. }
  13. public function actionPage($alias)
  14. {
  15. return $this->renderContent(Html::tag('h2',
  16. 'Page is '. Html::encode($alias)
  17. ));
  18. }
  19. }

这就是我们即将做自定义URL的控制器。

  1. 为了使用干净的URL,需要配置你的应用服务器。如果你在使用带有mod_rewrite的Apache,并打开了AllowOverride,那么你就可以添加如下内容到@web文件夹下的.htaccess文件中:
  1. Options +FollowSymLinks
  2. IndexIgnore */*
  3. RewriteEngine on
  4. # if a directory or a file exists, use it directly
  5. RewriteCond %{REQUEST_FILENAME} !-f
  6. RewriteCond %{REQUEST_FILENAME} !-d
  7. # otherwise forward it to index.php
  8. RewriteRule . index.php

如何做…

我们的网站将会在/home展示index页面,并且其它页面在/page/<alias_ here>。此外,/about应该定向到带有alias about的页面:

  1. @app/config/web.php文件中添加urlManager组件:
  1. 'components' => [
  2. // ..
  3. 'urlManager' => [
  4. 'enablePrettyUrl' => true,
  5. 'rules' => [
  6. 'home' => 'test/index',
  7. '<alias:about>' => 'test/page',
  8. 'page/<alias>' => 'test/page',
  9. ]
  10. ],
  11. // ..
  12. ],

保存过你的改动以后,你应该能浏览如下URL:

  1. /home
  2. /about
  3. /page/about
  4. /page/test
  1. 尝试运行/home URL,你将会得到如下截图:

第二章 路由,控制器和视图 - 图1

  1. 尝试运行/about页面:

第二章 路由,控制器和视图 - 图2

工作原理…

下面我们来回顾都做了些什么,以及是如何工作的。我们的第一条规则是:

  1. 'home' => 'test/index',

test/index是什么?在Yii应用中,每一个控制器和它的动作都有相应的内部路由。对于一个内部路由,它的格式是moduleID/controllerID/actionID。例如,TestControlleractionPage方法对应的路由是test/page。所以,为了获取控制器ID,你应该用它的不带Controller后缀的名称,并将它的首字母小写。为了获取一个动作ID,你应该用它的不带action前缀的方法名,同样将其首字母小写。

现在,什么是home?为了以更好的方式理解它,我们需要知道,至少是表面上,当我们使用不同的URL访问我们的应用时,都发生了些什么。

当我们使用/home时,URL路由从上到下一个一个的检查我们的规则,尝试找到和输入URL匹配的规则。如果找到了,那么路由将会从一个分配给它的内部路由中获取到控制器和它的动作,并执行它。所以,/home是URL模式,它定义了URL将会被处理的规则。

更多…

你也可以使用一个特殊的语法创建参数化的规则。回顾一下第三条规则:

  1. 'page/<alias>' => test/page',

这里我们定义了一个alias参数,它应该在URL中/page/后边被指定。它可以是任何东西,并会被传递给$alias参数:

  1. TestController::actionPage($alias)

你可以为这样一个参数定义一个模式。我们为第二条规则做同样的事情:

  1. '<alias:about>' => test/page'

这里的alias应该匹配about,否则这条规则不能被应用。

参考

欲了解更多信息,参考如下地址:

生成URLs

Yii不仅允许你将URL路由到不同的控制器动作上,而且可以通过执行一个正确的内部路由和它的参数来生成一个URL。这非常有用,因为在开发应用过程中,你可以将精力集中在内部路由上,只需要在上线前关注一下真实的URL。永远不要直接指定URL,并确保你使用了Yii URL工具集。它会允许你修改URL,而且不需要修改很多应用代码。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 找到@app/config/web.php文件,并替换规则数组:
  1. 'urlManager' => array(
  2. 'enablePrettyUrl' => true,
  3. 'showScriptName' => false,
  4. ),
  1. 配置你的应用服务器来使用干净的URL。如果你在使用带有mod_rewrite的Apache,并打开了AllowOverride,那么你就可以添加如下内容到@app/web文件夹下的.htaccess文件中:
  1. Options +FollowSymLinks
  2. IndexIgnore */*
  3. RewriteEngine on
  4. # if a directory or a file exists, use it directly
  5. RewriteCond %{REQUEST_FILENAME} !-f
  6. RewriteCond %{REQUEST_FILENAME} !-d
  7. # otherwise forward it to index.php
  8. RewriteRule . index.php

如何做…

  1. @app/controllers目录中,使用如下代码创建BlogController
  1. <?php
  2. namespace app\controllers;
  3. use yii\web\Controller;
  4. class BlogController extends Controller
  5. {
  6. public function actionIndex()
  7. {
  8. return $this->render('index');
  9. }
  10. public function actionRssFeed($param)
  11. {
  12. return $this->renderContent('This is RSS feed for our blog and ' . $param);
  13. }
  14. public function actionArticle($alias)
  15. {
  16. return $this->renderContent('This is an article with alias ' . $alias);
  17. }
  18. public function actionList()
  19. {
  20. return $this->renderContent('Blog\'s articles here');
  21. }
  22. public function actionHiTech()
  23. {
  24. return $this->renderContent('Just a test of action which contains more than one words in the name') ;
  25. }
  26. }

这是我们的博客控制器,我们将会给它生成自定义URL。

  1. @app/controllers文件夹中,使用如下代码创建TestController
  1. <?php
  2. namespace app\controllers;
  3. use Yii;
  4. use yii\web\Controller;
  5. class TestController extends Controller
  6. {
  7. public function actionUrls()
  8. {
  9. return $this->render('urls');
  10. }
  11. }
  1. @app/views文件夹中,创建test文件夹,以及urls.php视图文件,文件内容如下:
  1. <?php
  2. use yii\helpers\Url;
  3. use yii\helpers\Html;
  4. ?>
  5. <h1>Generating URLs</h1>
  6. <h3>Generating a link with URL to <i>blog</i> controller and
  7. <i>article</i> action with alias as param</h3>
  8. <?= Html::a('Link Name', ['blog/article', 'alias' => 'someAlias']); ?>
  9. <h3>Current url</h3>
  10. <?=Url::to('')?>
  11. <h3>Current Controller, but you can specify an action</h3>
  12. <?=Url::toRoute(['view', 'id' => 'contact']);?>
  13. <h3>Current module, but you can specify controller and
  14. action</h3>
  15. <?= Url::toRoute('blog/article')?>
  16. <h3>An absolute route to blog/list </h3>
  17. <?= Url::toRoute('/blog/list')?>
  18. <h3> URL for <i>blog</i> controller and action <i>HiTech</i>
  19. </h3>
  20. <?= Url::toRoute('blog/hi-tech')?>
  21. <h3>Canonical URL for current page</h3>
  22. <?= Url::canonical()?>
  23. <h3>Getting a home URL</h3>
  24. <?= Url::home()?>
  25. <h3>Saving a URL of the current page and getting it for
  26. re-use</h3>
  27. <?php Url::remember()?>
  28. <?=Url::previous()?>
  29. <h3>Creating URL to <i>blog</i> controller and <i>rss-feed</i>
  30. action while URL helper isn't available</h3>
  31. <?=Yii::$app->urlManager->createUrl(['blog/rss-feed', 'param' => 'someParam'])?>
  32. <h3>Creating an absolute URL to <i>blog</i> controller and
  33. <i>rss-feed</i></h3>
  34. <p>It's very useful for emails and console applications</p>
  35. <?=Yii::$app->urlManager->createAbsoluteUrl(['blog/rss-feed', 'param' => 'someParam'])?>
  1. 打开http://yii-book.app/test/urls你将会看到如下输出(参考先前代码中全部方法的列表):

第二章 路由,控制器和视图 - 图3

工作原理…

我们需要生成URL,指向BlogController的控制器动作(RssFeed, Article, List, HiTech)。

  1. <?= Html::a('Link Name', ['blog/article', 'alias' => 'someAlias']); ?>

依赖于我们需要的地方,有不同的方式可以做到这些,但是基础是一样的。下面列出一些生成URL的方法。

什么是内部路由?每一个控制器和它的动作有相应的路由。路由的格式是moduleID/controllerID/actionID。例如,BlogControlleractionHiTech方法对应于blog/hi-tech路由。

为了获取控制器ID,你应该用它的不带Controller后缀的名称,并将它的首字母小写。为了获取一个动作ID,你应该用它的不带action前缀的方法名,同样将每个单词的首字母小写,并使用-符号分割(例如actionHiTech将会是hi-tech)。

$_GET变量做为参数会被传递给用内部路由指定的动作中。例如,如果我们想为BlogController::actionArticle创建一个URL,并将$_GET['name']传递给它,可以按如下方式进行:

  1. <?= Html::a('Link Name', ['blog/article', 'alias' => 'someAlias']); ?>

在你的应用内部,可以使用相对URL,绝对URL应该被用于指向你的网站(例如其它网站)外部,或者用于链接到能被外部访问的资源上(RSS feeds,email等等)。

你可以很容易的使用URL管理器。URL管理器是一个内置应用组件,名叫urlManager。你必须使用这个组件,它可以通过Yii::$app->urlManager从web和console被访问到。

当你不能获得一个控制器实例时,例如,当你实施一个控制台应用,你可以使用如下两个urlManager创建方法:

  1. <?=Yii::$app->urlManager->createUrl(['blog/rss-feed', 'param' => 'someParam'])?>
  2. <?=Yii::$app->urlManager->createAbsoluteUrl(['blog/rss-feed','param' => 'someParam'])?>

更多…

欲了解更多信息,参考如下URL:

参考

  • 配置URL规则小节

在URL规则中使用正则表达式

Yii URL路由器的一个隐藏特性是,你可以使用正则表达式来处理地址。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 在你的@app/controllers文件夹中,使用如下代码创建PostController.php
  1. <?php
  2. namespace app\controllers;
  3. use yii\helpers\Html;
  4. use yii\web\Controller;
  5. class PostController extends Controller
  6. {
  7. public function actionView($alias)
  8. {
  9. return $this->renderContent(Html::tag('h2', 'Showing post with alias ' . Html::encode($alias)
  10. ));
  11. }
  12. public function actionIndex($type = 'posts', $order = 'DESC')
  13. {
  14. return $this->renderContent(Html::tag('h2', 'Showing ' . Html::encode($type) . ' ordered ' . Html::encode($order)));
  15. }
  16. public function actionHello($name)
  17. {
  18. return $this->renderContent(Html::tag('h2', 'Hello, ' . Html::encode($name) . '!'));
  19. }
  20. }

这是我们的应用控制器,我们将会使用自定义URL来方法它。

  1. 配置你的应用服务器,来使用干净的URL。如果你在使用带有mod_rewrite的Apache,并打开了AllowOverride,那么你就可以添加如下内容到@web文件夹下的.htaccess文件中:
  1. Options +FollowSymLinks
  2. IndexIgnore */*
  3. RewriteEngine on
  4. # if a directory or a file exists, use it directly
  5. RewriteCond %{REQUEST_FILENAME} !-f
  6. RewriteCond %{REQUEST_FILENAME} !-d
  7. # otherwise forward it to index.php
  8. RewriteRule . index.php

如何做…

我们希望我们的PostController动作根据一些指定的规则接受参数,并将其它所有不匹配的参数给以404 not found的HTTP响应。此外,post/index应该有一个alias URL archive。

添加如下urlManager组件配置到@app/config/web.php

  1. 'components' => [
  2. // ..
  3. 'urlManager' => [
  4. 'enablePrettyUrl' => true,
  5. 'rules' => [
  6. 'post/<alias:[-a-z]+>' => 'post/view',
  7. '<type:(archive|posts)>' => 'post/index',
  8. '<type:(archive|posts)>/<order:(DESC|ASC)>' => 'post/index',
  9. 'sayhello/<name>' => 'post/hello',
  10. ]
  11. ],
  12. // ..
  13. ],

如下URL将会成功:

  • http://yii-book.app/post/test
  • http://yii-book.app/posts
  • http://yii-book.app/archive
  • http://yii-book.app/posts/ASC
  • http://yii-book.app/sayhello

如下URL将会失败:

  • http://yii-book.app/archive/test
  • http://yii-book.app/post/another_post

下面的截图展示了http://yii-book.app/post/test运行是成功的:

第二章 路由,控制器和视图 - 图4

下面的截图展示了http://yii-book.app/archive也可以运行成功:

第二章 路由,控制器和视图 - 图5

下面的截图展示http://yii-book.app/archive/test没有运行成功,并有一个报错:

第二章 路由,控制器和视图 - 图6

工作原理…

你可以在参数定义和规则的其它部分使用正则表达式。下面我们一条一条的看这些规则:

  1. 'post/<alias:[-a-z]+>' => 'post/view',

alias参数应该包含一个或多个英文单词或者一个-。其它符号不被允许。

  1. '(posts|archive)' => 'post/index',
  2. '(posts|archive)/<order:(DESC|ASC)>' => 'post/index',

postsarchive都会指向post/indexorder参数只接受两个值——DESCASC

  1. 'sayhello/<name>' => 'post/hello',

你应该指定名称部分,但是没有对可以使用的单词做限制。注意到不管使用的规则是什么,开发者不应该假设输入的数据是安全的。

第二章 路由,控制器和视图 - 图7

更多…

欲了解更多关于正则表达式的信息,你可以使用如下资源:

参考

  • 配置URL规则小节

使用一个基础控制器

在许多框架中,通常会在指导中提出基础控制器的概念,它可以被其它控制器扩展。在Yii中,它不在指导中,因为你可以用其它多种方式很灵活的达到。但是,使用一个基础控制器是可能的,并且是有用的。

假设我们想添加添加一些控制器,它们只能被登录的用于访问。我们当然可以为每一个控制器单独设置一些限制,但是我们将会用一种更好的方式来做到它。

准备

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

如何做…

  1. 首先,我们需要一个基础控制器,它只能被登录的用于使用。创建@app/components/BaseController.php,内容如下:
  1. <?php
  2. namespace app\components;
  3. use Yii;
  4. use yii\web\Controller;
  5. use yii\filters\AccessControl;
  6. class BaseController extends Controller
  7. {
  8. public function actions()
  9. {
  10. return [
  11. 'error' => ['class' => 'yii\web\ErrorAction'],
  12. ];
  13. }
  14. public function behaviors()
  15. {
  16. return [
  17. 'access' => [
  18. 'class' => AccessControl::className(),
  19. 'rules' => [
  20. [
  21. 'allow' => true,
  22. 'actions' => 'error'
  23. ],
  24. [
  25. 'allow' => true,
  26. 'roles' => ['@'],
  27. ],
  28. ],
  29. ]
  30. ];
  31. }
  32. }

这个控制器有一个动作map,此外还有一个错误动作。

  1. 现在,使用Gii创建TestController,但是将基础类设置为app/components/BaseController

第二章 路由,控制器和视图 - 图8

你将会得到类似如下的输出:

  1. <?php
  2. namespace app\controllers;
  3. class TestController extends \app\components\BaseController
  4. {
  5. public function actionIndex()
  6. {
  7. return $this->render('index');
  8. }
  9. }
  1. 现在,你的TestController只能被登录用户访问,尽管我们没有在TestController控制器中做明确的说明。你可以在登出的时候通过访问http://yii-book.app/index.php?r=test/index来检查它。

工作原理…

这个把戏只是一个类继承。如果过滤器或者访问控制规则不在TestController,那么他们将会从SecureController中调用。

更多…

如果你需要继承基础控制器的方法,记住它不能被覆盖。例如,我们需要添加一个页面动作到控制器的动作map中:

  1. <?php
  2. namespace app\controllers;
  3. use yii\helpers\ArrayHelper;
  4. use app\components\BaseController;
  5. class TestController extends BaseController
  6. {
  7. public function actions()
  8. {
  9. return ArrayHelper::merge(parent::actions(), [
  10. 'page' => [
  11. 'class' => 'yii\web\ViewAction',
  12. ],
  13. ]);
  14. }
  15. public function behaviors()
  16. {
  17. $behaviors = parent::behaviors();
  18. $rules = $behaviors['access']['rules'];
  19. $rules = ArrayHelper::merge(
  20. $rules,
  21. [
  22. [
  23. 'allow' => true,
  24. 'actions' => ['page']
  25. ]
  26. ]
  27. );
  28. $behaviors['access']['rules'] = $rules;
  29. return $behaviors;
  30. }
  31. public function actionIndex()
  32. {
  33. return $this->render('index');
  34. }
  35. }

欲了解更多信息,参考http://www.yiiframework.com/doc-2.0/yii-base-controller.html

使用独立动作

在Yii中,你可以定义控制器动作作为独立的类,然后连接它们到你的控制器上。这将会帮助你复用一些常用功能。

例如,你可以为自动完成字段移动后端到一个动作中并保存,而不需要再次一遍一遍的写。

另外一个例子是,我们可以创建所有的CRUD操作作为分开的独立动作。我们将会写、创建、查看和删除模型的动作,并查看模型的操作列表。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 创建post表。使用如下命令创建migration:
  1. ./yii migrate/create create_post_table
  1. 更新刚刚创建的migration的方法和导出的类列表:
  1. <?php
  2. use yii\db\Schema;
  3. use yii\db\Migration;
  4. class m150719_152435_create_post_table extends Migration
  5. {
  6. const TABLE_NAME = '{{%post}}';
  7. public function up()
  8. {
  9. $tableOptions = null;
  10. if ($this->db->driverName === 'mysql') {
  11. $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
  12. }
  13. $this->createTable(self::TABLE_NAME, [
  14. 'id' => Schema::TYPE_PK,
  15. 'title' => Schema::TYPE_STRING.'(255) NOT NULL',
  16. 'content' => Schema::TYPE_TEXT.' NOT NULL',
  17. ], $tableOptions);
  18. for ($i = 1; $i < 7; $i++) {
  19. $this->insert(self::TABLE_NAME, [
  20. 'title' => 'Test article #'.$i,
  21. 'content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '
  22. .'Sed sit amet mauris est. Sed at dignissim dui. '
  23. .'Phasellus arcu massa, facilisis a fringilla sit amet, '
  24. .'rhoncus ut enim.',
  25. ]);
  26. }
  27. }
  28. public function down()
  29. {
  30. $this->dropTable(self::TABLE_NAME);
  31. }
  32. }
  1. 使用如下命名安装所有的migration:
  1. ./yii migrate up
  1. 使用Gii创建Post

如何做…

  1. 创建独立动作@app/actions/CreateAction.php
  1. <?php
  2. namespace app\actions;
  3. use Yii;
  4. use yii\base\Action;
  5. class CreateAction extends Action
  6. {
  7. public $modelClass;
  8. public function run()
  9. {
  10. $model = new $this->modelClass();
  11. if ($model->load(Yii::$app->request->post()) && $model->save()) {
  12. $this->controller->redirect(['view', 'id' => $model->getPrimaryKey()]);
  13. } else {
  14. return $this->controller->render('//crud/create', [
  15. 'model' => $model
  16. ]);
  17. }
  18. }
  19. }
  1. 创建独立动作@app/actions/DeleteAction.php
  1. <?php
  2. namespace app\actions;
  3. use yii\base\Action;
  4. use yii\web\NotFoundHttpException;
  5. class DeleteAction extends Action
  6. {
  7. public $modelClass;
  8. public function run($id)
  9. {
  10. $class = $this->modelClass;
  11. if (($model = $class::findOne($id)) === null) {
  12. throw new NotFoundHttpException('The requested page does not exist.');
  13. }
  14. $model->delete();
  15. return $this->controller->redirect(['index']);
  16. }
  17. }
  1. 创建独立动作@app/actions/IndexAction.php
  1. <?php
  2. namespace app\actions;
  3. use yii\base\Action;
  4. use yii\data\Pagination;
  5. class IndexAction extends Action
  6. {
  7. public $modelClass;
  8. public $pageSize = 3;
  9. public function run()
  10. {
  11. $class = $this->modelClass;
  12. $query = $class::find();
  13. $countQuery = clone $query;
  14. $pages = new Pagination([
  15. 'totalCount' => $countQuery->count(),
  16. ]);
  17. $pages->setPageSize($this->pageSize);
  18. $models = $query->offset($pages->offset)
  19. ->limit($pages->limit)
  20. ->all();
  21. return $this->controller->render('//crud/index', [
  22. 'pages' => $pages,
  23. 'models' => $models
  24. ]);
  25. }
  26. }
  1. 创建独立动作@app/actions/ViewAction.php
  1. <?php
  2. namespace app\actions;
  3. use yii\base\Action;
  4. use yii\web\NotFoundHttpException;
  5. class ViewAction extends Action
  6. {
  7. public $modelClass;
  8. public function run($id)
  9. {
  10. $class = $this->modelClass;
  11. if (($model = $class::findOne($id)) === null) {
  12. throw new NotFoundHttpException('The requested page does not exist.');
  13. }
  14. return $this->controller->render('//crud/view', [
  15. 'model' => $model
  16. ]);
  17. }
  18. }
  1. 创建视图文件@app/views/crud/create.php
  1. <?php
  2. use yii\helpers\Html;
  3. use yii\widgets\ActiveForm;
  4. /*
  5. * @var yii\web\View $this
  6. */
  7. ?>
  8. <h1><?= Yii::t('app', 'Create post'); ?></h1>
  9. <?php $form = ActiveForm::begin();?>
  10. <?php $form->errorSummary($model); ?>
  11. <?= $form->field($model, 'title')->textInput() ?>
  12. <?= $form->field($model, 'content')->textarea() ?>
  13. <?= Html::submitButton(Yii::t('app', 'Create'), ['class' =>
  14. 'btn btn-primary']) ?>
  15. <?php ActiveForm::end(); ?>
  1. 创建视图文件@app/views/crud/index.php
  1. <?php
  2. use yii\widgets\LinkPager;
  3. use yii\helpers\Html;
  4. use yii\helpers\Url;
  5. /*
  6. * @var yii\web\View $this
  7. * @var yii\data\Pagination $pages
  8. * @var array $models
  9. */
  10. ?>
  11. <h1>Posts</h1>
  12. <?= Html::a('+ Create a post', Url::toRoute('post/create')); ?>
  13. <?php foreach ($models as $model):?>
  14. <h3><?= Html::encode($model->title);?></h3>
  15. <p><?= Html::encode($model->content);?></p>
  16. <p>
  17. <?= Html::a('view', Url::toRoute(['post/view', 'id' =>
  18. $model->id]));?> |
  19. <?= Html::a('delete', Url::toRoute(['post/delete', 'id'
  20. => $model->id]));?>
  21. </p>
  22. <?php endforeach; ?>
  23. <?= LinkPager::widget([
  24. 'pagination' => $pages,
  25. ]); ?>
  1. 创建视图文件@app/views/crud/view.php
  1. <?php
  2. use yii\helpers\Html;
  3. use yii\helpers\Url;
  4. /*
  5. * @var yii\web\View $this
  6. * @var app\models\Post $model
  7. */
  8. ?>
  9. <p><?= Html::a('< back to posts', Url::toRoute('post/index'));
  10. ?></p>
  11. <h2><?= Html::encode($model->title);?></h2>
  12. <p><?= Html::encode($model->content);?></p>

为了使用独立动作,我们在动作map中通过复写动作方法定义它。

  1. 运行post/index

第二章 路由,控制器和视图 - 图9

工作原理…

每一个控制器可以从独立动作中创建,就像是在拼图。区别是你可以是独立动作非常灵活,并在很多地方复用。

在我们的动作中,我们定义了modelClass公共属性,它能在PostControlleractions方法中设置一个指定的类。

参考

欲了解更多信息, 参考http://www.yiiframework.com/doc-2.0/guide-structurecontrollers.html#standalone-actions

创建一个自定义过滤器

过滤器是一种对象,它会在控制器动作之前或者之后运行。例如,一个访问控制过滤器可能会在动作之前运行,确保它们只能被特殊的终端用户访问;一个内容压缩过滤器可能会在动作之后运行,用于在发送给终端用户之前压缩响应内容。

一个过滤器可能由一个前处理器(在动作之前执行)和/或一个后处理器(在动作之后执行)。过滤器本质上是一种特殊的行为。因此,使用过滤器和使用行为是一样的。

假设我们有一个web应用,它提供了一个用户界面,只在指定的小时内工作,例如从上午10点到下午6点。

准备

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

如何做…

  1. 创建一个控制器@app/controllers/TestController.php
  1. <?php
  2. namespace app\controllers;
  3. use app\components\CustomFilter;
  4. use yii\helpers\Html;
  5. use yii\web\Controller;
  6. class TestController extends Controller
  7. {
  8. public function behaviors()
  9. {
  10. return [
  11. 'access' => [
  12. 'class' => CustomFilter::className(),
  13. ],
  14. ];
  15. }
  16. public function actionIndex()
  17. {
  18. return $this->renderContent(Html::tag('h1',
  19. 'This is a test content'
  20. ));
  21. }
  22. }
  1. 创建一个新的过滤器@app/components/CustomFilter.php
  1. <?php
  2. namespace app\components;
  3. use Yii;
  4. use yii\base\ActionFilter;
  5. use yii\web\HttpException;
  6. class CustomFilter extends ActionFilter
  7. {
  8. const WORK_TIME_BEGIN = 10;
  9. const WORK_TIME_END = 18;
  10. protected function canBeDisplayed()
  11. {
  12. $hours = date('G');
  13. return $hours >= self::WORK_TIME_BEGIN && $hours <= self::WORK_TIME_END;
  14. }
  15. public function beforeAction($action)
  16. {
  17. if (!$this->canBeDisplayed())
  18. {
  19. $error = 'This part of website works from '
  20. . self::WORK_TIME_BEGIN . ' to '
  21. . self::WORK_TIME_END . ' hours.';
  22. throw new HttpException(403, $error);
  23. }
  24. return parent::beforeAction($action);
  25. }
  26. public function afterAction($action, $result)
  27. {
  28. if (Yii::$app->request->url == '/test/index') {
  29. Yii::trace("This is the index action");
  30. }
  31. return parent::afterAction($action, $result);
  32. }
  33. }
  1. 如果你在指定的时间之外访问页面,你将得到如下结果:

第二章 路由,控制器和视图 - 图10

工作原理…

首先,我们添加一些代码到我们的控制器中,它实现了我们的自定义过滤器:

  1. public function behaviors()
  2. {
  3. return [
  4. 'access' => [
  5. 'class' => CustomFilter::className(),
  6. ],
  7. ];
  8. }

默认情况下,过滤器会应用到控制器所有的动作上,但是我们可以指定哪些动作可以被应用,或者哪些动作不被应用。

你有两个动作——beforeActionafterAction。第一个会在控制器动作执行之前运行,第二个会在之后运行。

在我们的简单的例子中,我们定义了一个条件,如果时间早于早上10点,不允许访问网站,并且在after方法中,我们只是运行了一个trace方法,如果当前路径是test/index的话。

你可以在debugger中看到这个结果,在log部分:

第二章 路由,控制器和视图 - 图11

在真实的应用中,过滤器是比较复杂的,并且,Yii2提供了需要内置的过滤器,例如core、authentication、content negotiator,HTTP cache end等等。

参考

欲了解更多信息,参考http://www.yiiframework.com/doc-2.0/guidestructure-filters.html

展示静态页面

如果你有一些静态页面,并且不会经常修改他们,那么不值得查询数据库,并为他们做页面管理。

准备

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

如何做…

  1. 创建一个测试控制器文件@app/controllers/TestController.php
  1. <?php
  2. namespace app\controllers;
  3. use yii\web\Controller;
  4. class TestController extends Controller
  5. {
  6. public function actions()
  7. {
  8. return [
  9. 'page' => [
  10. 'class' => 'yii\web\ViewAction',
  11. ]
  12. ];
  13. }
  14. }
  1. 现在,将你的页面放进views/test/pages,命名为index.phpcontact.phpindex.php文件的内容如下:
  1. <h1>Index</h1>
  2. content of index file
  3. Contact.php content is:
  4. <h2>Contacts</h2>
  5. <p>Our contact: contact@localhost</p>
  1. 现在你可以通过访问URL检查你的页面
  2. http://yii-book.app/index.php?r=test/page&view=contact

第二章 路由,控制器和视图 - 图12

  1. 或者,如果你配置干净的URL格式的话,你可以访问http://yii-book.app/test/page/view/about

工作原理…

我们连接了外部动作,名叫\yii\web\ViewAction,它只是尝试去找到一个视图,和$_GET参数提供的名称一致。如果找到了,展示它。如果找不到,将会给一个404 not found的页面。如果没有设置viewParam,将会使用默认值defaultView

更多…

关于ViewAction

\yii\web\ViewAction有一些有用的参数。列表如下:

参数名称 描述
defaultView 如果用户没有在GET参数中提供yii\web\ViewAction::$viewParam,默认视图的名称。默认值是’index’。格式应该是path/to/view。同GET参数中的类似
layout 应用到请求视图的布局名称。它会在视图被渲染前分配给yii\base\Controller::$layout。默认值是null,意味着使用控制器的布局。如果为false,不使用布局。
viewParam 包含请求视图名称GET参数的名称
viewPrefix 这是一个字符串,会作为一个前缀附加到用户指定的视图名称上,构成一个完整的视图名称。例如,如果一个用户请求是tutorial/chap1,相应的视图名称是pages/tutorial/chap1,假设前缀是pages。真实的视图文件又yii\base\View::findViewFile()决定。

配置URL规则

ViewAction动作为你提供了一种方式,可以用于修改你的控制器,但是这个URL看着像http://yii-book.app/index.php?r=test/page&page=about。为了使URL更短更可读,添加一个URL规则到urlManager组件:

  1. '<view:about>' => 'test/page'

如果urlManager组件配置正确,你将会得到如下页面:

第二章 路由,控制器和视图 - 图13

为了配置urlManager组件,参考配置URL规则小节。

参考

欲了解更多信息,参考如下地址:

使用flash消息

当你使用一个form编辑一个模型时,删除一个模型,或者做其它操作,这是一个好习惯,告诉用户是否工作正常,或者发生了错误。典型情况下,在一些动作之后,例如编辑一个form,一个重定向将会发生,并且我们需要在页面上展示一条信息。但是,我们应该如何将它从当前页面传递到重定向目标,然后清理干净?flash消息将会帮助我们。

准备

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

如何做…

  1. 创建一个控制器@app/controllers/TestController.php
  1. <?php
  2. namespace app\controllers;
  3. use Yii;
  4. use yii\web\Controller;
  5. use yii\filters\AccessControl;
  6. class TestController extends Controller
  7. {
  8. public function behaviors()
  9. {
  10. return [
  11. 'access' => [
  12. 'class' => AccessControl::className(),
  13. 'rules' => [
  14. [
  15. 'allow' => true,
  16. 'roles' => ['@'],
  17. 'actions' => ['user']
  18. ],
  19. [
  20. 'allow' => true,
  21. 'roles' => ['?'],
  22. 'actions' => ['index', 'success',
  23. 'error']
  24. ],
  25. ],
  26. 'denyCallback' => function ($rule, $action) {
  27. Yii::$app->session->setFlash('error',
  28. 'This section is only for registered users.');
  29. $this->redirect(['index']);
  30. },
  31. ],
  32. ];
  33. }
  34. public function actionUser()
  35. {
  36. return $this->renderContent('user');
  37. }
  38. public function actionSuccess()
  39. {
  40. Yii::$app->session->setFlash('success', 'Everything went fine!');
  41. $this->redirect(['index']);
  42. }
  43. public function actionError()
  44. {
  45. Yii::$app->session->setFlash('error', 'Everything went wrong!');
  46. $this->redirect(['index']);
  47. }
  48. public function actionIndex()
  49. {
  50. return $this->render('index');
  51. }
  52. }
  1. 此外,创建@app/views/common/alert.php视图:
  1. <?php
  2. use yii\bootstrap\Alert;
  3. ?>
  4. <?php if (Yii::$app->session->hasFlash('success')):?>
  5. <?= Alert::widget([
  6. 'options' => ['class' => 'alert-success'],
  7. 'body' => Yii::$app->session->getFlash('success'),
  8. ]);?>
  9. <?php endif ?>
  10. <?php if (Yii::$app->session->hasFlash('error')) :?>
  11. <?= Alert::widget([
  12. 'options' => ['class' => 'alert-danger'],
  13. 'body' => Yii::$app->session->getFlash('error'),
  14. ]);?>
  15. <?php endif; ?>
  1. 创建视图@app/views/test/index.php
  1. <?php
  2. /* @var $this yii\web\View */
  3. ?>
  4. <?= $this->render('//common/alert') ?>
  5. <h2>Guest page</h2>
  6. <p>There's a content of guest page</p>
  1. 创建视图@app/views/test/user.php
  1. <?php
  2. /* @var $this yii\web\View */
  3. ?>
  4. <?= $this->render('//common/alert') ?>
  5. <h2>User page</h2>
  6. <p>There's a content of user page</p>
  1. 现在,如果你访问http://yii-book.app/index.php?r=test/success,你将会被重定向到http://yii-book.app/index.php?r=test/index,并展示了一条成功的消息:

第二章 路由,控制器和视图 - 图14

  1. 此外,如果你访问http://yii-book.app/index.php?r=test/error,你将会被重重定向到相同的页面上,但是会得到一条错误消息。刷新index页面,消息将会隐藏:

第二章 路由,控制器和视图 - 图15

  1. 尝试运行http://yii-book.app/index.php?r=test/user。你将会被重定向到http://yii-book.app/index.php?r=test/index,并会在denyCallback函数中执行并展示一条错误消息:

第二章 路由,控制器和视图 - 图16

工作原理…

我们使用Yii::$app->session->('success', 'Everything went fine!')设置一条flash消息。本质上,它保存了一条消息到一个session中,所以在最低等级上,我们的消息被保存在$_SESSION中,直到Yii::$app->session->getFlash('success')被调用,然后$_SESSION键会被删除。

这个flash消息将会在请求中访问之后被自动删除。

更多…

getAllFlashes()方法

有时你需要处理所有的flashs。你可以使用一个简单的方式来处理它,如下所示:

  1. $flashes = Yii::$app->session->getAllFlashes();
  2. <?php foreach ($flashes as $key => $message): ?>
  3. <?= Alert::widget([
  4. 'options' => ['class' => 'alert-info'],
  5. 'body' => $message,
  6. ]);
  7. ?>
  8. <?php endforeach; ?>

removeAllFlashes()方法

当你需要flush所有的flash时,使用下面的方法:

  1. Yii::$app->session->removeAllFlashes();

removeFlash()方法

当你需要移除指定的键,使用如下方法:

  1. Yii::$app->session->removeFlash('success');

在这个例子中,我们添加一个非常有用的回调函数,它设置一条错误信息,并重定向到test/index页面上。

参考

欲了解更多信息,参考:

在一个视图中使用控制器上下文

Yii视图非常强大,并且有许多特性。其中一个就是你可以在一个视图中使用控制器上下文。所以,我们来试下吧。

准备

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

如何做…

  1. 创建controllers/ViewController.php
  1. <?php
  2. namespace app\controllers;
  3. use yii\web\Controller;
  4. class ViewController extends Controller
  5. {
  6. public $pageTitle;
  7. public function actionIndex()
  8. {
  9. $this->pageTitle = 'Controller context test';
  10. return $this->render('index');
  11. }
  12. public function hello()
  13. {
  14. if (!empty($_GET['name'])) {
  15. echo 'Hello, ' . $_GET['name'] . '!';
  16. }
  17. }
  18. }
  1. 现在,我们创建views/view.php来展示我们可以做的事情:
  1. <h1><?= $this->context->pageTitle ?></h1>
  2. <p>Hello call. <?php $this->context->hello() ?></p>
  1. 为了测试它,你可以访问/index.php?r=view/index&name=Alex

第二章 路由,控制器和视图 - 图17

工作原理…

我们在一个视图中使用$this来引用当前运行的控制器。当做这些事情的时候,我们可以调用一个控制器方法,并访问他的属性。最常使用的属性是pageTitle,它表示当前页面的标题。在视图中,有许多内置的方法特别有用,例如renderPartials和小组件。

更多…

http://www.yiiframework.com/doc-2.0/guide-structure-views.html#accessing-data-in-views地址包含了CController的API文档,这里你可以到方法的列表,这些你可以用在你的视图中。

部分复用视图

Yii支持部分视图,所以,如果你有一个块,其中没有太多的逻辑,你希望可以复用,或者想实施电子邮件模板,部分视图是处理这种问题的正确方法。

假设我们有两个Twitter账户,其中一个用与博客,另外一个用于公司活动,我们的目标是在指定的页面上输出Twitter时间线。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. php_netyiiframework两个用户在https://twitter.com/settings/widgets/创建Twitter小组件,并为每一个创建的小组件找到data-widget-id值。

如何做…

  1. 创建一个控制器@app/controllers/BlogController.php
  1. <?php
  2. namespace app\controllers;
  3. use yii\web\Controller;
  4. class BlogController extends Controller
  5. {
  6. public function actionIndex()
  7. {
  8. $posts = [
  9. [
  10. 'title' => 'First post',
  11. 'content' => 'There\'s an example of reusing views with partials.',
  12. ],
  13. [
  14. 'title' => 'Second post',
  15. 'content' => 'We use twitter widget.'
  16. ],
  17. ];
  18. return $this->render('index', [
  19. 'posts' => $posts
  20. ]);
  21. }
  22. }
  1. 创建一个名为@app/views/common/twitter.php的视图文件,并粘贴从Twitter复制过来的嵌入代码。你将会得到如下代码:
  1. <?php
  2. /* @var $this \yii\web\View */
  3. /* @var $widget_id integer */
  4. /* @var $screen_name string */
  5. ?>
  6. <script>!function(d,s,id){var
  7. js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?
  8. 'http':'https';if(!d.getElementById(id)){js=d.createElement(s);j
  9. s.id=id;js.src=p+"://platform.twitter.com/
  10. widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"scr
  11. ipt","twitter-wjs");</script>
  12. <?php if ($widget_id && $screen_name): ?>
  13. <a class="twitter-timeline"
  14. data-widget-id="<?= $widget_id?>"
  15. href="https://twitter.com/<?= $screen_name?>"
  16. height="300">
  17. Tweets by @<?= $screen_name?>
  18. </a>
  19. <?php endif;?>
  1. 创建一个视图@app/views/blog/index.php
  1. <?php
  2. /* @var $category string */
  3. /* @var $posts array */
  4. /* @var $this \yii\web\View */
  5. ?>
  6. <div class="row">
  7. <div class="col-xs-7">
  8. <h1>Posts</h1>
  9. <hr>
  10. <?php foreach ($posts as $post): ?>
  11. <h3><?= $post['title']?></h3>
  12. <p><?= $post['content']?></p>
  13. <?php endforeach;?>
  14. </div>
  15. <div class="col-xs-5">
  16. <?= $this->render('//common/twitter', [
  17. 'widget_id' => '620531418213576704',
  18. 'screen_name' => 'php_net',
  19. ]);?>
  20. </div>
  21. </div>
  1. 使用如下内容替换@app/views/site/about.php文件的内容:
  1. <?php
  2. use yii\helpers\Html;
  3. /* @var $this yii\web\View */
  4. $this->title = 'About';
  5. ?>
  6. <div class="col-xs-7">
  7. <h1><?= Html::encode($this->title) ?></h1>
  8. <p>
  9. This is the About page. You may modify this page.
  10. </p>
  11. </div>
  12. <div class="col-xs-5">
  13. <?= $this->render('//common/twitter', [
  14. 'widget_id' => '620526086343012352',
  15. 'screen_name' => 'yiiframework'
  16. ]);?>
  17. </div>
  1. 尝试运行index.php?r=blog/index

第二章 路由,控制器和视图 - 图18

  1. 尝试运行index.php?r=site/about

第二章 路由,控制器和视图 - 图19

工作原理…

在当前例子中,两个视图使用一个额外的参数渲染了@app/views/common/twitter.php,从而构成Twitter小组件。注意到视图可以在控制器、小组件或者其它任何地方渲染,方法是通过调用视图渲染方法。例如,\yii\base\Controller::render做和\yii\base\View::render相同的模板处理,不同点是前者不使用布局。

在每一个视图文件中,我们可以使用$this访问View类的两个实例,所以任何视图文件都可以在其它任何视图中通过调用render方法调用。

更多…

欲了解更多信息,参考http://www.yiiframework.com/doc-2.0/guidestructureviews.html#rendering-views

使用blocks

Yii的一个特性是,你可以在你的视图中使用blocks。基本的思想是,你可以渲染一些输出,然后在一个视图中复用它。一个好的例子是,为你的布局定义额外的内容区域,然后在其它任何地方填充他们。

在先前的版本中,Yii 1.1,blocks被叫做clips。

准备

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

如何做…

  1. 对于我们的例子,我们需要在我们的布局中定义两个区域——beforeContentfooter
  2. 打开@app/views/layouts/main.php并将如下内容插入到内容输出前:
  1. <?php if(!empty($this->blocks['beforeContent'])) echo $this->blocks['beforeContent']; ?>
  1. 然后,使用如下代码替换footer代码:
  1. <footer class="footer">
  2. <div class="container">
  3. <?php if (!empty($this->blocks['footer'])):
  4. echo $this->blocks['footer'] ?>
  5. <?php else: ?>
  6. <p class="pull-left">&copy; My Company <?= date('Y') ?></p>
  7. <p class="pull-right"><?= Yii::powered() ?></p>
  8. <?php endif; ?>
  9. </div>
  10. </footer>
  1. 完成了!然后,添加一个新的动作到controllers/SiteController.php,名叫blocks
  1. public function actionBlocks()
  2. {
  3. return $this->render('blocks');
  4. }
  1. 现在,创建一个视图文件views/site/blocks.php
  1. <?php
  2. use \yii\Helpers\Html;
  3. /* @var $this \yii\web\View */
  4. ?>
  5. <?php $this->beginBlock('beforeContent');
  6. echo Html::tag('pre', 'Your IP is ' . Yii::$app->request->userIP);
  7. $this->endBlock(); ?>
  8. <?php $this->beginBlock('footer');
  9. echo Html::tag('h3', 'My custom footer block');
  10. $this->endBlock(); ?>
  11. <h1>Blocks usage example</h1>
  1. 现在,当你打开你的/index.php?r=site/blocks页面,你应该能在页面内容之前获得你的IP,以及一个build-with note in the footer:

第二章 路由,控制器和视图 - 图20

工作原理…

我们用代码标记一个区域,它会检查一个指定的block是否存在,并且如果这个block存在,这个代码就会输出它。然后,我们使用指定的控制器方法为我们定义的blocks记录内容,这两个方法是beginBlockendBlock

从控制器,你可以很容易地通过$this->view->blocks['blockID']访问我们的block的变量。

更多…

使用装饰器

在Yii中,我们可以将内容封装到一个装饰器中。装饰器的常用方法是布局。当你使用你的控制器的渲染方法渲染一个视图的时候,Yii自动使用主布局装饰它。让我们创建一个简单的装饰器,它会正确的格式化引用。

准备

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

如何做…

  1. 首先,我们将会创建一个装饰器文件@app/views/decorators/quote.php
  1. <div class="quote">
  2. <h2>&ldquo;<?= $content?>&rdquo;, <?= $author?></h2>
  3. </div>
  1. 现在,使用如下代码替换@app/views/site/index.php文件的内容:
  1. <?php
  2. use yii\widgets\ContentDecorator;
  3. /* @var */
  4. ?>
  5. <?php ContentDecorator::begin([
  6. 'viewFile' => '@app/views/decorators/quote.php',
  7. 'view' => $this,
  8. 'params' => ['author' => 'S. Freud']
  9. ]
  10. );?>
  11. Time spent with cats is never wasted.
  12. <?php ContentDecorator::end();?>
  1. 现在,你的Home页面应该会如下所示:

第二章 路由,控制器和视图 - 图21

工作原理…

装饰器非常简单。ContentDecorator::begin()ContentDecorator::end()之间的任何东西都会被渲染到一个$content变量中,并传递到一个装饰器模板中。然后,这个装饰器模板被渲染,并被插入到ContentDecorator::end()被调用的地方中。

我们可以使用ContentDecorator::begin()第二个参数传递额外的变量到装饰器模板中,例如之前的例子中我们传递了author变量。

注意我们使用了@app/views/decorators/quote.php作为视图路径。

参考

定义多个布局

大部分应用位所有的视图使用同一个布局。但是,有些情况需要使用多个布局。例如,一个应用在不同的页面上有不同的布局:博客有两个额外的列,文章有一个额外的列,归档没有额外的列。

准备

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

如何做…

  1. views/layouts中创建两个布局:blogarticlesblog的代码如下:
  1. <?php $this->beginContent('//layouts/main')?>
  2. <div>
  3. <?= $content ?>
  4. </div>
  5. <div class="sidebar tags">
  6. <ul>
  7. <li><a href="#php">PHP</a></li>
  8. <li><a href="#yii">Yii</a></li>
  9. </ul>
  10. </div>
  11. <div class="sidebar links">
  12. <ul>
  13. <li><a href="http://yiiframework.com/">Yiiframework</a></li>
  14. <li><a href="http://php.net/">PHP</a></li>
  15. </ul>
  16. </div>
  17. <?php $this->endContent()?>
  1. articles的代码如下:
  1. <?php
  2. /* @var $this yii\web\View */
  3. ?>
  4. <?php $this->beginContent('@app/views/layouts/main.php'); ?>
  5. <div class="container">
  6. <div class="col-xs-8">
  7. <?= $content ?>
  8. </div>
  9. <div class="col-xs-4">
  10. <h4>Table of contents</h4>
  11. <ol>
  12. <li><a href="#intro">Introduction</a></li>
  13. <li><a href="#quick-start">Quick start</a></li>
  14. <li>..</li>
  15. </ol>
  16. </div>
  17. </div>
  18. <?php $this->endContent() ?>
  1. 创建一个视图文件views/site/content.php
  1. <h1>Title</h1>
  2. <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit,
  3. sed do eiusmod tempor incididunt ut labore et dolore magna
  4. aliqua. Ut enim ad minim veniam, quis nostrud exercitation
  5. ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
  6. aute irure dolor in reprehenderit in voluptate velit esse
  7. cillum dolore eu fugiat nulla pariatur.</p>
  1. 创建三个控制器,名叫BlogControllerArticleControllerPortfolioController,每一个都一个index动作。controllers/BlogController.php文件内容如下:
  1. <?php
  2. namespace app\controllers;
  3. use yii\web\Controller;
  4. class BlogController extends Controller
  5. {
  6. public $layout = 'blog';
  7. public function actionIndex()
  8. {
  9. return $this->render('//site/content');
  10. }
  11. }
  1. controllers/ArticleController.php文件的内容如下:
  1. <?php
  2. namespace app\controllers;
  3. use yii\web\Controller;
  4. class ArticleController extends Controller
  5. {
  6. public $layout = 'articles';
  7. public function actionIndex()
  8. {
  9. return $this->render('//site/content');
  10. }
  11. }
  1. controllers/PortfolioController.php文件的内容如下:
  1. <?php
  2. namespace app\controllers;
  3. use yii\web\Controller;
  4. class PortfolioController extends Controller
  5. {
  6. public function actionIndex()
  7. {
  8. return $this->render('//site/content');
  9. }
  10. }
  1. 尝试运行http://yii-book.app/?r=blog/index

第二章 路由,控制器和视图 - 图22

  1. 尝试运行http://yii-book.app/?r=article/index

第二章 路由,控制器和视图 - 图23

  1. 尝试运行http://yii-book.app/?r=portfolio/index

第二章 路由,控制器和视图 - 图24

工作原理…

我们为博客和文章定义了两个额外的布局。因为我们不想从主布局中拷贝和粘贴相同的部分,我们使用$this->beginContent$this->endContent做额外的布局装饰器。

所以,我们使用一个在文章布局的内部渲染的视图,作为主布局的$content

参考

页码和数据排序

在最新的Yii发布版本中,焦点从直接使用Active Record移到了grids、lists和data providers。但是,有时直接使用Active Record是更好的。下面我们来看如果列出分好页的AR记录,并有能力对他们进行排序。在这部分中,我们将会创建电影的一个列表,并通过数据库中的一些属性对他们进行排序。在我们的例子中,我们将会通过电影标题和租用率对他们进行排序。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. http://dev.mysql.com/doc/index-other.html下载Sakila数据库。
  3. 执行下载的SQL:首先是schema,然后是数据。
  4. config/main.php中配置DB连接,来使用Sakila数据库。
  5. 使用Gii生成Film模型

如何做…

  1. 首先,你需要创建@app/controllers/FilmController.php
  1. <?php
  2. namespace app\controllers;
  3. use app\models\Film;
  4. use yii\web\Controller;
  5. use yii\data\Pagination;
  6. use yii\data\Sort;
  7. class FilmController extends Controller
  8. {
  9. public function actionIndex()
  10. {
  11. $query = Film::find();
  12. $countQuery = clone $query;
  13. $pages = new Pagination(['totalCount' => $countQuery->count()]);
  14. $pages->pageSize = 5;
  15. $sort = new Sort([
  16. 'attributes' => [
  17. 'title',
  18. 'rental_rate'
  19. ]
  20. ]);
  21. $models = $query->offset($pages->offset)
  22. ->limit($pages->limit)
  23. ->orderBy($sort->orders)
  24. ->all();
  25. return $this->render('index', [
  26. 'models' => $models,
  27. 'sort' => $sort,
  28. 'pages' => $pages
  29. ]);
  30. }
  31. }
  1. 现在,让我们实现@app/views/film/index.php
  1. <?php
  2. use yii\widgets\LinkPager;
  3. /**
  4. * @var \app\models\Film $models
  5. * @var \yii\web\View $this
  6. * @var \yii\data\Pagination $pages
  7. * @var \yii\data\Sort $sort
  8. */
  9. ?>
  10. <h1>Films List</h1>
  11. <p><?=$sort->link('title')?> |
  12. <?=$sort->link('rental_rate')?></p>
  13. <?php foreach ($models as $model): ?>
  14. <div class="list-group">
  15. <h4 class="list-group-item-heading"> <?=$model->title ?>
  16. <label class="label label-default">
  17. <?=$model->rental_rate ?>
  18. </label>
  19. </h4>
  20. <p class="list-group-item-text"><?=$model->description
  21. ?></p>
  22. </div>
  23. <?php endforeach ?>
  24. <?=LinkPager::widget([
  25. 'pagination' => $pages
  26. ]); ?>
  1. 尝试访问http://yii-book.app/index.php?r=film/index。你应该能得到一个工作的分页,和允许通过电影标题和租用率排序的链接:

第二章 路由,控制器和视图 - 图25

工作原理…

首先,我们得到了全部模型的数量,通过将这个数传递给Pagination实例的totalCount变量,初始化了新的分页组件实例。然后我们使用$page->pageSize字段为我们的分页设置每页的大小。然后,我们为这个模型创建了一个sorter实例,指定了我们希望用作排序的模型属性,并通过调用orderBy应用排序条件到查询,然后将其传递给$sort->orders做为一个参数。然后,我们调用了all()从DB中获取记录。

现在,我们有了模型的列表、页面以及被用于link pager的数据,以及我们用于生成排序连接的sorter。

在这个视图中,我们使用我们搜集的数据。首先,我们使用Sort::link生成链接。然后,我们列出模型。最后,使用LinkPager小组件,我们渲染了分页控制。

参考

访问如下地址,获取更多关于分页和排序的信息: