yii2

已更新至 2.0.13

yii2-cookbook-chinese: https://zhyoulun.github.io/yii2-cookbook-chinese/
Yii 2.0 Community Cookbook: https://github.com/samdark/yii2-cookbook/blob/master/book/README.md

Yii2 速记表:http://nai8.me/tool-sc.html
Yii2 干货集:https://github.com/forecho/awesome-yii2

Model: http://haobing.wang/yii2-model
View: http://haobing.wang/yii2-view
Controller: http://haobing.wang/yii2-controller

安装

2.0.12 切换到了 asset-packagist,不再需要安装 fxp/composer-asset-plugin 了。

composer global show
composer global require "fxp/composer-asset-plugin:^1.4.2"

安装 Yii2 模板

yiisoft/yii2-app-basic 模板还是 yiisoft/yii2-app-advanced

yiisoft/yii2-app-advanced 可以看成是一个目录中放了两个 yiisoft/yii2-app-basic,外加了 environment 和实现了 User 的基本方法。

通常教程都是以 yiisoft/yii2-app-basic 为例,如果实际项目使用的是
yiisoft/yii2-app-advanced,只要把 app 目录脑补成 frontendbackend 就好了。

例如新建 app\modules\forum\Forum,等同于高级模板中的 frontend\modules\forum\Forum

yiiframework.com 源码

yiiframework.com 源码:https://github.com/yiisoft-contrib/yiiframework.com
整合 npm: https://github.com/tanakahisateru/yii2-app-basic-npm

结合上面两个仓库,可以将 bower 替换为 npm 包,让 composer 从 bower 中解放,composer 只安装 php 包,资源包使用 npm 来安装。这样 composer 和 npm 都可以使用国内镜像源,项目部署会变得十分方便,几分钟就可以把依赖安装完。

给出了一个使用 Yii2 开发网站应用的实例,极具参考价值!

应用了:

  • Gulp
  • Browsersync
  • Sass

Advanced 模板

推荐使用移除 bower 的版本:

  1. 更换 composer 仓库地址为中国镜像源
  2. 更换 bower 包为 npm 包
  3. 项目依赖 Node.js, 需要安装 yarn 并配合淘宝镜像源使用。
composer create-project --prefer-dist blackhive/yii2-app-advanced app

或者使用官方版本:

composer create-project --prefer-dist yiisoft/yii2-app-advanced yii2-application

注意:2.0.13 之前的版本,如果不先安装 fxp/composer-asset-plugin,就会报错:

➜  yii2-application git:(master) ✗ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - yiisoft/yii2 2.0.9 requires bower-asset/jquery 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable -> no matching package found.
   ...
    - Installation request for yiisoft/yii2 ~2.0.6 -> satisfiable by yiisoft/yii2[2.0.10, 2.0.11, 2.0.11.1, 2.0.11.2, 2.0.12, 2.0.6, 2.0.7, 2.0.8, 2.0.9].

fxp/composer-asset-plugin 会读取 bower.json ,从 github 拉取 yii2 依赖的 jQeury 等资源的最新源码特别慢,特别慢,特别慢。这里也需要提供 github 的 token,所以从 Yii2.0.13 开始使用 asset-packagist.org 不再使用 fxp/composer-asset-plugin。(asset-packagist.org 也是从 github 拉取 npm 和 bower 包,速度一样慢)

Both "basic" and "advanced" application templates are now using asset-packagist.org and aren't relying on Composer asset plugin.

检查环境兼容:

cd yii2-application
php init
Yii Application Initialization Tool v1.0

Which environment do you want the application to be initialized in?

  [0] Development
  [1] Production

  Your choice [0-1, or "q" to quit] 0

  Initialize the application under 'Development' environment? [yes|no] yes

  Start initialization ...

   generate backend/config/main-local.php
   generate backend/config/params-local.php
   generate backend/config/test-local.php
   generate backend/web/index-test.php
   generate backend/web/index.php
   generate backend/web/robots.txt
   generate common/config/main-local.php
   generate common/config/params-local.php
   generate common/config/test-local.php
   generate console/config/main-local.php
   generate console/config/params-local.php
   generate frontend/config/main-local.php
   generate frontend/config/params-local.php
   generate frontend/config/test-local.php
   generate frontend/web/index-test.php
   generate frontend/web/index.php
   generate frontend/web/robots.txt
   generate yii
   generate yii_test
   generate yii_test.bat
   generate cookie validation key in backend/config/main-local.php
   generate cookie validation key in frontend/config/main-local.php
      chmod 0777 backend/runtime
      chmod 0777 backend/web/assets
      chmod 0777 frontend/runtime
      chmod 0777 frontend/web/assets
      chmod 0755 yii
      chmod 0755 yii_test

  ... initialization completed.

Copy /requirements.php to /web/requirements.php and then use a browser to access it via http://localhost/requirements.php

调整路径:
$frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2'; => $frameworkPath = dirname(__FILE__) . '/../../vendor/yiisoft/yii2';

To use this application component, the APC PHP extension must be loaded. Alternatively APCu PHP extension could be used via setting useApcu to true. In order to enable APC or APCu for CLI you should add "apc.enable_cli = 1" to your php.ini.

2.Create a new database and adjust the components['db'] configuration in common/config/main-local.php accordingly.

3.Open a console terminal, apply migrations with command /path/to/php-bin/php /path/to/yii-application/yii migrate

配置相关

检查权限

  • chmod('runtime', 0777)...done.
  • chmod('web/assets', 0777)...done.
  • chmod('yii', 0755)...done.

更改默认路由

# config/web.php 
$config = [
    'defaultRoute' => 'index',//更改默认控制器
    // 其他内容
]

配置中文

开启中文显示

# config/web.php 
$config = [
    'language'=>'zh-CN',//开启中文显示
    // 其他内容
]

// view/site/index.php
// 在视图文件中使用默认语言包 yii
<?= Yii::t('yii','Home')?>

配置语言包

# config/web.php 
'components' => [
    // ...
    'i18n' => [
        'translations' => [
            'app*' => [
                'class' => 'yii\i18n\PhpMessageSource',
                //'basePath' => '@app/messages',
                //'sourceLanguage' => 'en-US',
                'fileMap' => [
                    'app' => 'app.php',
                    'app/error' => 'error.php',
                ],
            ],
        ],
    ],
],

配置 Gii 和 Debug (开发环境)

如果你不是在本机开发,而是使用 vagrant,需要在 debug 和 gii 模块中添加如下参数:

// uncomment the following to add your IP if you are not connecting from localhost.
'allowedIPs' => ['127.0.0.1', '::1','192.168.10.134'],

这里的 192.168.10.134 是我写代码的电脑 IP。

添加语言包文件

# 新建文件 messages/zh-CN/app.php
return [
  'App' => '应用',
  'NI Hao' => '你好'
];

# 新建文件 messages/zh-CN/error.php 
return [
    'BigError' => '错误了'
];

使用语言包:翻译

# 使用自己添加的语言包
echo Yii::t('app','App'); // 输出:应用
echo Yii::t('app','NI Hao'); // 输出:你好
echo Yii::t('app/error','BigError');// 输出:错误了

$username = 'Alexander';
// 输出:“Hello, Alexander”
echo \Yii::t('app', 'NI Hao, {username}!', [
    'username' => $username,
]);

$price = 100;
$count = 2;
$subtotal = 200;
echo \Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', $price, $count, $subtotal);

Url 帮助类

use yii\helpers\Url;

Url::home();
$absoluteHomeUrl = Url::home(true);
$httpsAbsoluteHomeUrl = Url::home('https');
Url::toRoute(['index/post', 'id' => 1]);

使用 gii 生成代码

如果当前是开发环境, 应用会包含 gii 模块,模块类是 yii\gii\Module。你可以直接通过 URL 访问 Gii:http://hostname/index.php?r=gii

补充: 如果你通过本机以外的机器访问 Gii,请求会被出于安全原因拒绝。 你可以配置 Gii 为其添加允许访问的 IP 地址:

'gii' => [
    'class' => 'yii\gii\Module',
    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // 按需调整这里
],

因为使用的是 basic 模板,所以需要新建一个后台模块。

查询生成器(Query Builder)

查询构建器建立在 Database Access Objects(DAO) 基础之上,可让你创建 程序化的、DBMS无关的SQL语句。相比于原生的SQL语句,查询构建器可以帮你 写出可读性更强的SQL相关的代码,并生成安全性更强的SQL语句。

使用查询构建器通常包含以下两个步骤:

  1. 创建一个 yii\db\Query 对象来代表一条 SELECT SQL 语句的不同子句(例如 SELECT, FROM)。
  2. 执行 yii\db\Query 的一个查询方法(例如:all())从数据库当中检索数据。

如下所示代码是查询构造器的一个典型用法:

$rows = (new \yii\db\Query())
    ->select(['id', 'email'])
    ->from('user')
    ->where(['last_name' => 'Smith'])
    ->limit(10)
    ->all();

上面的代码将会生成并执行如下的SQL语句,其中 :last_name 参数绑定了 字符串 'Smith'。

SELECT `id`, `email` 
FROM `user`
WHERE `last_name` = :last_name
LIMIT 10

select

$query->select(['id', 'email']);
// 等同于:
$query->select('id, email');

# 就像写原生 SQL 语句一样,被选取的字段可以包含表前缀,以及/或者字段别名。 例如:
$query->select(['user.id AS user_id', 'email']);
// 等同于:
$query->select('user.id AS user_id, email');

// 如果使用数组格式来指定字段,你可以使用数组的键值来表示字段的别名。 例如,上面的代码可以被重写为如下形式:
$query->select(['user_id' => 'user.id', 'email']);

# 如果你在组建查询时没有调用 select() 方法,那么选择的将是 '*' , 也即选取的是所有的字段。


# 怎么实现where id>45 and uid=1?

# 方式1
Chat::find()->where(['>', 'id', 45])->andWhere(['uid' => 1])->asArray()->all();
# 方式2:预处理
Chat::find()->where('id > :id and uid = :uid', [':id'=>45, ':uid'=> 1])->asArray()->all();

# 如果先看到底运行的原生sql是什么可以用这样
$query = Post::find()->where('author_id = :author_id and status = :status', [':author_id'=>2, ':status'=> 2]);
$commandQuery = clone $query;
echo $commandQuery->createCommand()->getRawSql();
print_r($query->all());
# yii2中hasOne方法如何添加条件?

# 方法1 在 getter 中添加 where
public function getCanWeiList(){
    $tableAlias = Table::tableName();
    $where['status'] = 1;
    return $this->hasOne(Table::className(),['tableid'=>'id'])->where($where);
}

# 方法2 在 with 中添加 where
Customer::find()->with([
    'orders' => function ($query) {
        $query->andWhere('status = 1');
    },
    'country',
])->all();

复制和读写分离

许多数据库支持数据库复制来获得更好的数据库可用性, 以及更快的服务器响应时间。 通过数据库复制功能, 数据从所谓的主服务器被复制到从服务器。 所有的写和更新必须发生在主服务器上, 而读可以发生在从服务器上。

为了利用数据库复制并且完成读写分离,你可以按照下面的方法来配置 yii\db\Connection 组件:

[
    'class' => 'yii\db\Connection',

    // 主库的配置
    'dsn' => 'dsn for master server',
    'username' => 'master',
    'password' => '',

    // 从库的通用配置
    'slaveConfig' => [
        'username' => 'slave',
        'password' => '',
        'attributes' => [
            // 使用一个更小的连接超时
            PDO::ATTR_TIMEOUT => 10,
        ],
    ],

    // 从库的配置列表
    'slaves' => [
        ['dsn' => 'dsn for slave server 1'],
        ['dsn' => 'dsn for slave server 2'],
        ['dsn' => 'dsn for slave server 3'],
        ['dsn' => 'dsn for slave server 4'],
    ],
]

上述的配置指定了一主多从的设置。 这些从库其中之一将被建立起连接并执行读操作, 而主库将被用来执行写操作。 这样的读写分离将通过上述配置自动地完成。 比如,

// 使用上述配置来创建一个 Connection 实例
Yii::$app->db = Yii::createObject($config);

// 在从库中的一个上执行语句
$rows = Yii::$app->db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();

// 在主库上执行语句
Yii::$app->db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute();

Info: 通过调用 yii\db\Command::execute() 来执行的语句都被视为写操作, 而其他所有通过调用 yii\db\Command 中任一 "query" 方法来执行的语句都被视为读操作。 你可以通过 Yii::$app->db->slave 来获取当前有效的从库连接。

Connection 组件支持从库间的负载均衡和失效备援, 当第一次执行读操作时, Connection 组件将随机地挑选出一个从库并尝试与之建立连接, 如果这个从库被发现为”挂掉的“, 将尝试连接另一个从库。 如果没有一个从库是连接得上的, 那么将试着连接到主库上。 通过配置 server status cache, 一个“挂掉的”服务器将会被记住, 因此,在一个 yii\db\Connection::serverRetryInterval 内将不再试着连接该服务器。

Info: 在上面的配置中, 每个从库都共同地指定了 10 秒的连接超时时间, 这意味着,如果一个从库在 10 秒内不能被连接上, 它将被视为“挂掉的”。 你可以根据你的实际环境来调整该参数。

Yii2 中使用 sass

在运行环境 vagrant ubuntu box 中安装 sass 即可:

sudo su -c "gem install sass"

在 AppAsset.php 中直接引入 sass 文件

class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
 
    public $css = [
        'css/index.scss', // 引入 scss 文件
    ];
    // 其他内容...
}

调整配置

'assetManager' => [
    'converter' => [
          'class' => 'yii\web\AssetConverter',
          'commands' => [
              'scss' => ['css', 'sass {from} {to} --sourcemap'],
              // 其他命令
              'less' => ['css', 'lessc {from} {to} --no-color --source-map'],
              'sass' => ['css', 'sass {from} {to} --sourcemap'],
              'styl' => ['css', 'stylus < {from} > {to}'],
              'coffee' => ['js', 'coffee -p {from} > {to}'],
              'ts' => ['js', 'tsc --out {to} {from}'],
          ],
    ],
],

备注:在 windows git-shell 下编译好的 css 放到 yii2 下,总会出现诡异的乱码问题,索性直接在 vagrant ubuntu box 里安装 sass 了。

CSRF

Yii2 默认是启用CSRF令牌验证的。记得安装 basic 的第一步就是填写 cookieValidationKey

CSRF 验证是在 beforeAction 中完成的。

缓存

理解缓存,着重在于理解 缓存存储器缓存依赖

缓存存储器,解决的是数据存在哪里的问题。

缓存依赖,解决的是什么时候刷新缓存存储器中的数据的问题。

缓存依赖是可选的,因为缓存存储器支持 duration,即过期时间,单位为秒。但是,实际生产中,还是结合过期时间和缓存依赖实用效果最好。

缓存存储器

缓存组件,代表各种缓存存储器,例如内存,文件,数据库。缓存组件通常注册为应用程序组件,这样就可以全局进行配置和访问。例如,配置应用程序组件 cache 使用两个 memcached 服务器:

'components' => [
    'cache' => [
        'class' => 'yii\caching\MemCache',
        'servers' => [
            [
                'host' => 'server1',
                'port' => 11211,
                'weight' => 100,
            ],
            [
                'host' => 'server2',
                'port' => 11211,
                'weight' => 50,
            ],
        ],
    ],
],

注意:在实际开发中,我们会使用多种缓存存储器,这时就需要注册多个缓存组件。很多依赖缓存的类默认调用名为 cache 的组件(例如 yii\web\UrlManager),实际使用中需要指定我们真正要使用的缓存存储器对应的缓存组件id。

页面缓存

应用场景:首页,文章列表页,但是不能缓存有用户状态的页面。

public function behaviors()
{
    return [
        'pageCache' => [
            'class' => 'yii\filters\PageCache',
            'only' => ['index'],
            'duration' => 60,
            'dependency' => [
                //'db' => \Yii::$app->dbRead,// 默认使用 db,否则需要指定数据库名称
                'class' => 'yii\caching\DbDependency',
                'sql' => 'SELECT COUNT(*) FROM post',
            ],
            'variations' => [
                \Yii::$app->language,
            ]
        ],
    ];
}

缓存依赖

  • yii\caching\DbDependency:如果指定 SQL 语句的查询结果发生了变化,则依赖改变。
  • yii\caching\ExpressionDependency:如果指定的 PHP 表达式执行结果发生变化,则依赖改变。
  • yii\caching\FileDependency:如果文件的最后修改时间发生变化,则依赖改变。

参考Yii2性能优化之:缓存依赖

yii console

vagrant@dev:/vagrant_data/app$ /yii
-bash: /yii: No such file or directory

用vim打开该sh文件,输入: :set ff 回车,显示fileformat=dos,重新设置下文件格式: :set ff=unix保存退出,再执行。

Model

Controller

  • 控制器 ID: post-comment 对应控制器类 PostCommentController。
  • 动作 ID:create-comment 对应方法名 actionCreateComment。

behaviors

actions

不要忘了 return

// 返回给浏览器
return $this->render('list');

yii\web\Controller::render() 方法会 自动把视图执行的结果嵌入称为布局的文件中, 位于 views/layouts/main.php

View

视图是你用来生成响应内容的脚本。

<?php
use yii\helpers\Html;
?>
<p>You have entered the following information:</p>

<ul>
    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>
    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>
</ul>

layout

如何将数据库数据渲染到模板?

step1.新建一层 Controller

use yii\web\Controller;
use app\models\Category;

class CommonController extends Controller
{
    public function init()
    {
        $menu = Category::getMenu();
        //
        $this->view->params['menu'] = $menu;
    }
}

step2.在模板中使用数据

<?php foreach($this->params['menu'] as $topCategory) { 
             print_r($topCategory);
} ?>

Forms

Yii2 中的表单如果使用 ActiveForm 就会自动应用 CSRF 检查,可以提供整个应用的安全性。

Form 中也要引入 Model,来接收和显示数据,相应的 Model 还有用来验证数据 rule。

<?php

namespace app\models;

use Yii;
use yii\base\Model;

class EntryForm extends Model
{
    public $name;
    public $email;

    public function rules()
    {
        return [
            [['name', 'email'], 'required'],
            ['email', 'email'],
        ];
    }
}

yii\base\Model 被用于普通模型类的父类并与数据表无关。yii\db\ActiveRecord 通常是普通模型类的父类但与数据表有关联。

从 $_POST 搜集用户提交的数据, 由 Yii 的 yii\web\Request::post() 方法负责搜集。如果模型被成功填充数据(比如:如果用户已经提交了 HTML 表单), 动作将调用 validate() 去确保用户提交的是有效数据。

数据首先由客户端 JavaScript 脚本验证,然后才会提交给服务器通过 PHP 验证。yii\widgets\ActiveForm 足够智能到把你在 EntryForm 模型中声明的验证规则转化成客户端 JavaScript 脚本去执行验证。 如果用户浏览器禁用了 JavaScript, 服务器端仍然会像 actionEntry() 方法里这样验证一遍数据。 这保证了任何情况下用户提交的数据都是有效的。

Pagination

<?php

namespace app\controllers;

use yii\web\Controller;
use yii\data\Pagination;
use app\models\Country;

class CountryController extends Controller
{
    public function actionIndex()
    {
        $query = Country::find();

        $pagination = new Pagination([
            'defaultPageSize' => 5,
            'totalCount' => $query->count(),
        ]);

        $countries = $query->orderBy('name')
            ->offset($pagination->offset)
            ->limit($pagination->limit)
            ->all();

        return $this->render('index', [
            'countries' => $countries,
            'pagination' => $pagination,
        ]);
    }
}

为了限定每个请求所返回的国家数量,查询在 yii\data\Pagination 对象的帮助下进行分页。 Pagination 对象的使命主要有两点:

  • 为 SQL 查询语句设置 offset 和 limit 从句, 确保每个请求只需返回一页数据(本例中每页是 5 行)。
  • 在视图中显示一个由页码列表组成的分页器, 这点将在后面的段落中解释。

对应视图:

<?php
use yii\helpers\Html;
use yii\widgets\LinkPager;
?>
<h1>Countries</h1>
<ul>
<?php foreach ($countries as $country): ?>
    <li>
        <?= Html::encode("{$country->name} ({$country->code})") ?>:
        <?= $country->population ?>
    </li>
<?php endforeach; ?>
</ul>

<?= LinkPager::widget(['pagination' => $pagination]) ?>

过滤器(Filters)

过滤器可包含 预过滤(过滤逻辑在动作之前) 或 后过滤(过滤逻辑在动作之后), 也可同时包含两者。

例如访问控制过滤器可在动作执行之前来控制特殊终端用户是否有权限执行动作, 内容压缩过滤器可在动作执行之后发给终端用户之前压缩响应内容。

控制器类的过滤器默认应用到该类的 所有 动作, 你可以配置 only属性明确指定控制器应用到哪些动作。

public function behaviors()
{
    return [
        [
            'class' => 'yii\filters\HttpCache',
            'only' => ['index', 'view'],
            'lastModified' => function ($action, $params) {
                $q = new \yii\db\Query();
                return $q->from('user')->max('updated_at');
            },
        ],
    ];
}

在上述例子中, HttpCache 过滤器只应用到index和view动作。 也可以配置except属性使一些动作不执行过滤器。

小部件(Widgets)

创建自己的小部件,让不同视图文件共用代码。

前端资源(Assets)

Yii中的资源是和Web页面相关的文件,可为CSS文件,JavaScript文件,图片或视频等, 资源放在Web可访问的目录下,直接被Web服务器调用。

通过程序自动管理资源更好一点,例如, 当你在页面中使用 yii\jui\DatePicker 小部件时, 它会自动包含需要的CSS和JavaScript文件,而不是要求你手工去找到这些文件并包含, 当你升级小部件时,它会自动使用新版本的资源文件。

推荐将资源文件放到Web目录以避免不必要的发布资源过程。

对于 扩展 来说, 由于它们的资源和源代码都在不能Web访问的目录下, 在定义资源包类时必须指定sourcePath属性。这些资源不在 web 目录下,所以不能被Web直接访问, 为了使用这些源资源,需要将它们拷贝到一个可Web访问的Web目录中 成为发布的资源。

例如:

<?php
namespace app\assets;

use yii\web\AssetBundle;

class FontAwesomeAsset extends AssetBundle 
{
    public $sourcePath = '@bower/font-awesome'; 
    public $css = [ 
        'css/font-awesome.min.css', 
    ];
    public $publishOptions = [
        'only' => [
            'fonts/',
            'css/',
        ]
    ];
}  

如果使用国内 CDN 的话,需要改为:

<?php
namespace app\assets;

use yii\web\AssetBundle;

class FontAwesomeAsset extends AssetBundle 
{
    public $sourcePath = null; 
    public $css = [ 
        '//cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css', 
    ];
} 

资源选项

  • cssOptions: 当调用yii\web\View::registerCssFile()注册该包 每个 css文件时, 指定传递到该方法的选项。
  • jsOptions: 当调用yii\web\View::registerJsFile()注册该包 每个 JavaScript文件时, 指定传递到该方法的选项。

例如,只想IE9或更高的浏览器包含一个CSS文件,可以使用如下选项:

public $cssOptions = ['condition' => 'lte IE9'];

JavaScript文件默认包含在body的结束处。为使JavaScript文件包含在页面head区域,需要使用以下选项:

public $jsOptions = ['position' => \yii\web\View::POS_HEAD];

自定义资源包

例如,yii\web\JqueryAsset 资源包默认从jquery Bower包中使用jquery.js 文件, 为了提高可用性和性能,你可能需要从国内的 CDN 服务器上获取jquery文件, 可以在应用配置中配置assetManager,如下所示:

return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => [
                    'sourcePath' => null,   // 因为是外部资源,所以需要覆盖掉原有的值 '@bower/jquery/dist'
                    'js' => [
                        '//cdn.bootcss.com/jquery/2.2.4/jquery.min.js',// 默认值为 'jquery.js',
                    ]
                ],
            ],
        ],
    ],
];

请求处理(Handling Requests)

路由 和 URL

[
    'http://admin.example.com/login' => 'admin/user/login',
    'http://www.example.com/login' => 'site/login',
]

响应(Responses)

因为响应格式默认为HTML, 只需要在动作方法中返回一个字符串, 如果想使用其他响应格式,应在返回数据前先设置格式,例如:

public function actionInfo()
{
    \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    return [
        'message' => 'hello world',
        'code' => 100,
    ];
}

Sessions 和 Cookies

开启和关闭 Sessions

$session = Yii::$app->session;

// 检查session是否开启 
if ($session->isActive) ...

// 开启session
$session->open();

// 关闭session
$session->close();

// 销毁session中所有已注册的数据
$session->destroy();

多次调用open() 和close() 方法并不会产生错误, 因为方法内部会先检查session是否已经开启。

访问 session 数据

$session = Yii::$app->session;

// 获取session中的变量值
$language = $session->get('language');

// 设置一个session变量
$session->set('language', 'en-US');

// 删除一个session变量
$session->remove('language');

// 检查session变量是否已存在
if ($session->has('language')) ...

// 遍历所有session变量
foreach ($session as $name => $value) ...

注意: 当使用session组件访问session数据时候, 如果session没有开启会自动开启, 这和通过$_SESSION不同,$_SESSION要求先执行session_start()。

为更好的性能和可读性,推荐将每个数组项变成有相同键前缀的session变量, 也就是不用存储session数组:

// 使用带通用前缀的键,而不是数组。
$session['captcha.number'] = 5;
$session['captcha.lifetime'] = 3600;

自定义 session 存储

yii\web\Session 类默认存储 session 数据为文件到服务器上,Yii 提供以下 session 类实现不同的 session 存储方式:

  • yii\web\DbSession: 存储session数据在数据表中
  • yii\web\CacheSession: 存储session数据到缓存中,缓存和配置中的缓存组件相关
  • yii\redis\Session: 存储session数据到以redis 作为存储媒介中
  • yii\mongodb\Session: 存储session数据到MongoDB.

当我们将数据库里的数据删除掉,再刷新浏览器页面,就会发现用户登录状态被清空了。

Flash 数据

Falsh 数据是一种特别的 session 数据,它一旦在某个请求中设置后,只会在下次请求中有效,然后该数据就会自动被删除。常用于实现只需显示终端用户一次的信息,如用户提交一个表单后显示确认信息。

显示 Flash 的方法:

echo Alert::widget([
   'options' => ['class' => 'alert-info'],
   'body' => Yii::$app->session->getFlash('postDeleted'),
]);

通过给每个cookie签发一个 哈希字符串来告知服务端cookie是否在客户端被修改, 如果被修改,通过request组件的yii\web\Request::cookiescookie集合访问不到该cookie。

Cookie验证默认启用,可以设置yii\web\Request::$enableCookieValidation属性为false来禁用它, 尽管如此,我们强烈建议启用它。

注意: 直接通过$_COOKIE 和 setcookie() 读取和发送的Cookie不会被验证。

当使用cookie验证,必须指定yii\web\Request::$cookieValidationKey,它是用来生成s上述的哈希值, 可通过在应用配置中配置request 组件。

return [
    'components' => [
        'request' => [
            'cookieValidationKey' => 'fill in a secret key here',
        ],
    ],
];

补充: cookieValidationKey 对你的应用安全很重要, 应只被你信任的人知晓,请不要将它放入版本控制中。

日志(Logging)

日志消息

记录日志消息就跟调用下面的日志方法一样简单:

  • Yii::trace():记录一条消息去跟踪一段代码是怎样运行的。这主要在开发的时候使用。
  • Yii::info():记录一条消息来传达一些有用的信息。
  • Yii::warning():记录一个警告消息用来指示一些已经发生的意外。
  • Yii::error():记录一个致命的错误,这个错误应该尽快被检查。
Yii::trace('start calculating average revenue', __METHOD__);

日志目标

默认设置,日志是记录到 @runtime/logs/app.log,按照如下配置,更改 @app/config/console.php@app/config/web.php

注意:,这里如果不修改 `` ,在执行 migrate 命令时会报错:

yii\base\InvalidConfigException: You should configure "log" component to use one or more database targets

return [
    // the "log" component must be loaded during bootstrapping time
    'bootstrap' => ['log'],
    
    'components' => [
        'log' => [
            'targets' => [
                [
                    'class' => 'yii\log\DbTarget',
                    'levels' => ['error', 'warning'],
                ],
                [
                    'class' => 'yii\log\EmailTarget',
                    'levels' => ['error'],
                    'categories' => ['yii\db\*'],
                    'message' => [
                       'from' => ['log@example.com'],
                       'to' => ['admin@example.com', 'developer@example.com'],
                       'subject' => 'Database errors at example.com',
                    ],
                ],
            ],
        ],
    ],
];

创建日志表:

php yii migrate --migrationPath=@yii/log/migrations/

操作正常的话,就会创建好数据表 {{%log}}

Exception (异常)

use yii\web\NotFoundHttpException;

$order = Order::findOne(1);

if(!$order){
    throw new NotFoundHttpException('订单不存在');
}

理解 Yii2

理解 Yii2 要从 ObjectComponent 两个类开始:

  • yii\base\Object
  • yii\base\Component

Yii2 的自定义

Yii2 框架中,所有类的 public 属性都可以在初始化时显式传入自定义的参数,来实现覆盖默认属性的目的。可以查看官方 api 文档来发掘权威指南中没有涉及的使用技巧。