Url 助手(Url)
最后更新于:2022-04-01 04:02:30
# Url 帮助类
Url 帮助类提供一系列的静态方法来帮助管理 URL。
## 获得通用 URL
有两种获取通用 URLS 的方法 :当前请求的 home URL 和 base URL 。 为了获取 home URL ,使用如下代码:
~~~
$relativeHomeUrl = Url::home();
$absoluteHomeUrl = Url::home(true);
$httpsAbsoluteHomeUrl = Url::home('https');
~~~
如果没有传任何参数,这个方法将会生成相对 URL 。你可以传 `true` 来获得一个针对当前协议的绝对 URL; 或者,你可以明确的指定具体的协议类型( `https` , `http` )。
如下代码可以获得当前请求的 base URL:
`````php $relativeBaseUrl = Url::base(); $absoluteBaseUrl = Url::base(true); $httpsAbsoluteBaseUrl = Url::base('https'); `````
这个方法的调用方式和 `Url::home()` 的完全一样。
## 创建 URLs
为了创建一个给定路由的 URL 地址,请使用 `Url::toRoute()`方法。 这个方法使用 \yii\web\UrlManager 来创建一个 URL :
~~~
$url = Url::toRoute(['product/view', 'id' => 42]);
~~~
你可以指定一个字符串来作为路由,如: `site/index` 。如果想要指定将要被创建的 URL 的附加查询参数, 你同样可以使用一个数组来作为路由。数组的格式须为:
~~~
// generates: /index.php?r=site/index¶m1=value1¶m2=value2
['site/index', 'param1' => 'value1', 'param2' => 'value2']
~~~
如果你想要创建一个带有 anchor 的 URL ,你可以使用一个带有 `#` 参数的数组。比如:
~~~
// generates: /index.php?r=site/index¶m1=value1#name
['site/index', 'param1' => 'value1', '#' => 'name']
~~~
一个路由既可能是绝对的又可能是相对的。一个绝对的路由以前导斜杠开头(如: `/site/index`), 而一个相对的路由则没有(比如: `site/index` 或者 `index`)。一个相对的路由将会按照如下规则转换为绝对路由:
* 如果这个路由是一个空的字符串,将会使用当前 \yii\web\Controller::route 作为路由;
* 如果这个路由不带任何斜杠(比如 `index` ),它会被认为是当前控制器的一个 action ID, 然后将会把 \yii\web\Controller::uniqueId 插入到路由前面。
* 如果这个路由不带前导斜杠(比如: `site/index` ),它会被认为是相对当前模块(module)的路由, 然后将会把 \yii\base\Module::uniqueId 插入到路由前面。
从2.0.2版本开始,你可以用 [alias](http://www.yiichina.com/doc/guide/2.0/concept-aliases) 来指定一个路由。 在这种情况下, alias 将会首先转换为实际的路由, 然后会按照上述规则转换为绝对路由。
以下是该方法的一些例子:
~~~
// /index.php?r=site/index
echo Url::toRoute('site/index');
// /index.php?r=site/index&src=ref1#name
echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post/edit&id=100 assume the alias "@postEdit" is defined as "post/edit"
echo Url::toRoute(['@postEdit', 'id' => 100]);
// http://www.example.com/index.php?r=site/index
echo Url::toRoute('site/index', true);
// https://www.example.com/index.php?r=site/index
echo Url::toRoute('site/index', 'https');
~~~
还有另外一个方法 `Url::to()` 和 toRoute() 非常类似。这两个方法的唯一区别在于,前者要求一个路由必须用数组来指定。 如果传的参数为字符串,它将会被直接当做 URL 。
`Url::to()` 的第一个参数可以是:
* 数组:将会调用 toRoute() 来生成URL。比如: `['site/index']`, `['post/index', 'page' => 2]` 。 详细用法请参考 toRoute() 。
* 带前导 `@` 的字符串:它将会被当做别名, 对应的别名字符串将会返回。
* 空的字符串:当前请求的 URL 将会被返回;
* 普通的字符串:返回本身。
当 `$scheme` 指定了(无论是字符串还是 true ),一个带主机信息(通过 \yii\web\UrlManager::hostInfo 获得) 的绝对 URL 将会被返回。如果 `$url` 已经是绝对 URL 了, 它的协议信息将会被替换为指定的( https 或者 http )。
以下是一些使用示例:
~~~
// /index.php?r=site/index
echo Url::to(['site/index']);
// /index.php?r=site/index&src=ref1#name
echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post/edit&id=100 assume the alias "@postEdit" is defined as "post/edit"
echo Url::to(['@postEdit', 'id' => 100]);
// the currently requested URL
echo Url::to();
// /images/logo.gif
echo Url::to('@web/images/logo.gif');
// images/logo.gif
echo Url::to('images/logo.gif');
// http://www.example.com/images/logo.gif
echo Url::to('@web/images/logo.gif', true);
// https://www.example.com/images/logo.gif
echo Url::to('@web/images/logo.gif', 'https');
~~~
从2.0.3版本开始,你可以使用 yii\helpers\Url::current() 来创建一个基于当前请求路由和 GET 参数的 URL。 你可以通过传递一个`$params` 给这个方法来添加或者删除 GET 参数。 例如:
~~~
// assume $_GET = ['id' => 123, 'src' => 'google'], current route is "post/view"
// /index.php?r=post/view&id=123&src=google
echo Url::current();
// /index.php?r=post/view&id=123
echo Url::current(['src' => null]);
// /index.php?r=post/view&id=100&src=google
echo Url::current(['id' => 100]);
~~~
## 记住 URLs
有时,你需要记住一个 URL 并在后续的请求处理中使用它。 你可以用以下方式达到这个目的:
~~~
// Remember current URL
Url::remember();
// Remember URL specified. See Url::to() for argument format.
Url::remember(['product/view', 'id' => 42]);
// Remember URL specified with a name given
Url::remember(['product/view', 'id' => 42], 'product');
~~~
在后续的请求处理中,可以用如下方式获得记住的 URL:
~~~
$url = Url::previous();
$productUrl = Url::previous('product');
~~~
## 检查相对 URLs
你可以用如下代码检测一个 URL 是否是相对的(比如,包含主机信息部分)。
~~~
$isRelative = Url::isRelative('test/it');
~~~
Html 助手(Html)
最后更新于:2022-04-01 04:02:28
# Html 帮助类
任何一个 web 应用程序会生成很多 HTMl 超文本标记。如果超文本标记是静态的, 那么[将 PHP 和 HTML 混合在一个文件里](http://php.net/manual/en/language.basic-syntax.phpmode.php) 这种做法是非常高效的。但是,如果这些超文本标记是动态生成的,那么如果没有额外的辅助工具,这个过程将会变得复杂。 Yii 通过 HTML 帮助类来提供生成超文本标记的方法。这个帮助类包含有一系列的用于处理通用的 HTML 标签和其属性以及内容的静态方法。
> 注意:如果你的超文本标记接近静态的,那么最好是直接使用 HTML。 没有必要把所有的超文本标记都用 HTML 辅助类来生成。
## 基础
由于通过字符串连接来生成动态的 HTML 会很容易变得凌乱, Yii 提供了一系列的静态方法来操作标签配置并基于这些配置来创建对应的标签。
### 生成标签
生成一个标签的代码类似如下:
~~~
<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>
~~~
这个方法的第一个参数是标签名称。第二个是要装入到开始和结束标签间的内容。 注意到我们使用 `Html::encode` 。那是因为内容不会被自动的转码以允许在有需要的时候嵌套 HTML。 第三个参数是一个 HTML 配置数组,或者换言之,标签属性。在这个数组中,数组的下标是属性名称, 比如 `class`,`href` 或者 `target`,而值则是对应属性的值。
以上代码会生成如下 HTML :
~~~
<p class="username">samdark</p>
~~~
如果你只需要开启一个标签或者关闭一个标签,你可以使用 `Html::beginTag()` 和 `Html::endTag()` 方法。
标签属性( Options )在 Html 帮助类很多方法和大量的小部件中都有使用。在这些情况下, 有一些额外的处理我们需要知道:
* 如果一个值为 null ,那么对应的属性将不会被渲染。
* 如果是布尔类型的值的属性,将会被当做 [布尔属性](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes) 来处理。
* 属性的值将会用 yii\helpers\Html::encode() 方法进行 HTML 转码处理。
* 如果一个属性的值是一个数组,那么它将会被如下处理:
* 如果这个属性是一个如 yii\helpers\Html::$dataAttributes 所列的数据属性, 比如 `data` 或者 `ng`,一系列的属性列表将会被渲染,每个代表值数组中的元素。 比如: `'data' => ['id' => 1, 'name' => 'yii']` 将会生成 `data-id="1" data-name="yii"`; `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` 生成 `data-params='{"id":1,"name":"yii"}' data-status="ok"`。 注意后者 中,一个子数组被输出为 JSON 。
* 如果这个属性不是一个数据属性,那么值将会被 JSON-encoded。比如:`['params' => ['id' => 1, 'name' => 'yii']` 生成 `params='{"id":1,"name":"yii"}'`。
### 生成 CSS 类和样式
当开始构造一个 HTML 标签的属性时,我们经常需要对默认的属性进行修改。 为了添加或者删除 CSS 类,你可以使用如下代码:
~~~
$options = ['class' => 'btn btn-default'];
if ($type === 'success') {
Html::removeCssClass($options, 'btn-default');
Html::addCssClass($options, 'btn-success');
}
echo Html::tag('div', 'Pwede na', $options);
// in case of $type of 'success' it will render
// <div class="btn btn-success">Pwede na</div>
~~~
基于同样的目的,针对 `style` 属性:
~~~
$options = ['style' => ['width' => '100px', 'height' => '100px']];
// gives style="width: 100px; height: 200px; position: absolute;"
Html::addCssStyle($options, 'height: 200px; position: absolute;');
// gives style="position: absolute;"
Html::removeCssStyle($options, ['width', 'height']);
~~~
当使用 yii\helpers\Html::addCssStyle() 方法时,你可以指定一个和 CSS 属性相关的名值对的数组, 也可以直接是一个类似 `width: 100px; height: 200px;` 的字符串。这些格式将会自动的被 yii\helpers\Html::cssStyleFromArray() 和yii\helpers\Html::cssStyleToArray() 方法进行转换。方法 yii\helpers\Html::removeCssStyle() 接收一个包含要被移除的属性数组作为参数。 如果只想移除一个属性,你可以直接传递一个字符串。
### 标签内容的转码和解码
为了让内容能够正确安全的显示,一些 HTML 特殊字符应该被转码。在 PHP 中, 这个操作由 [htmlspecialchars](http://www.php.net/manual/en/function.htmlspecialchars.php) 和[htmlspecialchars_decode](http://www.php.net/manual/en/function.htmlspecialchars-decode.php) 完成。 直接使用这些方法的问题是,你总是需要指定转码所需的额外标志。由于标志一般总是不变的,而内容转码的过程为了避免一些安全问题, 需要和应用的默认过程匹配, Yii 提供了两个简单可用的对 PHP 原生方法的封装:
~~~
$userName = Html::encode($user->name);
echo $userName;
$decodedUserName = Html::decode($userName);
~~~
## 表单
处理表单标签是大量的重复性劳动并且易错。因此, Yii 也提供了一系列的方法来辅助处理表单标签。
> 注意: 考虑在处理 models 以及需要验证的情形下,使用 yii\widgets\ActiveForm 组件。
### 创建表单
表单可以用类似如下代码,使用 yii\helpers\Html::beginForm() 方法开启:
~~~
<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>
~~~
方法的第一个参数为表单将要被提交的 URL 地址。它可以以 Yii 路由的形式被指定,并由 yii\helpers\Url::to() 来接收处理。 第二个参数是使用的方法,默认为 `post` 方法。第三个参数为表单标签的属性数组。在上面的例子中, 我们把编码 POST 请求中的表单数据的方式改为 `multipart/form-data`。 如果是上传文件,这个调整是必须的。
关闭表单标签非常简单:
~~~
<?= Html::endForm() ?>
~~~
### 按钮
你可以用如下代码生成按钮:
~~~
<?= Html::button('Press me!', ['class' => 'teaser']) ?>
<?= Html::submitButton('Submit', ['class' => 'submit']) ?>
<?= Html::resetButton('Reset', ['class' => 'reset']) ?>
~~~
上述三个方法的第一个参数为按钮的标题,第二个是标签属性。标题默认没有进行转码,如果标题是由终端用输入的,那么请自行用 yii\helpers\Html::encode() 方法进行转码。
### 输入栏
input 相关的方法有两组:以 `active` 开头的被称为 active inputs,另一组则不以其开头。 active inputs 依据指定的模型和属性获取数据,而普通 input 则是直接指定数据。
一般用法如下:
~~~
type, input name, input value, options
<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>
type, model, model attribute name, options
<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>
~~~
如果你知道 input 类型,更方便的做法是使用以下快捷方法:
* yii\helpers\Html::buttonInput()
* yii\helpers\Html::submitInput()
* yii\helpers\Html::resetInput()
* yii\helpers\Html::textInput(), yii\helpers\Html::activeTextInput()
* yii\helpers\Html::hiddenInput(), yii\helpers\Html::activeHiddenInput()
* yii\helpers\Html::passwordInput() / yii\helpers\Html::activePasswordInput()
* yii\helpers\Html::fileInput(), yii\helpers\Html::activeFileInput()
* yii\helpers\Html::textarea(), yii\helpers\Html::activeTextarea()
Radios 和 checkboxes 在方法的声明上有一点点不同:
~~~
<?= Html::radio('agree', true, ['label' => 'I agree']);
<?= Html::activeRadio($model, 'agree', ['class' => 'agreement'])
<?= Html::checkbox('agree', true, ['label' => 'I agree']);
<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement'])
~~~
Dropdown list 和 list box 将会如下渲染:
~~~
<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>
<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>
<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>
<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>
~~~
第一个参数是 input 的名称,第二个是当前选中的值,第三个则是一个下标为列表值, 值为列表标签的名值对数组。
如果你需要使用多项选择, checkbox list 应该能够符合你的需求:
~~~
<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>
<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>
~~~
否则,用 radio list :
~~~
<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>
<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>
~~~
### Labels 和 Errors
如同 inputs 一样,Yii 也提供了两个方法用于生成表单 label 。 带 ative 方法用于从 model 中取数据,另外一个则是直接接收数据。
~~~
<?= Html::label('User name', 'username', ['class' => 'label username']) ?>
<?= Html::activeLabel($user, 'username', ['class' => 'label username'])
~~~
为了从一个或者一组 model 中显示表单的概要错误,你可以使用如下方法:
~~~
<?= Html::errorSummary($posts, ['class' => 'errors']) ?>
~~~
为了显示单个错误:
~~~
<?= Html::error($post, 'title', ['class' => 'error']) ?>
~~~
### Input 的名和值
Yii 提供了方法用于从 model 中获取 input 的名称,ids,值。这些主要用于内部调用, 但是有时候你也需要使用它们:
~~~
// Post[title]
echo Html::getInputName($post, 'title');
// post-title
echo Html::getInputId($post, 'title');
// my first post
echo Html::getAttributeValue($post, 'title');
// $post->authors[0]
echo Html::getAttributeValue($post, '[0]authors[0]');
~~~
在上面的例子中,第一个参数为模型,而第二个参数是属性表达式。 在最简单的表单中,这个属性表达式就是属性名称,但是在一些多行输入的时候, 它也可以是属性名以数组下标前缀或者后缀(也可能是同时)。
* `[0]content` 代表多行输入时第一个 model 的 content 属性的数据值。
* `dates[0]` 代表 dates 属性的第一个数组元素。
* `[0]dates[0]` 代表多行输入时第一个 model 的 dates 属性的第一个数组元素。
为了获取一个没有前缀或者后缀的属性名称,我们可以如下做:
~~~
// dates
echo Html::getAttributeName('dates[0]');
~~~
## 样式表和脚本
Yii 提供两个方法用于生成包含内联样式和脚本代码的标签。
~~~
<?= Html::style('.danger { color: #f00; }') ?>
Gives you
<style>.danger { color: #f00; }</style>
<?= Html::script('alert("Hello!");', ['defer' => true]);
Gives you
<script defer>alert("Hello!");</script>
~~~
如果你想要外联 css 样式文件,可以如下做:
~~~
<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>
generates
<!--[if IE 5]>
<link href="http://example.com/css/ie5.css" />
<![endif]-->
~~~
第一个参数是 URL。第二个参数是标签属性数组。比普通的标签配置项额外多出的是,你可以指定:
* `condition` 来让 `<link` 被条件控制注释包裹( IE hacker )。 希望你在未来不再需要条件控制注释。
* `noscript` 可以被设置为 `true` ,这样 `<link`就会被 `<noscript>`包裹,如此那么这段代码只有在浏览器不支持 JavaScript 或者被用户禁用的时候才会被引入进来。
为了外联 JavaScript 文件:
~~~
<?= Html::jsFile('@web/js/main.js') ?>
~~~
这个方法的第一个参数同 CSS 一样用于指定外联链接。第二个参数是一个标签属性数组。 同 `cssFile` 一样,你可以指定 `condtion`配置项。
## 超链接
有一个方法可以用于便捷的生成超链接:
~~~
<?= Html::a('Profile', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>
~~~
第一个参数是超链接的标题。它不会被转码,所以如果是用户输入数据, 你需要使用 `Html::encode()` 方法进行转码。第二个参数是`<a` 标签的 `href` 属性的值。 关于该参数能够接受的更详细的数据值,请参阅 [Url::to()](http://www.yiichina.com/doc/guide/2.0/helper-url)。第三个参数是标签的属性数组。
在需要的时候,你可以用如下代码生成 `mailto` 链接:
~~~
<?= Html::mailto('Contact us', 'admin@example.com') ?>
~~~
## 图片
为了生成图片标签,你可以如下做:
~~~
<?= Html::img('@web/images/logo.png', ['alt' => 'My logo']) ?>
generates
<img src="/docs/guide/2.0/http://example.com/images/logo.png" alt="My logo" />
~~~
除了 [aliases](http://www.yiichina.com/doc/guide/2.0/concept-aliases) 之外,第一个参数可以接受 路由,查询,URLs。 同 [Url::to()](http://www.yiichina.com/doc/guide/2.0/helper-url) 一样。
## 列表
无序列表可以如下生成:
~~~
<?= Html::ul($posts, ['item' => function($item, $index) {
return Html::tag(
'li',
$this->render('post', ['item' => $item]),
['class' => 'post']
);
}]) ?>
~~~
有序列表请使用 `Html::ol()` 方法。
Array 助手(ArrayHelper)
最后更新于:2022-04-01 04:02:25
# 数组助手类
除了[PHP中丰富的数组函数集](http://php.net/manual/zh/book.array.php), Yii 数组助手类提供了额外的静态方法,让你更高效地处理数组。
## 获取值
用原生PHP从一个对象、数组、或者包含这两者的一个复杂数据结构中获取数据是非常繁琐的。 你首先得使用`isset` 检查 key 是否存在, 然后如果存在你就获取它,如果不存在, 则提供一个默认返回值:
~~~
class User
{
public $name = 'Alex';
}
$array = [
'foo' => [
'bar' => new User(),
]
];
$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;
~~~
Yii 提供了一个非常方便的方法来做这件事:
~~~
$value = ArrayHelper::getValue($array, 'foo.bar.name');
~~~
方法的第一个参数是我们从哪里获取值。第二个参数指定了如何获取数据, 它可以是下述几种类型中的一个:
* 数组键名或者欲从中取值的对象的属性名称;
* 以点号分割的数组键名或者对象属性名称组成的字符串,上例中使用的参数类型就是该类型;
* 返回一个值的回调函数。
回调函数如下例所示:
~~~
$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {
return $user->firstName . ' ' . $user->lastName;
});
~~~
第三个可选的参数如果没有给定值,则默认为 `null`,如下例所示:
~~~
$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');
~~~
对于取到值后想要立即从数组中删除的情况,你可以使用 `remove` 方法:
~~~
$array = ['type' => 'A', 'options' => [1, 2]];
$type = ArrayHelper::remove($array, 'type');
~~~
执行了上述代码之后, `$array` 将包含 `['options' => [1, 2]]` 并且 `$type` 将会是 `A` 。 注意和 `getValue` 方法不同的是,`remove` 方法只支持简单键名。
## 检查键名的存在
`ArrayHelper::keyExists` 工作原理和[array_key_exists](http://php.net/manual/en/function.array-key-exists.php)差不多,除了 它还可支持大小写不敏感的键名比较,比如:
~~~
$data1 = [
'userName' => 'Alex',
];
$data2 = [
'username' => 'Carsten',
];
if (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {
echo "Please provide username.";
}
~~~
## 检索列
通常你要从多行数据或者多个对象构成的数组中获取某列的值,一个普通的例子是获取id值列表。
~~~
$data = [
['id' => '123', 'data' => 'abc'],
['id' => '345', 'data' => 'def'],
];
$ids = ArrayHelper::getColumn($array, 'id');
~~~
结果将是 `['123', '345']`。
如果需要额外的转换或者取值的方法比较复杂, 第二参数可以指定一个匿名函数:
~~~
$result = ArrayHelper::getColumn($array, function ($element) {
return $element['id'];
});
~~~
## 重建数组索引
按一个指定的键名重新索引一个数组,可以用 `index` 方法。输入的数组应该是多维数组或者是一个对象数组。 键名(译者注:第二个参数)可以是子数组的键名、对象的属性名, 也可以是一个返回给定元素数组键值的匿名函数。
如果一个键值(译者注:第二个参数对应的值)是 null,相应的数组元素将被丢弃并且不会放入到结果中,例如,
~~~
$array = [
['id' => '123', 'data' => 'abc'],
['id' => '345', 'data' => 'def'],
];
$result = ArrayHelper::index($array, 'id');
// the result is:
// [
// '123' => ['id' => '123', 'data' => 'abc'],
// '345' => ['id' => '345', 'data' => 'def'],
// ]
// using anonymous function
$result = ArrayHelper::index($array, function ($element) {
return $element['id'];
});
~~~
## 建立哈希表
为了从一个多维数组或者一个对象数组中建立一个映射表(键值对),你可以使用 `map`方法.`$from` 和 `$to` 参数分别指定了欲构建的映射表的键名和属性名。 根据需要,你可以按照一个分组字段 `$group` 将映射表进行分组,例如。
~~~
$array = [
['id' => '123', 'name' => 'aaa', 'class' => 'x'],
['id' => '124', 'name' => 'bbb', 'class' => 'x'],
['id' => '345', 'name' => 'ccc', 'class' => 'y'],
);
$result = ArrayHelper::map($array, 'id', 'name');
// 结果是:
// [
// '123' => 'aaa',
// '124' => 'bbb',
// '345' => 'ccc',
// ]
$result = ArrayHelper::map($array, 'id', 'name', 'class');
// 结果是:
// [
// 'x' => [
// '123' => 'aaa',
// '124' => 'bbb',
// ],
// 'y' => [
// '345' => 'ccc',
// ],
// ]
~~~
## 多维排序
`multisort` 方法可用来对嵌套数组或者对象数组进行排序,可按一到多个键名排序,比如,
~~~
$data = [
['age' => 30, 'name' => 'Alexander'],
['age' => 30, 'name' => 'Brian'],
['age' => 19, 'name' => 'Barney'],
];
ArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);
~~~
排序之后我们在 `$data` 中得到的值如下所示:
~~~
[
['age' => 19, 'name' => 'Barney'],
['age' => 30, 'name' => 'Brian'],
['age' => 30, 'name' => 'Alexander'],
];
~~~
第二个参数指定排序的键名,如果是单键名的话可以是字符串,如果是多键名则是一个数组, 或者是如下例所示的一个匿名函数:
~~~
ArrayHelper::multisort($data, function($item) {
return isset($item['age']) ? ['age', 'name'] : 'name';
});
~~~
第三个参数表示增降顺序。单键排序时,它可以是 `SORT_ASC` 或者 `SORT_DESC` 之一。 如果是按多个键名排序,你可以用一个数组为各个键指定不同的顺序。
最后一个参数(译者注:第四个参数)是PHP的排序标识(sort flag),可使用的值和调用PHP [sort()](http://php.net/manual/zh/function.sort.php) 函数时传递的值一样。
## 检测数组类型
想知道一个数组是索引数组还是联合数组很方便,这有个例子:
~~~
// 不指定键名的数组
$indexed = ['Qiang', 'Paul'];
echo ArrayHelper::isIndexed($indexed);
// 所有键名都是字符串
$associative = ['framework' => 'Yii', 'version' => '2.0'];
echo ArrayHelper::isAssociative($associative);
~~~
## HTML 编码和解码值
为了将字符串数组中的特殊字符做 HTML 编解码,你可以使用下列方法:
~~~
$encoded = ArrayHelper::htmlEncode($data);
$decoded = ArrayHelper::htmlDecode($data);
~~~
默认情况只会对值做编码(译者注:原文中是编码,应为编解码)。通过给第二个参数传 `false` ,你也可以对键名做编码。 编码将默认使用应用程序的字符集,你可以通过第三个参数指定该字符集。
## 合并数组
~~~
/**
* 将两个或者多个数组递归式的合并为一个数组。
* 如果每个数组有一个元素的键名相同,
* 那么后面元素的将覆盖前面的元素(不同于 array_merge_recursive)。
* 如果两个数组都有相同键名的数组元素(译者注:嵌套数组)
* 则将引发递归合并。
* 对数值型键名的元素,后面数组中的这些元素会被追加到前面数组中。
* @param array $a 被合并的数组
* @param array $b 合并的数组,你可以在第三、第四个
* 参数中指定另外的合并数组,等等
* @return 合并的结果数组 (原始数组不会被改变)
*/
public static function merge($a, $b)
~~~
## 对象转换为数组
你经常要将一个对象或者对象的数组转换成一个数组,常见的情形是,为了通过REST API提供数据数组(或其他使用方式), 将AR模型(活动记录模型)转换成数组。如下代码可完成这个工作:
~~~
$posts = Post::find()->limit(10)->all();
$data = ArrayHelper::toArray($posts, [
'app\models\Post' => [
'id',
'title',
// the key name in array result => property name
'createTime' => 'created_at',
// the key name in array result => anonymous function
'length' => function ($post) {
return strlen($post->content);
},
],
]);
~~~
第一个参数包含我们想要转换的数据,在本例中,我们要转换一个叫 `Post` 的 AR 模型。
第二个参数是每个类的转换映射表,我们在此设置了一个`Post` 模型的映射。 每个映射数组包含一组的映射,每个映射可以是:
* 一个要包含的照原样的字段名(和类中属性的名称一致);
* 一个由你可随意取名的键名和你想从中取值的模型列名组成的键值对;
* 一个由你可随意取名的键名和有返回值的回调函数组成的键值对;
这上面的转换结果将会是:
~~~
[
'id' => 123,
'title' => 'test',
'createTime' => '2013-01-01 12:00AM',
'length' => 301,
]
~~~
也可以在一个特定的类中实现yii\base\Arrayable接口, 从而为其对象提供默认的转换成数组的方法。
助手一览(Overview)
最后更新于:2022-04-01 04:02:23
# 助手类
> 注意:这部分正在开发中。
Yii 提供许多类来简化常见编码,如对字条串或数组的操作, HTML 代码生成,等等。这些助手类被编写在命名空间 `yii\helpers`下,并且 全是静态类 (就是说它们只包含静态属性和静态方法,而且不能实例化)。
可以通过调用其中一个静态方法来使用助手类,如下:
~~~
use yii\helpers\Html;
echo Html::encode('Test > test');
~~~
> 注意:为了支持 [自定义助手类](http://www.yiichina.com/doc/guide/2.0/helper-overview#customizing-helper-classes),Yii 将每一个助手类 分隔成两个类:一个基类 (例如 `BaseArrayHelper`) 和一个具体的类 (例如`ArrayHelper`). 当使用助手类时,应该仅使用具体的类版本而不使用基类。
## 核心助手类
Yii 发布版中提供以下核心助手类:
* [ArrayHelper](http://www.yiichina.com/doc/guide/2.0/helper-array)
* Console
* FileHelper
* [Html](http://www.yiichina.com/doc/guide/2.0/helper-html)
* HtmlPurifier
* Image
* Inflector
* Json
* Markdown
* Security
* StringHelper
* [Url](http://www.yiichina.com/doc/guide/2.0/helper-url)
* VarDumper
## 自定义助手类
如果想要自定义一个核心助手类 (例如 yii\helpers\ArrayHelper),你应该创建一个新的类继承 helpers对应的基类 (例如 yii\helpers\BaseArrayHelper) 并同样的命 名你的这个类 (例如 yii\helpers\ArrayHelper),包括它的命名空间。这个类 会用来替换框架最初的实现。
下面示例显示了如何自定义 yii\helpers\ArrayHelper 类的 yii\helpers\ArrayHelper::merge() 方法:
~~~
<?php
namespace yii\helpers;
class ArrayHelper extends BaseArrayHelper
{
public static function merge($a, $b)
{
// 你自定义的实现
}
}
~~~
将你的类保存在一个名为 `ArrayHelper.php` 的文件中。该文件可以在任何目录,例如 `@app/components`。
接下来,在你的应用程序 [入口脚本](http://www.yiichina.com/doc/guide/2.0/structure-entry-scripts) 处,在引入的 `yii.php` 文件后面 添加以下代码行,用 [Yii 自动加载器](http://www.yiichina.com/doc/guide/2.0/concept-autoloading) 来加载自定义类 代替框架的原始助手类:
~~~
Yii::$classMap['yii\helpers\ArrayHelper'] = '@app/components/ArrayHelper.php';
~~~
注意,自定义助手类仅仅用于如果你想要更改助手类中 现有的函数的行为。如果你想为你的应用程序添加附加功能,最好为它创建一个单独的 助手类。
助手类(Helpers)
最后更新于:2022-04-01 04:02:21
jQuery UI 小部件(jQuery UI Widgets)
最后更新于:2022-04-01 04:02:18
# JUI Extension for Yii 2
This is the jQuery UI extension for Yii 2\. It encapsulates [jQuery UI widgets](http://jqueryui.com/) as Yii widgets, and makes using jQuery UI widgets in Yii applications extremely easy.
## [](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/README.md#getting-started)Getting Started
* [Installation](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/installation.md)
* [Basic Usage](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/basic-usage.md)
## [](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/README.md#usage)Usage
* [Yii widgets](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/usage-widgets.md)
## [](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/README.md#additional-topics)Additional topics
* [Handling date input with the DatePicker](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/topics-date-picker.md)
Bootstrap 小部件(Bootstrap Widgets)
最后更新于:2022-04-01 04:02:16
# Twitter Bootstrap Extension for Yii 2
The extension includes support for the [Bootstrap 3](http://getbootstrap.com/) markup and components framework (also known as "Twitter Bootstrap"). Bootstrap is an excellent, responsive framework that can greatly speed up the client-side of your development process.
The core of Bootstrap is represented by two parts:
* CSS basics, such as a grid layout system, typography, helper classes, and responsive utilities.
* Ready to use components, such as forms, menus, pagination, modal boxes, tabs etc.
## [](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/README.md#getting-started)Getting Started
* [Installation](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/installation.md)
* [Basic Usage](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/basic-usage.md)
## [](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/README.md#usage)Usage
* [Yii widgets](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/usage-widgets.md)
* [Html helper](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/helper-html.md)
* [Asset Bundles](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/asset-bundles.md)
## [](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/README.md#additional-topics)Additional topics
* [Using the .less files of Bootstrap directly](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/topics-less.md)
小部件(Widgets)
最后更新于:2022-04-01 04:02:14
集成第三方代码(Working with Third-Party Code)
最后更新于:2022-04-01 04:02:11
# 引入第三方代码
有时,你可能会需要在 Yii 应用中使用第三方的代码。又或者是你想要在第三方系统中把 Yii 作为类库引用。在下面这个板块中,我们向你展示如何实现这些目标。
## 在 Yii 中使用第三方类库
要想在 Yii 应用中使用第三方类库,你主要需要确保这些库中的类文件都可以被正常导入或可以被自动加载。
### 使用 Composer 包
目前很多第三方的类库都以 [Composer](https://getcomposer.org/) 包的形式发布。你只需要以下两个简单的步骤即可安装他们:
1. 修改你应用的 `composer.json` 文件,并注明需要安装哪些 Composer 包。
2. 运行 `php composer.phar install` 安装这些包。
这些Composer 包内的类库,可以通过 Composer 的自动加载器实现自动加载。不过请确保你应用的 [入口脚本](http://www.yiichina.com/doc/guide/2.0/structure-entry-scripts)包含以下几行用于加载 Composer 自动加载器的代码:
~~~
// install Composer autoloader (安装 Composer 自动加载器)
require(__DIR__ . '/../vendor/autoload.php');
// include Yii class file (加载 Yii 的类文件)
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
~~~
### 使用下载的类库
若你的类库并未发布为一个 Composer 包,你可以参考以下安装说明来安装它。在大多数情况下,你需要预先下载一个发布文件,并把它解压缩到 `BasePath/vendor` 目录,这里的 `BasePath` 代指你应用程序自身的 [base path(主目录)](http://www.yiichina.com/doc/guide/2.0/structure-applications#basePath)。
若该类库包含他自己的类自动加载器,你可以把它安装到你应用的[入口脚本](http://www.yiichina.com/doc/guide/2.0/structure-entry-scripts)里。我们推荐你把它的安装代码置于 `Yii.php` 的导入之前,这样 Yii 的官方自动加载器可以拥有更高的优先级。
若一个类库并没有提供自动加载器,但是他的类库命名方式符合 [PSR-4](http://www.php-fig.org/psr/psr-4/) 标准,你可以使用 Yii 官方的自动加载器来自动加载这些类。你只需给他们的每个根命名空间声明一下[根路径别名](http://www.yiichina.com/doc/guide/2.0/concept-aliases#defining-aliases)。比如,假设说你已经在目录 `vendor/foo/bar` 里安装了一个类库,且这些类库的根命名空间为 `xyz`。你可以把以下代码放入你的应用配置文件中:
~~~
[
'aliases' => [
'@xyz' => '@vendor/foo/bar',
],
]
~~~
若以上情形都不符合,最可能是这些类库需要依赖于 PHP 的 include_path 配置,来正确定位并导入类文件。只需参考它的安装说明简单地配置一下 PHP 导入路径即可。
最悲催的情形是,该类库需要显式导入每个类文件,你可以使用以下方法按需导入相关类文件:
* 找出该库内包含哪些类。
* 在应用的[入口脚本](http://www.yiichina.com/doc/guide/2.0/structure-entry-scripts)里的 `Yii::$classMap` 数组中列出这些类,和他们各自对应的文件路径。
举例来说,
~~~
Yii::$classMap['Class1'] = 'path/to/Class1.php';
Yii::$classMap['Class2'] = 'path/to/Class2.php';
~~~
## 在第三方系统内使用 Yii
因为 Yii 提供了很多牛逼的功能,有时,你可能会想要使用它们中的一些功能用来支持开发或完善某些第三方的系统,比如:WordPress,Joomla,或是用其他 PHP 框架开发的应用程序。举两个例子吧,你可能会想念方便的 yii\helpers\ArrayHelper 类,或在第三方系统中使用 [Active Record](http://www.yiichina.com/doc/guide/2.0/db-active-record) 活动记录功能。要实现这些目标,你只需两个步骤:安装 Yii,启动 Yii。
若这个第三方系统支持 Composer 管理他的依赖文件,你可以直接运行一下命令来安装 Yii:
~~~
php composer.phar require yiisoft/yii2-framework:*
php composer.phar install
~~~
不然的话,你可以[下载](http://www.yiiframework.com/download/) Yii 的发布包,并把它解压到对应系统的 `BasePath/vendor` 目录内。
之后,你需要修改该第三方应用的入口脚本,在开头位置添加 Yii 的引入代码:
~~~
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
$yiiConfig = require(__DIR__ . '/../config/yii/web.php');
new yii\web\Application($yiiConfig); // 千万别在这调用 run() 方法。(笑)
~~~
如你所见,这段代码与典型的 Yii 应用的[入口脚本](http://www.yiichina.com/doc/guide/2.0/structure-entry-scripts)非常相似。唯一的不同之处在于在 Yii 应用创建成功之后,并不会紧接着调用 `run()`方法。因为,`run()` 方法的调用会接管 HTTP 请求的处理流程。(译注:换言之,这就不是第三方系统而是 Yii 系统了,URL 规则也会跟着换成 Yii 的规则了)
与 Yii 应用中一样,你可以依据运行该第三方系统的环境,针对性地配置 Yii 应用实例。比如,为了使用[活动记录](http://www.yiichina.com/doc/guide/2.0/db-active-record)功能,你需要先用该第三方系统的 DB 连接信息,配置 Yii 的 `db` 应用组件。
现在,你就可以使用 Yii 提供的绝大多数功能了。比如,创建 AR 类,并用它们来操作数据库。
## 配合使用 Yii 2 和 Yii 1
如果你之前使用 Yii 1,大概你也有正在运行的 Yii 1 应用吧。不必用 Yii 2 重写整个应用,你也可以通过增添对哪些 Yii 2 独占功能的支持来增强这个系统。下面我们就来详细描述一下具体的实现过程。
> 注意:Yii 2 需要 PHP 5.4+ 的版本。你需要确保你的服务器以及现有应用都可以支持 PHP 5.4。
首先,参考前文板块中给出的方法,在已有的应用中安装 Yii 2。
之后,如下修改 Yii 1 应用的入口脚步:
~~~
// 导入下面会详细说明的定制 Yii 类文件。
require(__DIR__ . '/../components/Yii.php');
// Yii 2 应用的配置文件
$yii2Config = require(__DIR__ . '/../config/yii2/web.php');
new yii\web\Application($yii2Config); // Do NOT call run()
// Yii 1 应用的配置文件
$yii1Config = require(__DIR__ . '/../config/yii1/main.php');
Yii::createWebApplication($yii1Config)->run();
~~~
因为,Yii 1 和 Yii 2 都包含有 `Yii` 这个类,你应该创建一个定制版的 Yii 来把他们组合起来。上面的代码里包含了的这个定制版的`Yii` 类,可以用以下代码创建出来:
~~~
$yii2path = '/path/to/yii2';
require($yii2path . '/BaseYii.php'); // Yii 2.x
$yii1path = '/path/to/yii1';
require($yii1path . '/YiiBase.php'); // Yii 1.x
class Yii extends \yii\BaseYii
{
// 复制粘贴 YiiBase (1.x) 文件中的代码于此
}
Yii::$classMap = include($yii2path . '/classes.php');
// 通过 Yii 1 注册 Yii2 的类自动加载器
Yii::registerAutoloader(['Yii', 'autoload']);
~~~
大功告成!此时,你可以在你代码的任意位置,调用 `Yii::$app` 以访问 Yii 2 的应用实例,而用 `Yii::app()` 则会返回 Yii 1 的应用实例:
~~~
echo get_class(Yii::app()); // 输出 'CWebApplication'
echo get_class(Yii::$app); // 输出 'yii\web\Application'
~~~
模板引擎(Template Engines)
最后更新于:2022-04-01 04:02:09
# 使用模板引擎
默认情况下,Yii 使用 PHP 作为其默认的模板引擎语言,但是,你可以配置 Yii 以扩展的方式支持其他的渲染引擎,比如 [Twig](http://twig.sensiolabs.org/) 或[Smarty](http://www.smarty.net/)等。
组件 `view` 就是用于渲染视图的。你可以重新配置这个组件的行为以增加一个自定义的模板引擎。
~~~
[
'components' => [
'view' => [
'class' => 'yii\web\View',
'renderers' => [
'tpl' => [
'class' => 'yii\smarty\ViewRenderer',
//'cachePath' => '@runtime/Smarty/cache',
],
'twig' => [
'class' => 'yii\twig\ViewRenderer',
'cachePath' => '@runtime/Twig/cache',
// Array of twig options:
'options' => [
'auto_reload' => true,
],
'globals' => ['html' => '\yii\helpers\Html'],
'uses' => ['yii\bootstrap'],
],
// ...
],
],
],
]
~~~
在上述的代码中, Smarty 和 Twig 都被配置以让视图文件使用。但是,为了让扩展安装到项目中,你同样需要修改你的`composer.json` 文件,如下:
~~~
"yiisoft/yii2-smarty": "*",
"yiisoft/yii2-twig": "*",
~~~
上述代码需要增加到 `composer.json` 的 `require` 节中。在做了上述修改,并保存后,你可以运行 `composer update --prefer-dist` 命令来安装扩展。
对于特定模板引擎的使用详细,请参考其文档:
* [Twig guide](https://github.com/yiisoft/yii2-twig/tree/master/docs/guide)
* [Smarty guide](https://github.com/yiisoft/yii2-smarty/tree/master/docs/guide)
共享主机环境(Shared Hosting Environment)
最后更新于:2022-04-01 04:02:07
# 共享托管环境
共享的托管环境常常会对目录结构以及配置文件有较多的限制。然而,在大多数情况下,你仍可以通过少量的修改以在共享托管环境下运行 Yii 2.0。
## 部署一个基础应用模板
由于共享托管环境往往只有一个 webroot,如果可能,请优先使用基础项目模板( basic project template )构建你的应用程序。参考 [安装 Yii 章节](http://www.yiichina.com/doc/guide/2.0/start-installation)在本地安装基础项目模板。当你让应用程序在本地正常运行后,我们将要做少量的修改以让它可以在共享托管服务器运行。
### 重命名 webroot
用FTP或者其他的工具连接到你的托管服务器,你可能看到类似如下的目录结构:
~~~
config
logs
www
~~~
在以上,`www` 是你的 web 服务器的 webroot 目录。不同的托管环境下名称可能各不相同,通常是类似: `www`, `htdocs`, 和`public_html` 之类的名称。
对于我们的基础项目模板而言,其 webroot 名为 `web` 。 在你上传你的应用程序到 web 服务器上去之前,将你的本地 webroot 重命名以匹配服务器。 即: 从 `web` 改为 `www`, `public_html` 或者其他你的托管环境的 webroot 名称。
### FTP 根目录可写
如果你有 FTP 根目录的写权限,即,有 `config`, `logs` 和 `www` 的根目录,那么,如本地根目录相同的结构上传 `assets`,`commands` 等目录。
### 增加 web 服务器的额外配置
如果你的 web 服务器是 Apache,你需要增加一个包含如下内容的 `.htaccess` 文件到你的 `web` 目录(或者 `public_html` 根据实际情况而定,是你的 `index.php` 文件所在的目录)。
~~~
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
~~~
对于nginx而言,你不需要额外的配置文件。
### 检查环境要求
为了运行 Yii ,你的 web 服务器必须匹配它的环境要求。最低的要求必须是 PHP 5.4。为了检查环境配置,将 `requirements.php` 从你的根目录拷贝到 webroot 目录,并通过浏览器输入 URL `http://example.com/requirements.php` 运行它。最后,检查结束后别忘了删除这个文件哦!
## 部署一个高级应用程序模板
部署一个高级应用程序到共享的托管环境比基础应用要麻烦的原因在于它包含有两个 webroot 目录,而共享的托管环境不支持两个。对于这种情况,我们需要调整目录结构。
### 将入口文件移动到同一个 webroot
首先我们需要一个 webroot 目录,如[重命名 webroot](http://www.yiichina.com/doc/guide/2.0/tutorial-shared-hosting#renaming-webroot)一节所述,创建一个新的跟你的托管环境 webroot 同名的目录,如类似 `www` 或者`public_html` 的名字。创建如下的目录结构,其中 `www` 目录指代你刚刚创建的 webroot 目录。
~~~
www
admin
backend
common
console
environments
frontend
...
~~~
`www` 目录是我们的前台目录,所以将 `frontend/web` 里面的内容移到这个目录。 将 `backend/web` 里面的内容移到 `www/admin` 目录。对于每种情况下,你需要调整`index.php` 和 `index-test.php` 里面引用的目录结构。
### 分离 Session 和 Cookie
通常情况下,backend 和 frontend 运行在不同的域下,当我们将其都移到同一个域时, frontend 和 backend 将会共享相同的 cookie,这样会造成冲突。为了修复这个问题,如下调整 backend 的应用程序配置文件 `backend/config/main.php`:
~~~
'components' => [
'request' => [
'csrfParam' => '_backendCSRF',
'csrfCookie' => [
'httpOnly' => true,
'path' => '/admin',
],
],
'user' => [
'identityCookie' => [
'name' => '_backendIdentity',
'path' => '/admin',
'httpOnly' => true,
],
],
'session' => [
'name' => 'BACKENDSESSID',
'cookieParams' => [
'path' => '/admin',
],
],
],
~~~
性能优化(Performance Tuning)
最后更新于:2022-04-01 04:02:05
# 性能优化
有许多因素影响你的 Web 应用程序的性能。有些是环境,有些是你的代码,而其他一些与 Yii 本身有关。 在本节中,我们将列举这些因素并解释如何通过调整这些因素来提高应用程序的性能。
## 优化你的 PHP 环境
一个好的 PHP 环境是非常重要的。为了得到最大的性能,
* 使用最新稳定版本的 PHP 。 PHP 的主要版本可能带来显著的性能提升。
* 启用字节码缓存 [Opcache](http://php.net/opcache)(PHP 5.5或更高版本)或 [APC](http://ru2.php.net/apc) (PHP 5.4或更早版本)。字节码缓存省去了每次解析和加载 PHP 脚本所带来的开销。
## 禁用调试模式
对于运行在生产环境中的应用程序,你应该禁用调试模式。 Yii 中使用名为 `YII_DEBUG` 的常量来定义调试模式是否应被激活。 若启用了调试模式,Yii 将需要额外的时间来产生和记录调试信息。
你可以将下面的代码行放在 [入口脚本](http://www.yiichina.com/doc/guide/2.0/structure-entry-scripts) 的开头来禁用调试模式:
~~~
defined('YII_DEBUG') or define('YII_DEBUG', false);
~~~
> 提示: `YII_DEBUG` 的默认值是 false 。所以如果你确信你不在你应用程序代码中别的地方更改其默认值, 你可以简单地删除上述行来禁用调试模式。
## 使用缓存技术
你可以使用各种缓存技术来提高应用程序的性能。例如,如果你的应用程序允许用户以 Markdown 格式输入文字, 你可以考虑缓存解析后的 Markdown 内容,避免每个请求都重复解析相同的 Markdown 文本。 请参阅 [缓存](http://www.yiichina.com/doc/guide/2.0/caching-overview) 一节,了解 Yii 提供的缓存支持。
## 开启 Schema 缓存
Schema 缓存是一个特殊的缓存功能,每当你使用[活动记录](http://www.yiichina.com/doc/guide/2.0/db-active-record)时应该要开启这个缓存功能。如你所知, 活动记录能智能检测数据库对象的集合(例如列名、列类型、约束)而不需要手动地描述它们。活动记录是通过执行额外的SQL查询来获得该信息。 通过启用 Schema 缓存,检索到的数据库对象的集合将被保存在缓存中并在将来的请求中重用。
要开启 Schema 缓存,需要配置一个 `cache` [应用组件](http://www.yiichina.com/doc/guide/2.0/structure-application-components)来储存 Schema 信息, 并在 [配置](http://www.yiichina.com/doc/guide/2.0/concept-configurations) 中设置 yii\db\Connection::enableSchemaCache 为 `true` :
~~~
return [
// ...
'components' => [
// ...
'cache' => [
'class' => 'yii\caching\FileCache',
],
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydatabase',
'username' => 'root',
'password' => '',
'enableSchemaCache' => true,
// Duration of schema cache.
'schemaCacheDuration' => 3600,
// Name of the cache component used to store schema information
'schemaCache' => 'cache',
],
],
];
~~~
## 合并和压缩资源文件
一个复杂的网页往往包括许多 CSS 和 JavaScript 资源文件。为减少 HTTP 请求的数量和这些资源总下载的大小,应考虑将它们合并成一个单一的文件并压缩。 这可大大提高页面加载时间,且减少了服务器负载。想了解更多细节,请参阅[前端资源](http://www.yiichina.com/doc/guide/2.0/structure-assets)部分。
## 优化会话存储
默认会话数据被存储在文件中。这是好的对处于发展项目或小型项目。但是,当涉及要处理大量并发请求时,最好使用其他的会话存储方式,比如数据库。 Yii 支持各种会话存储。你可以通过在[配置](http://www.yiichina.com/doc/guide/2.0/concept-configurations)中配置 `session` 组件来使用这些存储,如下代码:
~~~
return [
// ...
'components' => [
'session' => [
'class' => 'yii\web\DbSession',
// Set the following if you want to use DB component other than
// default 'db'.
// 'db' => 'mydb',
// To override default session table, set the following
// 'sessionTable' => 'my_session',
],
],
];
~~~
以上配置是使用数据库来存储会话数据。默认情况下,它会使用 `db` 应用组件连接数据库并将会话数据存储在 `session` 表。 因此,你必须创建如下的 `session` 表,
~~~
CREATE TABLE session (
id CHAR(40) NOT NULL PRIMARY KEY,
expire INTEGER,
data BLOB
)
~~~
你也可以通过使用缓存来存储会话数据 yii\web\CacheSession 。理论上讲,你可以使用只要支持[数据缓存](http://www.yiichina.com/doc/guide/2.0/caching-data#supported-cache-storage)。 但是请注意,某些缓存的存储当达到存储限制会清除缓存数据。出于这个原因,你应主要在不存在存储限制时才使用这些缓存存储。 如果你的服务器支持[Redis](http://redis.io/),强烈建议你通过使用 yii\redis\Session 来作为会话存储。
## 优化数据库
执行数据库查询并从数据库中取出数据往往是一个 Web 应用程序主要的性能瓶颈。 尽管使用[数据缓存](http://www.yiichina.com/doc/guide/2.0/caching-data)技术可以缓解性能下降,但它并不完全解决这个问题。 当数据库包含大量的数据且缓存数据是无效的,获取最新的数据可能是最耗性能的假如在没有适当地设计数据库和查询条件。
一般来说,提高数据库查询的性能是创建索引。例如,如果你需要找一个用户表的“用户名”,你应该为“用户名”创建一个索引。 注意,尽管索引可以使选择查询的速度快得多,但它会减慢插入、更新和删除的查询。
对于复杂的数据库查询,建议你创建数据库视图来保存查询分析和准备的时间。
最后,在“SELECT”中使用“LIMIT”查询。这可以避免从数据库中取出大量数据。
## 使用普通数组
尽管[活动记录](http://www.yiichina.com/doc/guide/2.0/db-active-record)对象使用起来非常方便,但当你需要从数据库中检索大量数据时它的效率不如使用普通的数组。 在这种情况下,你可以考虑在使用活动记录查询数据时调用 `asArray()` ,使检索到的数据被表示为数组而不是笨重的活动记录对象。例如,
~~~
class PostController extends Controller
{
public function actionIndex()
{
$posts = Post::find()->limit(100)->asArray()->all();
return $this->render('index', ['posts' => $posts]);
}
}
~~~
在上述代码中,$posts 将被表中的行填充形成数组。每一行是一个普通的数组。要访问 第 i 行的 `title` 列,你可以使用表达式`$post[$i]['title']` 。
你也可以使用[DAO](http://www.yiichina.com/doc/guide/2.0/db-dao)以数组的方式来构建查询和检索数据。
## 优化 Composer 自动加载
因为 Composer 自动加载用于加载大多数第三方类文件,应考虑对其进行优化,通过执行以下命令:
~~~
composer dumpautoload -o
~~~
## 处理离线数据
当一个请求涉及到一些资源密集操作,你应该想办法在无需用户等待他们完成脱机模式时来执行这些操作。
有两种方法可以离线数据处理:推和拉。
在拉中,只要有请求涉及到一些复杂的操作,你创建一个任务,并将其保存在永久存储,例如数据库。然后,使用一个单独的进程(如 cron 作业)拉任务,并进行处理。 这种方法很容易实现,但它也有一些缺点。例如,该任务过程中需要定期地从任务存储拉。如果拉频率太低,这些任务可以延迟处理; 但是如果频率过高,将引起的高开销。
在推中,你可以使用消息队列(如 RabbitMQ ,ActiveMQ , Amazon SQS 等)来管理任务。 每当一个新的任务放在队列中,它会启动或者通知任务处理过程去触发任务处理。
## 性能分析
你应该配置你的代码来找出性能缺陷,并相应地采取适当措施。 以下分析工具可能是有用的:
* [Yii debug toolbar and debugger](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)
* [XDebug profiler](http://xdebug.org/docs/profiler)
* [XHProf](http://www.php.net/manual/en/book.xhprof.php)
收发邮件(Mailing)
最后更新于:2022-04-01 04:02:02
# 收发邮件
> 注意:本节正在开发中。
Yii 支持组成和发送电子邮件。然而,该框架提供的只有内容组成功能和基本接口。实际的邮件发送机制可以通过扩展提供, 因为不同的项目可能需要不同的实现方式,它通常取决于外部服务和库。
大多数情况下你可以使用 [yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer) 官方扩展。
## 配置
邮件组件配置取决于你所使用的扩展。一般来说你的应用程序配置应如下:
~~~
return [
//....
'components' => [
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
],
],
];
~~~
## 基本用法
一旦 “mailer” 组件被配置,可以使用下面的代码来发送邮件:
~~~
Yii::$app->mailer->compose()
->setFrom('from@domain.com')
->setTo('to@domain.com')
->setSubject('Message subject')
->setTextBody('Plain text content')
->setHtmlBody('<b>HTML content</b>')
->send();
~~~
在上面的例子中所述的 `compose()` 方法创建了电子邮件消息,这是填充和发送的一个实例。 如果需要的话在这个过程中你可以用上更复杂的逻辑:
~~~
$message = Yii::$app->mailer->compose();
if (Yii::$app->user->isGuest) {
$message->setFrom('from@domain.com')
} else {
$message->setFrom(Yii::$app->user->identity->email)
}
$message->setTo(Yii::$app->params['adminEmail'])
->setSubject('Message subject')
->setTextBody('Plain text content')
->send();
~~~
> 注意:每个 “mailer” 的扩展也有两个主要类别:“Mailer” 和 “Message”。 “Mailer” 总是知道类名和具体的 “Message”。 不要试图直接实例 “Message” 对象 - 而是始终使用 `compose()` 方法。
你也可以一次发送几封邮件:
~~~
$messages = [];
foreach ($users as $user) {
$messages[] = Yii::$app->mailer->compose()
// ...
->setTo($user->email);
}
Yii::$app->mailer->sendMultiple($messages);
~~~
一些特定的扩展可能会受益于这种方法,使用单一的网络消息等。
## 撰写邮件内容
Yii 允许通过特殊的视图文件来撰写实际的邮件内容。默认情况下,这些文件应该位于 “@app/mail” 路径。
一个邮件视图内容的例子:
~~~
<?php
use yii\helpers\Html;
use yii\helpers\Url;
/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\BaseMessage instance of newly created mail message */
?>
<h2>This message allows you to visit our site home page by one click</h2>
<?= Html::a('Go to home page', Url::home('http')) ?>
~~~
为了通过视图文件撰写正文可传递视图名称到 `compose()` 方法中:
~~~
Yii::$app->mailer->compose('home-link') // 渲染一个视图作为邮件内容
->setFrom('from@domain.com')
->setTo('to@domain.com')
->setSubject('Message subject')
->send();
~~~
你也可以在 `compose()` 方法中传递一些视图所需参数,这些参数可以在视图文件中使用:
~~~
Yii::$app->mailer->compose('greetings', [
'user' => Yii::$app->user->identity,
'advertisement' => $adContent,
]);
~~~
你可以指定不同的视图文件的 HTML 和纯文本邮件内容:
~~~
Yii::$app->mailer->compose([
'html' => 'contact-html',
'text' => 'contact-text',
]);
~~~
如果指定视图名称为纯字符串,它的渲染结果将被用来作为 HTML Body,同时纯文本正文将被删除所有 HTML 实体。
视图渲染结果可以被包裹进布局,可使用 yii\mail\BaseMailer::htmlLayout 和 yii\mail\BaseMailer::textLayout 来设置。 它的运行方式跟常规应用程序的布局是一样的。布局可用于设置邮件 CSS 样式或其他共享内容:
~~~
<?php
use yii\helpers\Html;
/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\MessageInterface the message being composed */
/* @var $content string main view render result */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" />
<style type="text/css">
.heading {...}
.list {...}
.footer {...}
</style>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
<?= $content ?>
<div class="footer">With kind regards, <?= Yii::$app->name ?> team</div>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
~~~
## 文件附件
你可以使用 `attach()` 和 `attachContent()` 方法来添加附件的信息:
~~~
$message = Yii::$app->mailer->compose();
// 附件来自本地文件
$message->attach('/path/to/source/file.pdf');
// 动态创建一个文件附件
$message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);
~~~
## 嵌入图片
你可以使用 `embed()` 方法将图片插入到邮件内容。此方法返回会图片 ID ,这将用在 "img" 标签中。 当通过视图文件来写信时,这种方法易于使用:
~~~
Yii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])
// ...
->send();
~~~
然后在该视图文件中,你可以使用下面的代码:
~~~
<img src="/docs/guide/2.0/<?= $message->embed($imageFileName); ?>">
~~~
## 测试和调试
开发人员常常要检查一下,有什么电子邮件是由应用程序发送的,他们的内容是什么等。这可通过`yii\mail\BaseMailer::useFileTransport` 来检查。 如果开启这个选项,会把邮件信息保存在本地文件而不是发送它们。这些文件保存在 `yii\mail\BaseMailer::fileTransportPath` 中,默认在 '@runtime/mail' 。
> 提示: 你可以保存这些信息到本地文件或者把它们发送出去,但不能同时两者都做。
邮件信息文件可以在一个普通的文本编辑器中打开,这样你就可以浏览实际的邮件标题,内容等。这种机制可以用来调试应用程序或运行单元测试。
> 提示: 该邮件信息文件是会被 `\yii\mail\MessageInterface::toString()` 转成字符串保存的,它依赖于实际在应用程序中使用的邮件扩展。
## 创建自己的邮件解决方案
为了创建你自己的邮件解决方案,你需要创建两个类,一个用于 “Mailer”,另一个用于 “Message”。 你可以使用`yii\mail\BaseMailer` 和 `yii\mail\BaseMessage` 作为基类。这些类已经实现了基本的逻辑,这在本指南中有介绍。 然而,它们的使用不是必须的,它实现了 `yii\mail\MailerInterface` 和 `yii\mail\MessageInterface` 接口。 然后,你需要实现所有 abstract 方法来构建解决方案。
国际化(Internationalization)
最后更新于:2022-04-01 04:02:00
# 国际化
国际化(I18N)是指在设计软件时,使它可以无需做大的改变就能够适应不同的语言和地区的需要。对于 Web 应用程序, 这有着特别重要的意义,因为潜在的用户可能会在全球范围内。 Yii 提供的国际化功能支持全方位信息翻译,视图翻译,日期和数字格式化。
## 区域和语言
区域设置是一组参数以定义用户希望能在他们的用户界面所看到用户的语言,国家和任何特殊的偏好。 它通常是由语言 ID 和区域 ID 组成。例如,ID “en-US” 代表英语和美国的语言环境。为了保持一致性, 在 Yii 应用程序中使用的所有区域 ID 应该规范化为 `ll-CC`,其中 `ll` 是根据两个或三个字母的小写字母语言代码 [ISO-639](http://www.loc.gov/standards/iso639-2/) 和 `CC` 是两个字母的国别代码 [ISO-3166](http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html)。 有关区域设置的更多细节可以看 [ICU 项目文档](http://userguide.icu-project.org/locale#TOC-The-Locale-Concept)。
在 Yii中,我们经常用 “language” 来代表一个区域。
一个 Yii 应用使用两种语言:yii\base\Application::$sourceLanguage 和 yii\base\Application::$language 。前者指的是写在代码中的语言,后者是向最终用户显示内容的语言。 而信息翻译服务主要是将文本消息从原语言翻译到目标语言。
可以用类似下面的应用程序配置来配置应用程序语言:
~~~
return [
// 设置目标语言为俄语
'language' => 'ru-RU',
// 设置源语言为英语
'sourceLanguage' => 'en-US',
......
];
~~~
默认的 yii\base\Application::$sourceLanguage 值是 `en-US`,即美国英语。 建议你保留此默认值不变,因为通常让人将英语翻译成其它语言要比将其它语言翻译成其它语言容易得多。
你经常需要根据不同的因素来动态地设置 yii\base\Application::$language ,如最终用户的语言首选项。 要在应用程序配置中配置它,你可以使用下面的语句来更改目标语言:
~~~
// 改变目标语言为中文
\Yii::$app->language = 'zh-CN';
~~~
## 消息翻译
消息翻译服务用于将一条文本信息从一种语言(通常是 yii\base\Application::$sourceLanguage ) 翻译成另一种语言(通常是 yii\base\Application::$language)。 它的翻译原理是通过在语言文件中查找要翻译的信息以及翻译的结果。如果要翻译的信息可以在语言文件中找到,会返回相应的翻译结果; 否则会返回原始未翻译的信息。
为了使用消息翻译服务,需要做如下工作:
* 调用 Yii::t() 方法且在其中包含每一条要翻译的消息;
* 配置一个或多个消息来源,能在其中找得到要翻译的消息和翻译结果;
* 让译者翻译信息并将它们存储在消息来源。
这个 Yii::t() 方法的用法如下,
~~~
echo \Yii::t('app', 'This is a string to translate!');
~~~
第一个参数指储存消息来源的类别名称,第二个参数指需要被翻译的消息。
这个 Yii::t() 方法会调用 `i18n` [应用组件](http://www.yiichina.com/doc/guide/2.0/structure-application-components) 来实现翻译工作。这个组件可以在应用程序中按下面的代码来配置,
~~~
'components' => [
// ...
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
//'basePath' => '@app/messages',
//'sourceLanguage' => 'en-US',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
],
],
],
],
~~~
在上面的代码中,配置了由 yii\i18n\PhpMessageSource 所支持的消息来源。模式 `app*` 表示所有以 `app` 开头的消息类别名称都使用这个翻译的消息来源。该 yii\i18n\PhpMessageSource 类使用 PHP 文件来存储消息翻译。 每 PHP 文件对应单一类别的消息。默认情况下,文件名应该与类别名称相同。但是,你可以配置 yii\i18n\PhpMessageSource::fileMap 来映射一个类别到不同名称的 PHP 文件。在上面的例子中, 类别 `app/error` 被映射到PHP文件 `@app/messages/ru-RU/error.php`(假设 `ru-RU` 为目标语言)。如果没有此配置, 该类别将被映射到 `@app/messages/ru-RU/app/error.php` 。
除了在PHP文件中存储消息来源,也可以使用下面的消息来源在不同的存储来存储翻译的消息:
* yii\i18n\GettextMessageSource 使用 GNU Gettext 的 MO 或 PO 文件保存翻译的消息。
* yii\i18n\DbMessageSource 使用一个数据库表来存储翻译的消息。
## 消息格式化
在要翻译的消息里,你可以嵌入一些占位符,并让它们通过动态的参数值来代替。你甚至可以根据目标语言格式的参数值来使用特殊的占位符。 在本节中,我们将介绍如何用不同的方式来格式化消息。
### 消息参数
在待翻译的消息,可以嵌入一个或多个占位符,以便它们可以由给定的参数值取代。通过给不同的参数值,可以动态地改变翻译内容的消息。 在下面的例子中,占位符 `{username}` 在 `“Hello, {username}!”` 中将分别被 `'Alexander'`和`'Qiang'` 所替换。
~~~
$username = 'Alexander';
// 输出:“Hello, Alexander”
echo \Yii::t('app', 'Hello, {username}!', [
'username' => $username,
]);
$username = 'Qiang';
// 输出:“Hello, Qiang”
echo \Yii::t('app', 'Hello, {username}!', [
'username' => $username,
]);
~~~
当翻译的消息包含占位符时,应该让占位符保留原样。这是因为调用 `Yii::t()` 时,占位符将被实际参数值代替。
你可以使用 *名称占位符* 或者 *位置占位符*,但不能两者都用在同一个消息里。
前面的例子说明了如何使用名称占位符。即每个占位符的格式为 `{参数名称}` ,你所提供的参数作为关联数组, 其中数组的键是参数名称(没有大括号),数组的值是对应的参数值。
位置占位符是使用基于零的整数序列,在调用 `Yii::t()` 时会参数值根据它们出现位置的顺序分别进行替换。 在下面的例子中,位置占位符 `{0}`,`{1}` 和 `{2}` 将分别被 `$price`,`$count` 和 `$subtotal` 所替换。
~~~
$price = 100;
$count = 2;
$subtotal = 200;
echo \Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', $price, $count, $subtotal);
~~~
> 提示:大多数情况下你应该使用名称占位符。这是因为参数名称可以让翻译者更好的理解要被翻译的消息。
### 格式化参数
你可以在消息的占位符指定附加格式的规则,这样的参数值可在替换占位符之前格式化它们。在下面的例子中, 价格参数值将视为一个数并格式化为货币值:
~~~
$price = 100;
echo \Yii::t('app', 'Price: {0, number, currency}', $price);
~~~
> 注意:参数的格式化需要安装 [intl PHP 扩展](http://www.php.net/manual/en/intro.intl.php)。
可以使用缩写的形式或完整的形式来格式化占位符:
~~~
short form: {PlaceholderName, ParameterType}
full form: {PlaceholderName, ParameterType, ParameterStyle}
~~~
请参阅 [ICU 文档](http://icu-project.org/apiref/icu4c/classMessageFormat.html) 关于如何指定这样的占位符的说明。
接下来我们会展示一些常用的使用方法。
#### 数字
参数值应该被格式化为一个数。例如,
~~~
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number}', $sum);
~~~
你可以指定参数的格式为 `integer`(整型),`currency` (货币),或者 `percent` (百分数):
~~~
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number, currency}', $sum);
~~~
你也可以指定一个自定义模式来格式化数字。 例如,
~~~
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number, ,000,000000}', $sum);
~~~
[格式化参考](http://icu-project.org/apiref/icu4c/classicu_1_1DecimalFormat.html)。
#### 日期
该参数值应该被格式化为一个日期。 例如,
~~~
echo \Yii::t('app', 'Today is {0, date}', time());
~~~
你可以指定一个可选的参数格式 `short` ,`medium` ,`long` ,或 `full` :
~~~
echo \Yii::t('app', 'Today is {0, date, short}', time());
~~~
你还可以指定一个自定义模式来格式化日期:
~~~
echo \Yii::t('app', 'Today is {0, date, yyyy-MM-dd}', time());
~~~
[格式化参考](http://icu-project.org/apiref/icu4c/classicu_1_1SimpleDateFormat.html)。
#### 时间
参数值应该被格式化为一个时间。 例如,
~~~
echo \Yii::t('app', 'It is {0, time}', time());
~~~
你可以指定一个可选的参数格式 `short` ,`medium` ,`long` ,或 `full` :
~~~
echo \Yii::t('app', 'It is {0, time, short}', time());
~~~
你还可以指定一个自定义模式来格式化时间:
~~~
echo \Yii::t('app', 'It is {0, date, HH:mm}', time());
~~~
[格式化参考](http://icu-project.org/apiref/icu4c/classicu_1_1SimpleDateFormat.html)。
#### 拼写
参数值为一个数并被格式化为它的字母拼写形式。 例如,
~~~
// 输出:"42 is spelled as forty-two"
echo \Yii::t('app', '{n,number} is spelled as {n, spellout}', ['n' => 42]);
~~~
#### 序数词
参数值为一个数并被格式化为一个序数词。 例如,
~~~
// 输出:"You are the 42nd visitor here!"
echo \Yii::t('app', 'You are the {n, ordinal} visitor here!', ['n' => 42]);
~~~
#### 持续时间
参数值为秒数并被格式化为持续的时间段。 例如,
~~~
// 输出:"You are here for 47 sec. already!"
echo \Yii::t('app', 'You are here for {n, duration} already!', ['n' => 47]);
~~~
#### 复数
不同的语言有不同的方式来表示复数。 Yii 提供一个便捷的途径,即使是非常复杂的规则也使翻译消息时不同的复数形式行之有效。 取之以直接处理词形变化规则,它是足以面对某些词形变化语言的翻译。 例如,
~~~
// 当 $n = 0 时,输出:"There are no cats!"
// 当 $n = 1 时,输出:"There is one cat!"
// 当 $n = 42 时,输出:"There are 42 cats!"
echo \Yii::t('app', 'There {n, plural, =0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);
~~~
在上面的多个规则的参数中, `=0` 意味着 `n` 的值是 0 ,`=1` 意味着 `n` 的值是 1 , 而 `other` 则是对于其它值, `#` 会被 `n` 中的值给替代。
复数形式可以是某些非常复杂的语言。下面以俄罗斯为例,`=1` 完全匹配 `n = 1`,而 `one` 匹配 `21` 或 `101`:
~~~
Здесь {n, plural, =0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!
~~~
注意,上述信息主要是作为一个翻译的信息,而不是一个原始消息,除非设置应用程序的 yii\base\Application::$sourceLanguage 为`ru-RU`。
如果没有找到一个翻译的原始消息,复数规则 yii\base\Application::$sourceLanguage 将被应用到原始消息。
要了解词形变化形式,你应该指定一个特定的语言,请参考 [rules reference at unicode.org](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)。
#### 选择
可以使用 `select` 参数类型来选择基于参数值的短语。例如,
~~~
// 输出:"Snoopy is a dog and it loves Yii!"
echo \Yii::t('app', '{name} is a {gender} and {gender, select, female{she} male{he} other{it}} loves Yii!', [
'name' => 'Snoopy',
'gender' => 'dog',
]);
~~~
在上面的表达中, `female` 和 `male` 是可能的参数值,而 `other` 用于处理不与它们中任何一个相匹配的值。对于每一个可能的参数值, 应指定一个短语并把它放在在一对大括号中。
### 指定默认翻译
你可以指定使用默认的翻译,该翻译将作为一个类别,用于不匹配任何其他翻译的后备。这种翻译应标有 `*` 。 为了做到这一点以下内容需要添加到应用程序的配置:
~~~
//配置 i18n 组件
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource'
],
],
],
~~~
现在,你可以使用每一个还没有配置的类别,这跟 Yii 1.1 的行为有点类似。该类别的消息将来自在默认翻译 `basePath` 中的一个文件, 该文件在 `@app/messages` :
~~~
echo Yii::t('not_specified_category', 'message from unspecified category');
~~~
该消息将来自 `@app/messages/<LanguageCode>/not_specified_category.php` 。
### 翻译模块消息
如果你想翻译一个模块的消息,并避免使用单一翻译文件的所有信息,你可以按照下面的方式来翻译:
~~~
<?php
namespace app\modules\users;
use Yii;
class Module extends \yii\base\Module
{
public $controllerNamespace = 'app\modules\users\controllers';
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
Yii::$app->i18n->translations['modules/users/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/modules/users/messages',
'fileMap' => [
'modules/users/validation' => 'validation.php',
'modules/users/form' => 'form.php',
...
],
];
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('modules/users/' . $category, $message, $params, $language);
}
}
~~~
在上面的例子中,我们使用通配符匹配,然后过滤了所需的文件中的每个类别。取之使用 `fileMap` ,你可以简单地使用类映射的同名文件。 现在你可以直接使用 `Module::t('validation', 'your custom validation message')` 或 `Module::t('form', 'some form label')`。
### 翻译小部件消息
上述模块的翻译规则也同样适用于小部件的翻译规则,例如:
~~~
<?php
namespace app\widgets\menu;
use yii\base\Widget;
use Yii;
class Menu extends Widget
{
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
$i18n = Yii::$app->i18n;
$i18n->translations['widgets/menu/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/widgets/menu/messages',
'fileMap' => [
'widgets/menu/messages' => 'messages.php',
],
];
}
public function run()
{
echo $this->render('index');
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('widgets/menu/' . $category, $message, $params, $language);
}
}
~~~
你可以简单地使用类映射的同名文件而不是使用 `fileMap` 。现在你直接可以使用 `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` 。
> **提示**: 对于小部件也可以使用 i18n 视图,并一样以控制器的规则来应用它们。
### 翻译框架信息
Yii 自带了一些默认的信息验证错误和其他一些字符串的翻译。这些信息都是在 `yii` 类别中。有时候你想纠正应用程序的默认信息翻译。 为了做到这一点,需配置 `i18n` [应用组件](http://www.yiichina.com/doc/guide/2.0/structure-application-components) 如下:
~~~
'i18n' => [
'translations' => [
'yii' => [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/messages'
],
],
],
~~~
现在可以把你修改过的翻译放在 `@app/messages/<language>/yii.php`。
### 处理缺少的翻译
如果翻译的消息在消息源文件里找不到,Yii 将直接显示该消息内容。这样一来当你的原始消息是一个有效的冗长的文字时会很方便。 然而,有时它是不能实现我们的需求。你可能需要执行一些自定义处理的情况,这时请求的翻译可能在消息翻译源文件找不到。 这可通过使用 yii\i18n\MessageSource::EVENT_MISSING_TRANSLATION - yii\i18n\MessageSource 的事件来完成。
例如,你可能想要将所有缺失的翻译做一个明显的标记,这样它们就可以很容易地在页面中找到。 为此,你需要先设置一个事件处理程序。这可以在应用程序配置中进行:
~~~
'components' => [
// ...
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
'on missingTranslation' => ['app\components\TranslationEventHandler', 'handleMissingTranslation']
],
],
],
],
~~~
现在,你需要实现自己的事件处理程序:
~~~
<?php
namespace app\components;
use yii\i18n\MissingTranslationEvent;
class TranslationEventHandler
{
public static function handleMissingTranslation(MissingTranslationEvent $event)
{
$event->translatedMessage = "@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @";
}
}
~~~
如果 yii\i18n\MissingTranslationEvent::translatedMessage 是由事件处理程序设置,它将显示翻译结果。
> 注意:每个消息源会单独处理它缺少的翻译。如果是使用多个消息源,并希望他们把缺少的翻译以同样的方式来处理, 你应该给它们每一个消息源指定相应的事件处理程序。
### 使用 `message` 命令
翻译储存在 yii\i18n\PhpMessageSource,[[yii\i18n\GettextMessageSource|.po 文件] 或者 yii\i18n\DbMessageSource。 具体见类的附加选项。
首先,你需要创建一个配置文件。确定应该保存在哪里,然后执行命令
~~~
./yii message/config path/to/config.php
~~~
打开创建的文件,并按照需求来调整参数。特别注意:
* `languages`: 代表你的应用程序应该被翻译成什么语言的一个数组;
* `messagePath`: 存储消息文件的路径,这应与配置中 `i18n` 的 `basePath` 参数一致。
> 注意,这里不支持路径别名,它们必须是配置文件相对路径的位置
一旦你做好了配置文件,你就可以使用命令提取消息
~~~
./yii message path/to/config.php
~~~
然后你会发现你的文件(如果你已经选择基于文件的翻译)在 `messagePath` 目录。
## 视图的翻译
有时你可能想要翻译一个完整的视图文件,而不是翻译单条文本消息。为了达到这一目的,只需简单的翻译视图并在它子目录下保存一个名称一样的目标语言文件。 例如,如果你想要翻译的视图文件为 `views/site/index.php` 且目标语言是 `ru-RU`,你可以将视图翻译并保存为 `views/site/ru-RU/index.php`。现在 每当你调用 yii\base\View::renderFile() 或任何其它方法 (如 yii\base\Controller::render()) 来渲染 `views/site/index.php` 视图, 它最终会使用所翻译的 `views/site/ru-RU/index.php`。
> 注意:如果 yii\base\Application::$language 跟 yii\base\Application::$sourceLanguage 相同, 在翻译视图的存在下,将呈现原始视图。
## 格式化日期和数字值
在 [格式化输出数据](http://www.yiichina.com/doc/guide/2.0/output-formatting) 一节可获取详细信息。
## 设置 PHP 环境
Yii 使用 [PHP intl 扩展](http://php.net/manual/en/book.intl.php) 来提供大多数 I18N 的功能, 如日期和数字格式的 yii\i18n\Formatter 类和消息格式的 yii\i18n\MessageFormatter 类。 当 `intl` 扩展没有安装时,两者会提供一个回调机制。然而,该回调机制只适用于目标语言是英语的情况下。 因此,当 I18N 对你来说必不可少时,强烈建议你安装 `intl`。
[PHP intl 扩展](http://php.net/manual/en/book.intl.php) 是基于对于所有不同的语言环境提供格式化规则的 [ICU库](http://site.icu-project.org/)。 不同版本的 ICU 中可能会产生不同日期和数值格式的结果。为了确保你的网站在所有环境产生相同的结果,建议你安装与 `intl` 扩展相同的版本(和 ICU 同一版本)。
要找出所使用的 PHP 是哪个版本的 ICU ,你可以运行下面的脚本,它会给出你所使用的 PHP 和 ICU 的版本。
~~~
<?php
echo "PHP: " . PHP_VERSION . "\n";
echo "ICU: " . INTL_ICU_VERSION . "\n";
~~~
此外,还建议你所使用的 ICU 版本应等于或大于 49 的版本。这确保了可以使用本文档描述的所有功能。例如, 低于 49 版本的 ICU 不支持使用 `#` 占位符来实现复数规则。请参阅 [http://site.icu-project.org/download](http://site.icu-project.org/download) 获取可用 ICU 版本的完整列表。 注意,版本编号在 4.8 之后发生了变化(如 ICU4.8,ICU49,50 ICU 等)。
另外,ICU 库中时区数据库的信息可能过时。要更新时区数据库时详情请参阅 [ICU 手册](http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data) 。而对于 ICU 输出格式使用的时区数据库, PHP 用的时区数据库可能跟它有关。你可以通过安装 [pecl package `timezonedb`](http://pecl.php.net/package/timezonedb) 的最新版本来更新它。
核心验证器(Core Validators)
最后更新于:2022-04-01 04:01:58
# 核心验证器(Core Validators)
Yii 提供一系列常用的核心验证器,主要存在于 `yii\validators` 命名空间之下。为了避免使用冗长的类名,你可以直接用**昵称**来指定相应的核心验证器。比如你可以用 `required` 昵称代指 yii\validators\RequiredValidator 类:
~~~
public function rules()
{
return [
[['email', 'password'], 'required'],
];
}
~~~
yii\validators\Validator::builtInValidators 属性声明了所有被支持的验证器昵称。
下面,我们将详细介绍每一款验证器的主要用法和属性。
yii\validators\BooleanValidator
~~~
[
// 检查 "selected" 是否为 0 或 1,无视数据类型
['selected', 'boolean'],
// 检查 "deleted" 是否为布尔类型,即 true 或 false
['deleted', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true],
]
~~~
该验证器检查输入值是否为一个布尔值。
* `trueValue`: 代表**真**的值。默认为 `'1'`。
* `falseValue`:代表**假**的值。默认为 `'0'`。
* `strict`:是否要求待测输入必须严格匹配 `trueValue` 或 `falseValue`。默认为 `false`。
> 注意:因为通过 HTML 表单传递的输入数据都是字符串类型,所以一般情况下你都需要保持 yii\validators\BooleanValidator::strict 属性为假。
yii\captcha\CaptchaValidator
~~~
[
['verificationCode', 'captcha'],
]
~~~
该验证器通常配合 yii\captcha\CaptchaAction 以及 yii\captcha\Captcha 使用,以确保某一输入与 yii\captcha\Captcha 小部件所显示的验证代码(verification code)相同。
* `caseSensitive`:对验证代码的比对是否要求大小写敏感。默认为 false。
* `captchaAction`:指向用于渲染 CAPTCHA 图片的 yii\captcha\CaptchaAction 的 [路由](http://www.yiichina.com/doc/guide/2.0/structure-controllers#routes)。默认为 `'site/captcha'`。
* `skipOnEmpty`:当输入为空时,是否跳过验证。默认为 false,也就是输入值为必需项。
yii\validators\CompareValidator
~~~
[
// 检查 "password" 特性的值是否与 "password_repeat" 的值相同
['password', 'compare'],
// 检查年龄是否大于等于 30
['age', 'compare', 'compareValue' => 30, 'operator' => '>='],
]
~~~
该验证器比较两个特定输入值之间的关系是否与 `operator` 属性所指定的相同。
* `compareAttribute`:用于与原特性相比较的特性名称。当该验证器被用于验证某目标特性时,该属性会默认为目标属性加后缀`_repeat`。举例来说,若目标特性为 `password`,则该属性默认为 `password_repeat`。
* `compareValue`:用于与输入值相比较的常量值。当该属性与 `compareAttribute` 属性同时被指定时,该属性优先被使用。
* `operator`:比较操作符。默认为 `==`,意味着检查输入值是否与 `compareAttribute` 或 `compareValue` 的值相等。该属性支持如下操作符:
* `==`:检查两值是否相等。比对为非严格模式。
* `===`:检查两值是否全等。比对为严格模式。
* `!=`:检查两值是否不等。比对为非严格模式。
* `!==`:检查两值是否不全等。比对为严格模式。
* `>`:检查待测目标值是否大于给定被测值。
* `>=`:检查待测目标值是否大于等于给定被测值。
* `<`:检查待测目标值是否小于给定被测值。
* `<=`:检查待测目标值是否小于等于给定被测值。
yii\validators\DateValidator
~~~
[
[['from', 'to'], 'date'],
]
~~~
该验证器检查输入值是否为适当格式的 date,time,或者 datetime。另外,它还可以帮你把输入值转换为一个 UNIX 时间戳并保存到 yii\validators\DateValidator::timestampAttribute 属性所指定的特性里。
* `format`:待测的 日期/时间 格式。请参考 [date_create_from_format() 相关的 PHP 手册](http://www.php.net/manual/zh/datetime.createfromformat.php)了解设定格式字符串的更多细节。默认值为`'Y-m-d'`。
* `timestampAttribute`:用于保存用输入时间/日期转换出来的 UNIX 时间戳的特性。
yii\validators\DefaultValueValidator
~~~
[
// 若 "age" 为空,则将其设为 null
['age', 'default', 'value' => null],
// 若 "country" 为空,则将其设为 "USA"
['country', 'default', 'value' => 'USA'],
// 若 "from" 和 "to" 为空,则分别给他们分配自今天起,3 天后和 6 天后的日期。
[['from', 'to'], 'default', 'value' => function ($model, $attribute) {
return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' :'+6 days'));
}],
]
~~~
该验证器并不进行数据验证。而是,给为空的待测特性分配默认值。
* `value`:默认值,或一个返回默认值的 PHP Callable 对象(即回调函数)。它们会分配给检测为空的待测特性。PHP 回调方法的样式如下:
~~~
function foo($model, $attribute) {
// ... 计算 $value ...
return $value;
}
~~~
> 补充:如何判断待测值是否为空,被写在另外一个话题的[处理空输入](http://www.yiichina.com/doc/guide/2.0/input-validation#handling-empty-inputs)章节。
yii\validators\NumberValidator
~~~
[
// 检查 "salary" 是否为浮点数
['salary', 'double'],
]
~~~
该验证器检查输入值是否为双精度浮点数。他等效于 [number](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#number) 验证器。
* `max`:上限值(含界点)。若不设置,则验证器不检查上限。
* `min`:下限值(含界点)。若不设置,则验证器不检查下限。
yii\validators\EmailValidator
~~~
[
// 检查 "email" 是否为有效的邮箱地址
['email', 'email'],
]
~~~
该验证器检查输入值是否为有效的邮箱地址。
* `allowName`:检查是否允许带名称的电子邮件地址 (e.g. `张三 <John.san@example.com>`)。 默认为 false。
* `checkDNS`:检查邮箱域名是否存在,且有没有对应的 A 或 MX 记录。不过要知道,有的时候该项检查可能会因为临时性 DNS 故障而失败,哪怕它其实是有效的。默认为 false。
* `enableIDN`:验证过程是否应该考虑 IDN(internationalized domain names,国际化域名,也称多语种域名,比如中文域名)。默认为 false。要注意但是为使用 IDN 验证功能,请先确保安装并开启 `intl` PHP 扩展,不然会导致抛出异常。
yii\validators\ExistValidator
~~~
[
// a1 需要在 "a1" 特性所代表的字段内存在
['a1', 'exist'],
// a1 必需存在,但检验的是 a1 的值在字段 a2 中的存在性
['a1', 'exist', 'targetAttribute' => 'a2'],
// a1 和 a2 的值都需要存在,且它们都能收到错误提示
[['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']],
// a1 和 a2 的值都需要存在,只有 a1 能接收到错误信息
['a1', 'exist', 'targetAttribute' => ['a1', 'a2']],
// 通过同时在 a2 和 a3 字段中检查 a2 和 a1 的值来确定 a1 的存在性
['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']],
// a1 必需存在,若 a1 为数组,则其每个子元素都必须存在。
['a1', 'exist', 'allowArray' => true],
]
~~~
该验证器检查输入值是否在某表字段中存在。它只对[活动记录](http://www.yiichina.com/doc/guide/2.0/db-active-record)类型的模型类特性起作用,能支持对一个或多过字段的验证。
* `targetClass`:用于查找输入值的目标 [AR](http://www.yiichina.com/doc/guide/2.0/db-active-record) 类。若不设置,则会使用正在进行验证的当前模型类。
* `targetAttribute`:用于检查输入值存在性的 `targetClass` 的模型特性。
* 若不设置,它会直接使用待测特性名(整个参数数组的首元素)。
* 除了指定为字符串以外,你也可以用数组的形式,同时指定多个用于验证的表字段,数组的键和值都是代表字段的特性名,值表示 `targetClass` 的待测数据源字段,而键表示当前模型的待测特性名。
* 若键和值相同,你可以只指定值。(如:`['a2']` 就代表 `['a2'=>'a2']`)
* `filter`:用于检查输入值存在性必然会进行数据库查询,而该属性为用于进一步筛选该查询的过滤条件。可以为代表额外查询条件的字符串或数组(关于查询条件的格式,请参考 yii\db\Query::where());或者样式为 `function ($query)` 的匿名函数,`$query`参数为你希望在该函数内进行修改的 yii\db\Query 对象。
* `allowArray`:是否允许输入值为数组。默认为 false。若该属性为 true 且输入值为数组,则数组的每个元素都必须在目标字段中存在。值得注意的是,若用吧 `targetAttribute` 设为多元素数组来验证被测值在多字段中的存在性时,该属性不能设置为 true。
> 译注:[exist](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#exist) 和 [unique](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#unique) 验证器的机理和参数都相似,有点像一体两面的阴和阳。
>
> * 他们的区别是 exist 要求 `targetAttribute` 键所代表的的属性在其值所代表字段中找得到;而 unique 正相反,要求键所代表的的属性不能在其值所代表字段中被找到。
> * 从另一个角度来理解:他们都会在验证的过程中执行数据库查询,查询的条件即为where $v=$k (假设 `targetAttribute` 的其中一对键值对为 `$k => $v`)。unique 要求查询的结果数 `$count==0`,而 exist 则要求查询的结果数 `$count>0`
> * 最后别忘了,unique 验证器不存在 `allowArray` 属性哦。
yii\validators\FileValidator
~~~
[
// 检查 "primaryImage" 是否为 PNG, JPG 或 GIF 格式的上传图片。
// 文件大小必须小于 1MB
['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 1024*1024*1024],
]
~~~
该验证器检查输入值是否为一个有效的上传文件。
* `extensions`:可接受上传的文件扩展名列表。它可以是数组,也可以是用空格或逗号分隔各个扩展名的字符串 (e.g. "gif, jpg")。 扩展名大小写不敏感。默认为 null,意味着所有扩展名都被接受。
* `mimeTypes`:可接受上传的 MIME 类型列表。它可以是数组,也可以是用空格或逗号分隔各个 MIME 的字符串 (e.g. "image/jpeg, image/png")。 Mime 类型名是大小写不敏感的。默认为 null,意味着所有 MIME 类型都被接受。
* `minSize`:上传文件所需最少多少 Byte 的大小。默认为 null,代表没有下限。
* `maxSize`:上传文件所需最多多少 Byte 的大小。默认为 null,代表没有上限。
* `maxFiles`:给定特性最多能承载多少个文件。默认为 1,代表只允许单文件上传。若值大于一,那么输入值必须为包含最多`maxFiles` 个上传文件元素的数组。
* `checkExtensionByMimeType`:是否通过文件的 MIME 类型来判断其文件扩展。若由 MIME 判定的文件扩展与给定文件的扩展不一样,则文件会被认为无效。默认为 true,代表执行上述检测。
`FileValidator` 通常与 yii\web\UploadedFile 共同使用。请参考 [文件上传](http://www.yiichina.com/doc/guide/2.0/input-file-upload)章节来了解有关文件上传与上传文件的检验的全部内容。
yii\validators\FilterValidator
~~~
[
// trim 掉 "username" 和 "email" 输入
[['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],
// 标准化 "phone" 输入
['phone', 'filter', 'filter' => function ($value) {
// 在此处标准化输入的电话号码
return $value;
}],
]
~~~
该验证器并不进行数据验证。而是,给输入值应用一个滤镜,并在检验过程之后把它赋值回特性变量。
* `filter`:用于定义滤镜的 PHP 回调函数。可以为全局函数名,匿名函数,或其他。该函数的样式必须是 `function ($value) { return $newValue; }`。该属性不能省略,必须设置。
* `skipOnArray`:是否在输入值为数组时跳过滤镜。默认为 false。请注意如果滤镜不能处理数组输入,你就应该把该属性设为 true。否则可能会导致 PHP Error 的发生。
> 技巧:如果你只是想要用 trim 处理下输入值,你可以直接用 [trim](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#trim) 验证器的。
yii\validators\ImageValidator
~~~
[
// 检查 "primaryImage" 是否为适当尺寸的有效图片
['primaryImage', 'image', 'extensions' => 'png, jpg',
'minWidth' => 100, 'maxWidth' => 1000,
'minHeight' => 100, 'maxHeight' => 1000,
],
]
~~~
该验证器检查输入值是否为代表有效的图片文件。它继承自 [file](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#file) 验证器,并因此继承有其全部属性。除此之外,它还支持以下为图片检验而设的额外属性:
* `minWidth`:图片的最小宽度。默认为 null,代表无下限。
* `maxWidth`:图片的最大宽度。默认为 null,代表无上限。
* `minHeight`:图片的最小高度。 默认为 null,代表无下限。
* `maxHeight`:图片的最大高度。默认为 null,代表无上限。
yii\validators\RangeValidator
~~~
[
// 检查 "level" 是否为 1、2 或 3 中的一个
['level', 'in', 'range' => [1, 2, 3]],
]
~~~
该验证器检查输入值是否存在于给定列表的范围之中。
* `range`:用于检查输入值的给定值列表。
* `strict`:输入值与给定值直接的比较是否为严格模式(也就是类型与值都要相同,即全等)。默认为 false。
* `not`:是否对验证的结果取反。默认为 false。当该属性被设置为 true,验证器检查输入值是否**不在**给定列表内。
* `allowArray`:是否接受输入值为数组。当该值为 true 且输入值为数组时,数组内的每一个元素都必须在给定列表内存在,否则返回验证失败。
yii\validators\NumberValidator
~~~
[
// 检查 "age" 是否为整数
['age', 'integer'],
]
~~~
该验证器检查输入值是否为整形。
* `max`:上限值(含界点)。若不设置,则验证器不检查上限。
* `min`:下限值(含界点)。若不设置,则验证器不检查下限。
yii\validators\RegularExpressionValidator
~~~
[
// 检查 "username" 是否由字母开头,且只包含单词字符
['username', 'match', 'pattern' => '/^[a-z]\w*$/i']
]
~~~
该验证器检查输入值是否匹配指定正则表达式。
* `pattern`:用于检测输入值的正则表达式。该属性是必须的,若不设置则会抛出异常。
* `not`:是否对验证的结果取反。默认为 false,代表输入值匹配正则表达式时验证成功。如果设为 true,则输入值不匹配正则时返回匹配成功。
yii\validators\NumberValidator
~~~
[
// 检查 "salary" 是否为数字
['salary', 'number'],
]
~~~
该验证器检查输入值是否为数字。他等效于 [double](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#double) 验证器。
* `max`:上限值(含界点)。若不设置,则验证器不检查上限。
* `min`:下限值(含界点)。若不设置,则验证器不检查下限。
yii\validators\RequiredValidator
~~~
[
// 检查 "username" 与 "password" 是否为空
[['username', 'password'], 'required'],
]
~~~
该验证器检查输入值是否为空,还是已经提供了。
* `requiredValue`:所期望的输入值。若没设置,意味着输入不能为空。
* `strict`:检查输入值时是否检查类型。默认为 false。当没有设置 `requiredValue` 属性时,若该属性为 true,验证器会检查输入值是否严格为 null;若该属性设为 false,该验证器会用一个更加宽松的规则检验输入值是否为空。
当设置了 `requiredValue` 属性时,若该属性为 true,输入值与 `requiredValue` 的比对会同时检查数据类型。
> 补充:如何判断待测值是否为空,被写在另外一个话题的[处理空输入](http://www.yiichina.com/doc/guide/2.0/input-validation#handling-empty-inputs)章节。
yii\validators\SafeValidator
~~~
[
// 标记 "description" 为安全特性
['description', 'safe'],
]
~~~
该验证器并不进行数据验证。而是把一个特性标记为[安全特性](http://www.yiichina.com/doc/guide/2.0/structure-models#safe-attributes)。
yii\validators\StringValidator
~~~
[
// 检查 "username" 是否为长度 4 到 24 之间的字符串
['username', 'string', 'length' => [4, 24]],
]
~~~
该验证器检查输入值是否为特定长度的字符串。并检查特性的值是否为某个特定长度。
* `length`:指定待测输入字符串的长度限制。该属性可以被指定为以下格式之一:
* 证书:the exact length that the string should be of;
* 单元素数组:代表输入字符串的最小长度 (e.g. `[8]`)。这会重写 `min` 属性。
* 包含两个元素的数组:代表输入字符串的最小和最大长度(e.g. `[8, 128]`)。 这会同时重写 `min` 和 `max` 属性。
* `min`:输入字符串的最小长度。若不设置,则代表不设下限。
* `max`:输入字符串的最大长度。若不设置,则代表不设上限。
* `encoding`:待测字符串的编码方式。若不设置,则使用应用自身的 yii\base\Application::charset 属性值,该值默认为 `UTF-8`。
yii\validators\FilterValidator
~~~
[
// trim 掉 "username" 和 "email" 两侧的多余空格
[['username', 'email'], 'trim'],
]
~~~
该验证器并不进行数据验证。而是,trim 掉输入值两侧的多余空格。注意若该输入值为数组,那它会忽略掉该验证器。
yii\validators\UniqueValidator
~~~
[
// a1 需要在 "a1" 特性所代表的字段内唯一
['a1', 'unique'],
// a1 需要唯一,但检验的是 a1 的值在字段 a2 中的唯一性
['a1', 'unique', 'targetAttribute' => 'a2'],
// a1 和 a2 的组合需要唯一,且它们都能收到错误提示
[['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']],
// a1 和 a2 的组合需要唯一,只有 a1 能接收错误提示
['a1', 'unique', 'targetAttribute' => ['a1', 'a2']],
// 通过同时在 a2 和 a3 字段中检查 a2 和 a3 的值来确定 a1 的唯一性
['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']],
]
~~~
该验证器检查输入值是否在某表字段中唯一。它只对[活动记录](http://www.yiichina.com/doc/guide/2.0/db-active-record)类型的模型类特性起作用,能支持对一个或多过字段的验证。
* `targetClass`:用于查找输入值的目标 [AR](http://www.yiichina.com/doc/guide/2.0/db-active-record) 类。若不设置,则会使用正在进行验证的当前模型类。
* `targetAttribute`:用于检查输入值唯一性的 `targetClass` 的模型特性。
* 若不设置,它会直接使用待测特性名(整个参数数组的首元素)。
* 除了指定为字符串以外,你也可以用数组的形式,同时指定多个用于验证的表字段,数组的键和值都是代表字段的特性名,值表示 `targetClass` 的待测数据源字段,而键表示当前模型的待测特性名。
* 若键和值相同,你可以只指定值。(如:`['a2']` 就代表 `['a2'=>'a2']`)
* `filter`:用于检查输入值唯一性必然会进行数据库查询,而该属性为用于进一步筛选该查询的过滤条件。可以为代表额外查询条件的字符串或数组(关于查询条件的格式,请参考 yii\db\Query::where());或者样式为 `function ($query)` 的匿名函数,`$query`参数为你希望在该函数内进行修改的 yii\db\Query 对象。
> 译注:[exist](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#exist) 和 [unique](http://www.yiichina.com/doc/guide/2.0/tutorial-core-validators#unique) 验证器的机理和参数都相似,有点像一体两面的阴和阳。
>
> * 他们的区别是 exist 要求 `targetAttribute` 键所代表的的属性在其值所代表字段中找得到;而 unique 正相反,要求键所代表的的属性不能在其值所代表字段中被找到。
> * 从另一个角度来理解:他们都会在验证的过程中执行数据库查询,查询的条件即为where $v=$k (假设 `targetAttribute` 的其中一对键值对为 `$k => $v`)。unique 要求查询的结果数 `$count==0`,而 exist 则要求查询的结果数 `$count>0`
> * 最后别忘了,unique 验证器不存在 `allowArray` 属性哦。
yii\validators\UrlValidator
~~~
[
// 检查 "website" 是否为有效的 URL。若没有 URI 方案,则给 "website" 特性加 "http://" 前缀
['website', 'url', 'defaultScheme' => 'http'],
]
~~~
该验证器检查输入值是否为有效 URL。
* `validSchemes`:用于指定那些 URI 方案会被视为有效的数组。默认为 `['http', 'https']`,代表 `http` 和 `https` URLs 会被认为有效。
* `defaultScheme`:若输入值没有对应的方案前缀,会使用的默认 URI 方案前缀。默认为 null,代表不修改输入值本身。
* `enableIDN`:验证过程是否应该考虑 IDN(internationalized domain names,国际化域名,也称多语种域名,比如中文域名)。默认为 false。要注意但是为使用 IDN 验证功能,请先确保安装并开启 `intl` PHP 扩展,不然会导致抛出异常。
控制台命令(Console Commands)
最后更新于:2022-04-01 04:01:55
# 控制台命令
除了用于构建 Web 应用程序的丰富功能,Yii 中也有一个拥有丰富功能的控制台,它们主要用于创建网站后台处理的任务。
控制台应用程序的结构非常类似于 Yii 的一个 Web 应用程序。它由一个或多个 yii\console\Controller 类组成,它们在控制台环境下通常被称为“命令”。每个控制器还可以有一个或多个动作,就像 web 控制器。
两个项目模板(基础模版和高级模版)都有自己的控制台应用程序。你可以通过运行 `yii` 脚本,在位于仓库的基本目录中运行它。 当你不带任何参数来运行它时,会给你一些可用的命令列表:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-10_56189328b5e63.png)
正如你在截图中看到,Yii 中已经定义了一组默认情况下可用的命令:
* yii\console\controllers\AssetController - 允许合并和压缩你的 JavaScript 和 CSS 文件。在 [资源 - 使用 asset 命令](http://www.yiichina.com/doc/guide/2.0/structure-assets#using-the-asset-command) 一节可获取更多信息。
* yii\console\controllers\CacheController - 清除应用程序缓存。
* yii\console\controllers\FixtureController - 管理用于单元测试 fixture 的加载和卸载。 这个命令的更多细节在 [Testing Section about Fixtures](http://www.yiichina.com/doc/guide/2.0/test-fixtures#managing-fixtures).
* yii\console\controllers\HelpController - 提供有关控制台命令的帮助信息,这是默认的命令并会打印上面截图所示的输出。
* yii\console\controllers\MessageController - 从源文件提取翻译信息。 要了解更多关于这个命令的用法,请参阅 [I18N 章节](http://www.yiichina.com/doc/guide/2.0/tutorial-i18n#message-command).
* yii\console\controllers\MigrateController - 管理应用程序数据库迁移。 在 [数据库迁移章节](http://www.yiichina.com/doc/guide/2.0/db-migrations) 可获取更多信息。
## 用法
你可以使用以下语法来执行控制台控制器操作:
~~~
yii <route> [--option1=value1 --option2=value2 ... argument1 argument2 ...]
~~~
以上,`<route>` 指的是控制器动作的路由。选项将填充类属性,参数是动作方法的参数。
例如,将 yii\console\controllers\MigrateController::actionUp() 限制 5 个数据库迁移并将 yii\console\controllers\MigrateController::$migrationTable 设置为 `migrations` 应该这样调用:
~~~
yii migrate/up 5 --migrationTable=migrations
~~~
> **注意**: 当在控制台使用 `*` 时, 不要忘记像 `"*"` 一样用引号来引起来,为了防止在 shell 中执行命令时被当成当前目录下的所有文件名。
## 入口脚本
控制台应用程序的入口脚本相当于用于 Web 应用程序的 `index.php` 入口文件。 控制台入口脚本通常被称为 `yii`,位于应用程序的根目录。它包含了类似下面的代码:
~~~
#!/usr/bin/env php
<?php
/**
* Yii console bootstrap file.
*/
defined('YII_DEBUG') or define('YII_DEBUG', true);
require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
$config = require(__DIR__ . '/config/console.php');
$application = new yii\console\Application($config);
$exitCode = $application->run();
exit($exitCode);
~~~
该脚本将被创建为你应用程序中的一部分;你可以根据你的需求来修改它。如果你不需要记录错误信息或者希望提高整体性能,`YII_DEBUG` 常数应定义为 `false`。 在基本的和高级的两个应用程序模板中,控制台应用程序的入口脚本在默认情况下会启用调试模式,以提供给开发者更好的环境。
## 配置
在上面的代码中可以看到,控制台应用程序使用它自己的配置文件,名为 `console.php` 。在该文件里你可以给控制台配置各种 [应用组件](http://www.yiichina.com/doc/guide/2.0/structure-application-components) 和属性。
如果你的 web 应用程序和控制台应用程序共享大量的配置参数和值,你可以考虑把这些值放在一个单独的文件中,该文件中包括( web 和控制台)应用程序配置。 你可以在“高级”项目模板中看到一个例子。
> 提示:有时,你可能需要使用一个与在入口脚本中指定的应用程序配置不同的控制台命令。例如,你可能想使用 `yii migrate`命令来升级你的测试数据库,它被配置在每个测试套件。要动态地更改配置,只需指定一个自定义应用程序的配置文件,通过`appconfig`选项来执行命令:
>
> ~~~
> yii <route> --appconfig=path/to/config.php ...
> ~~~
## 创建你自己的控制台命令
### 控制台的控制器和行为
一个控制台命令继承自 yii\console\Controller 控制器类。 在控制器类中,定义一个或多个与控制器的子命令相对应的动作。在每一个动作中,编写你的代码实现特定的子命令的适当的任务。
当你运行一个命令时,你需要指定一个控制器的路由。例如,路由 `migrate/create` 调用子命令对应的yii\console\controllers\MigrateController::actionCreate() 动作方法。 如果在执行过程中提供的路由不包含路由 ID ,将执行默认操作(如 web 控制器)。
### 选项
通过覆盖在 yii\console\Controller::options() 中的方法,你可以指定可用于控制台命令(controller/actionID)选项。这个方法应该返回控制器类的公共属性的列表。 当运行一个命令,你可以指定使用语法 `--OptionName=OptionValue` 选项的值。 这将分配`OptionValue` 到控制器类的 `OptionName` 属性。
If the default value of an option is of an array type and you set this option while running the command, the option value will be converted into an array by splitting the input string on any commas.
### 参数
除了选项,命令还可以接收参数。参数将传递给请求的子命令对应的操作方法。第一个参数对应第一个参数,第二个参数对应第二个参数,依次类推。 命令被调用时,如果没有足够的参数,如果有定义默认值的情况下,则相应的参数将采取默认声明的值;如果没有设置默认值,并且在运行时没有提供任何值,该命令将以一个错误退出。
你可以使用 `array` 类型提示来指示一个参数应该被视为一个数组。该数组通过拆分输入字符串的逗号来生成。
下面的示例演示如何声明参数:
~~~
class ExampleController extends \yii\console\Controller
{
// 命令 "yii example/create test" 会调用 "actionCreate('test')"
public function actionCreate($name) { ... }
// 命令 "yii example/index city" 会调用 "actionIndex('city', 'name')"
// 命令 "yii example/index city id" 会调用 "actionIndex('city', 'id')"
public function actionIndex($category, $order = 'name') { ... }
// 命令 "yii example/add test" 会调用 "actionAdd(['test'])"
// 命令 "yii example/add test1,test2" 会调用 "actionAdd(['test1', 'test2'])"
public function actionAdd(array $name) { ... }
}
~~~
### 退出代码
使用退出代码是控制台应用程序开发的最佳做法。通常,执行成功的命令会返回 `0`。如果命令返回一个非零数字,会认为出现错误。 该返回的数字作为出错代码,用以了解错误的详细信息。例如 `1` 可能代表一个未知的错误,所有的代码都将保留在特定的情况下:输入错误,丢失的文件等等。
要让控制台命令返回一个退出代码,只需在控制器操作方法中返回一个整数:
~~~
public function actionIndex()
{
if (/* some problem */) {
echo "A problem occured!\n";
return 1;
}
// do something
return 0;
}
~~~
你可以使用一些预定义的常数:
* `Controller::EXIT_CODE_NORMAL` 值为 `0`;
* `Controller::EXIT_CODE_ERROR` 值为 `1`.
为控制器定义有意义的常量,以防有更多的错误代码类型,这会是一个很好的实践。
### 格式和颜色
Yii 支持格式化输出,如果终端运行命令不支持的话则会自动退化为非格式化输出。
要输出格式的字符串很简单。以下展示了如何输出一些加粗的文字:
~~~
$this->stdout("Hello?\n", Console::BOLD);
~~~
如果你需要建立字符串动态结合的多种样式,最好使用 `ansiFormat` :
~~~
$name = $this->ansiFormat('Alex', Console::FG_YELLOW);
echo "Hello, my name is $name.";
~~~
从头构建自定义模版(Building Application from Scratch)
最后更新于:2022-04-01 04:01:53
# 创建你自己的应用程序结构
> 注:本章节正在开发中。
虽然 [basic](https://github.com/yiisoft/yii2-app-basic) 和 [advanced](https://github.com/yiisoft/yii2-app-advanced) 项目模板能够满足你的大部分需求,但是,你仍有可能需要创建你自己的项目模板来开始项目。
Yii 的项目模板是一个包含 `composer.json` 文件的仓库,并被注册为一个 Composer package。任何一个仓库都可以被标识为一个 Composer package,只要让其可以通过 `create-project` Composer 命令安装。
由于完全从新创建一个你自己的模板工作量有点大,最好的方式是以一个内建模板为基础。这里,我们使用基础应用模板。
## 克隆基础模板
第一步是从 Git 仓库克隆 Yii 的基础模板:
~~~
git clone git@github.com:yiisoft/yii2-app-basic.git
~~~
等待仓库下载到你的电脑。因为为调整到你自己的模板所产生的修改不会被 push 回,你可以删除下载下来的 `.git` 目录及其内容。
## 修改文件
Next, you'll want to modify the `composer.json` to reflect your template. Change the `name`, `description`, `keywords`, `homepage`,`license`, and `support` values to describe your new template. Also adjust the `require`, `require-dev`, `suggest`, and other options to match your template's requirements. 接下来,你需要修改 `composer.json` 以配置你自己的模板。修改 `name`,`description`, `keywords`, `homepage`, `license`, 和 `support` 的值来描述你自己的模板。同样,调整 `require`, `require-dev`,`suggest` 和其他的参数来匹配你模板的环境需求。
> 注意:在 `composer.json` 文件中,使用 `extra` 下的 `writeable` 参数来指定使用模板创建的应用程序后需要设置文件权限的文件列表。
接下来,真正的修改你的应用程序默认的目录结构和内容。最后,更新 README 文件以符合你的模板。
## 发布一个 Package
模板调整好后,通过其创建一个 Git 仓库并提交你的代码。如果你希望将你的应用程序模板开源,[Github](http://www.yiichina.com/doc/guide/2.0/tutorial-start-from-scratch) 将是最好的托管服务。如果你不喜欢其他的人来跟你一起协作,你可以使用任意的 Git 仓库服务。
接下来,你需要为 Composer 注册你的 package。对于公有的模板,你可以将 package 注册到 [Packagist](https://packagist.org/)。对于私有的模板,注册 package 将会麻烦一点。参考 [Composer documentation](https://getcomposer.org/doc/05-repositories.md#hosting-your-own) 获取更多的指示。
## 使用模板
以上就是为了创建一个新的 Yii 项目模板你需要做的事情。现在,你可以使用你自己的模板创建项目了:
~~~
composer global require "fxp/composer-asset-plugin:~1.0.0"
composer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project
~~~
高级应用模版(Advanced Project Template)
最后更新于:2022-04-01 04:01:51
# Yii 2 Advanced Project Template
Yii 2 Advanced Project Template is a skeleton [Yii 2](http://www.yiiframework.com/) application best for developing complex Web applications with multiple tiers.
The template includes three tiers: front end, back end, and console, each of which is a separate Yii application.
The template is designed to work in a team development environment. It supports deploying the application in different environments.
It also goes a bit further regarding features and provides essential database, signup and password restore out of the box.
## [](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md#getting-started)Getting Started
* [Installation](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-installation.md)
* [Difference from basic project template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-comparison.md)
* [Configuring Composer](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-composer.md)
## [](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md#structure)Structure
* [Directories](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/structure-directories.md)
* [Predefined path aliases](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/structure-path-aliases.md)
* [Applications](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/structure-applications.md)
* [Configuration and environments](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/structure-environments.md)
## [](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md#additional-topics)Additional Topics
* [Creating links from backend to frontend](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/topic-link-backend-frontend.md)
高级专题(Special Topics)
最后更新于:2022-04-01 04:01:48
测试夹具(Fixtures)
最后更新于:2022-04-01 04:01:46
# Fixtures
Fixtures 是测试中非常重要的一部分。他们的主要目的是建立一个固定/已知的环境状态以确保 测试可重复并且按照预期方式运行。Yii 提供一个简单可用的 Fixure 框架 允许你精确的定义你的 Fixtures 。
Yii 的 Fixture 框架的核心概念称之为 *fixture object* 。一个 Fixture object 代表 一个测试环境的某个特定方面,它是 yii\test\Fixture 或者其子类的实例。 比如,你可以使用 `UserFixture` 来确保用户DB表包含固定的数据。 你在运行一个测试之前加载一个或者多个 fixture object,并在结束后卸载他们。
一个 Fixture 可能依赖于其他的 Fixtures ,通过它的 yii\test\Fixture::depends 来指定。 当一个 Fixture 被加载前,它依赖的 Fixture 会被自动的加载;同样,当某个 Fixture 被卸载后, 它依赖的 Fixtures 也会被自动的卸载。
## 定义一个 Fixture
为了定义一个 Fixture,你需要创建一个新的 class 继承自 yii\test\Fixture 或者 yii\test\ActiveFixture 。前一个类对于一般用途的 Fixture 比较适合, 而后者则有一些增强功能专用于与数据库和 ActiveRecord 一起协作。
下面的代码定义一个关于 `User` ActiveRecord 和相关的用户表的 Fixture:
~~~
<?php
namespace app\tests\fixtures;
use yii\test\ActiveFixture;
class UserFixture extends ActiveFixture
{
public $modelClass = 'app\models\User';
}
~~~
> 技巧:每个 `ActiveFixture` 都会准备一个 DB 表用来测试。你可以通过设置 yii\test\ActiveFixture::tableName 或 yii\test\ActiveFixture::modelClass 属性来指定具体的表。如果是后者, 表名会从 `modleClass` 指定的 `ActiveRecord` 中获取。
> 注意:yii\test\ActiveFixture 仅限于 SQL 数据库,对于 NoSQL 数据库, Yii 提供以下 `ActiveFixture` 类:
>
> * Mongo DB: yii\mongodb\ActiveFixture
> * Elasticsearch: yii\elasticsearch\ActiveFixture (since version 2.0.2)
提供给 `ActiveFixture` 的 fixture data 通常放在一个路径为 `FixturePath/data/TableName.php` 的文件中, 其中 `FixturePath`代表 Fixture 类所在的路径, `TableName` 则是和 Fixture 关联的表。在以上的例子中, 这个文件应该是`@app/tests/fixtures/data/user.php` 。 data 文件返回一个包含要被插入用户表中的数据文件,比如:
~~~
<?php
return [
'user1' => [
'username' => 'lmayert',
'email' => 'strosin.vernice@jerde.com',
'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',
'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',
],
'user2' => [
'username' => 'napoleon69',
'email' => 'aileen.barton@heaneyschumm.com',
'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',
'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',
],
];
~~~
你可以给某行指定一个 alias 别名,这样在你以后的测试中,你可以通过别名来确定某行。 在上面的例子中,这两行指定别名为`user1` 和 `user2`。
同样,你不需要特别的为自动增长(auto-incremental)的列指定数据, Yii 将会在 Fixture 被加载时自动的填充正确的列值到这些行中。
> 技巧:你可以通过设置 yii\test\ActiveFixture::dataFile 属性来自定义 data 文件的位置。 同样,你可以重写 yii\test\ActiveFixture::getData() 来提供数据。
如之前所述,一个 Fixture 可以依赖于其他的 Fixture 。比如一个 `UserProfileFixture` 可能需要依赖于 `UserFixture`, 因为 user profile 表包括一个指向 user 表的外键。那么, 这个依赖关系可以通过 yii\test\Fixture::depends 属性来指定,比如如下:
~~~
namespace app\tests\fixtures;
use yii\test\ActiveFixture;
class UserProfileFixture extends ActiveFixture
{
public $modelClass = 'app\models\UserProfile';
public $depends = ['app\tests\fixtures\UserFixture'];
}
~~~
依赖关系确保所有的 Fixtures 能够以正常的顺序被加载和卸载。在以上的例子中, 为确保外键存在, `UserFixture` 会在`UserProfileFixture` 之前加载, 同样,也会在其卸载后同步卸载。
在上面,我们展示了如何定义一个 DB 表的 Fixture 。为了定义一个与 DB 无关的 Fixture (比如一个fixture关于文件和路径的),你可以从一个更通用的基类 yii\test\Fixture 继承, 并重写 yii\test\Fixture::load() 和 yii\test\Fixture::unload() 方法。
## 使用 Fixtures
如果你使用 [CodeCeption](http://codeception.com/) 作为你的 Yii 代码测试框架, 你需要考虑使用 `yii2-codeception` 扩展,这个扩展包含内置的机制来支持加载和访问 Fixtures。 如果你使用其他的测试框架,为了达到加载和访问 Fixture 的目的, 你需要在你的测试用例中使用 yii\test\FixtureTrait。
在以下示例中,我们会展示如何通过 `yii2-codeception` 写一个 `UserProfile` 单元来测试某个 class。
在一个继承自 yii\codeception\DbTestCase 或者 yii\codeception\TestCase 的单元测试类中, 你可以在 yii\test\FixtureTrait::fixtures() 方法中声明你希望使用哪个 Fixture。比如:
~~~
namespace app\tests\unit\models;
use yii\codeception\DbTestCase;
use app\tests\fixtures\UserProfileFixture;
class UserProfileTest extends DbTestCase
{
public function fixtures()
{
return [
'profiles' => UserProfileFixture::className(),
];
}
// ...test methods...
}
~~~
在测试用例的每个测试方法运行前 `fixtures()` 方法列表返回的 Fixture 会被自动的加载, 并在结束后自动的卸载。同样,如前面所述,当一个 Fixture 被加载之前, 所有它依赖的 Fixture 也会被自动的加载。在上面的例子中,因为 `UserProfileFixture` 依赖于`UserFixtrue`,当运行测试类中的任意测试方法时, 两个 Fixture,`UserFixture` 和 `UserProfileFixture` 会被依序加载。
当我们通过 `fixtures()` 方法指定需要加载的 Fixture 时,我们既可以使用一个类名, 也可以使用一个配置数组。配置数组可以让你自定义加载的 fixture 的属性名。
你同样可以给一个 Fixture 指定一个别名(alias),在上面的例子中,`UserProfileFixture` 的别名为 `profiles` 。 在测试方法中,你可以通过别名来访问一个 Fixture 对象。比如, `$this->profiles` 将会返回 `UserProfileFixture` 对象。
因为 `UserProfileFixture` 从 `ActiveFixture` 处继承, 在后面,你可以通过如下的语法形式来访问 Fixture 提供的数据:
~~~
// returns the data row aliased as 'user1'
$row = $this->profiles['user1'];
// returns the UserProfile model corresponding to the data row aliased as 'user1'
$profile = $this->profiles('user1');
// traverse every data row in the fixture
foreach ($this->profiles as $row) ...
~~~
> 注: `$this->profiles` 的类型仍为 `UserProfileFixture`, 以上的例子是通过 PHP 魔术方法来实现的。
## 定义和使用全局 Fixtures
以上示例的 Fixture 主要用于单个的测试用例, 在许多情况下,你需要使用一些全局的 Fixture 来让所有或者大量的测试用例使用。 yii\test\InitDbFixture 就是这样的一个例子,它主要做两个事情:
* 它通过执行在 `@app/tests/fixtures/initdb.php` 里的脚本来做一些通用的初始化任务;
* 在加载其他的 DB Fixture 之前禁用数据库完整性校验,同时在其他的 DB Fixture 卸载后启用数据库完整性校验。
全局的 Fixture 和非全局的用法类似,唯一的区别是你在 yii\codeception\TestCase::globalFixtures() 中声明它, 而非 `fixtures()` 方法。当一个测试用例加载 Fixture 时, 它首先加载全局的 Fixture,然后才是非全局的。
yii\codeception\DbTestCase 默认已经在其 `globalFixtures()` 方法中声明了 `InitDbFixture`, 这意味着如果你想要在每个测试之前执行一些初始化工作,你只需要调整 `@app/tests/fixtures/initdb.php` 中的代码即可。 你只需要简单的集中精力中开发单个的测试用例和相关的 Fixture。
## 组织 Fixture 类和相关的数据文件
默认情况下,Fixture 类会在其所在的目录下面的 `data` 子目录寻找相关的数据文件。 在一些简单的项目中,你可以遵循此范例。对于一些大项目, 您可能经常为同一个 Fixture 类的不同测试而切换不同的数据文件。 在这种情况下,我们推荐你按照一种类似于命名空间 的方式有层次地组织你的数据文件,比如:
~~~
# under folder tests\unit\fixtures
data\
components\
fixture_data_file1.php
fixture_data_file2.php
...
fixture_data_fileN.php
models\
fixture_data_file1.php
fixture_data_file2.php
...
fixture_data_fileN.php
# and so on
~~~
这样,你就可以避免在测试用例之间产生冲突,并根据你的需要使用它们。
> 注意:在以上的例子中,Fixture 文件只用于示例目的。在真实的环境下,你需要根据你的 Fixture 类继承的基类来决定它们的命名。 比如,如果你从 yii\test\ActiveFixture 继承了一个 DB Fixture, 你需要用数据库表名字作为 Fixture 的数据文件名;如果你从 yii\mongodb\ActiveFixture 继承了一个 MongoDB Fixture, 你需要使用 collection 名作为文件名。
组织 Fixuture 类名的方式同样可以使用前述的层次组织法,但是,为了避免跟数据文件产生冲突, 你需要用 `fixtures` 作为根目录而非 `data`。
## 总结
> 注意:本节内容正在开发中。
在上面,我们描述了如何定义和使用 Fixture,在下面,我们将总结出一个 标准地运行与 DB 有关的单元测试的规范工作流程:
1. 使用 `yii migrate` 工具来让你的测试数据库更新到最新的版本;
2. 运行一个测试:
* 加载 Fixture:清空所有的相关表结构,并用 Fixture 数据填充
* 执行真实的测试用例
* 卸载 Fixture
3. 重复步骤2直到所有的测试结束。
**以下部分即将被清除**
# 管理 Fixture
>注: 本章节正在开发中 > > todo: 以下部分将会与 test-fixtures.md 合并。
Fixture 是测试中很很重要的一个部分,它们的主要目的是为你提供不同的测试所需要的数据。 因为这些数据,你的测试将会更高效和有用。
Yii 通过 `yii fixture` 命令行工具来支持 Fixture,这个工具支持:
* 把 Fixture 加载到不同的存储工具中,比如:RDBMS,NoSQL 等;
* 以不同的方式卸载 Fixture(通常是清空存储);
* 自动的生成 Fixutre 并用随机数据填充。
## Fixtures 格式
Fixtures 是不同方法和配置的对象, 官方引用[documentation](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixture.md) 。 让我们假设有 Fixtures 数据加载:
~~~
#Fixtures 数据目录的 users.php 文件,默认 @tests\unit\fixtures\data
return [
[
'name' => 'Chase',
'login' => 'lmayert',
'email' => 'strosin.vernice@jerde.com',
'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',
'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',
],
[
'name' => 'Celestine',
'login' => 'napoleon69',
'email' => 'aileen.barton@heaneyschumm.com',
'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',
'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',
],
];
~~~
如果我们使用 Fixture 将数据加载到数据库,那么这些行将被应用于 `users` 表。 如果我们使用 nosql Fixtures ,例如 `mongodb`fixture,那么这些数据将应用于 `users` mongodb 集合。 为了了解更多实现各种加载策略,访问官网 [documentation](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixture.md)。 上面的 Fixture 案例是由 `yii2-faker` 扩展自动生成的, 在这里了解更多 [section](http://www.yiichina.com/doc/guide/2.0/test-fixtures#auto-generating-fixtures)。 Fixture 类名不应该是复数形式。
## 加载 Fixtures
Fixture 类应该以 `Fixture` 类作为后缀。默认的 Fixtures 能在 `tests\unit\fixtures` 命名空间下被搜索到, 你可以通过配置和命名行选项来更改这个行为,你可以通过加载或者卸载指定它名字前面的`-`来排除一些 Fixtures,像 `-User`。
运行如下命令去加载 Fixture:
~~~
yii fixture/load <fixture_name>
~~~
必需参数 `fixture_name` 指定一个将被加载数据的 Fixture 名字。 你可以同时加载多个 Fixtures 。 以下是这个命令的正确格式:
~~~
// load `User` fixture
yii fixture/load User
// same as above, because default action of "fixture" command is "load"
yii fixture User
// load several fixtures
yii fixture User UserProfile
// load all fixtures
yii fixture/load "*"
// same as above
yii fixture "*"
// load all fixtures except ones
yii fixture "*" -DoNotLoadThisOne
// load fixtures, but search them in different namespace. By default namespace is: tests\unit\fixtures.
yii fixture User --namespace='alias\my\custom\namespace'
// load global fixture `some\name\space\CustomFixture` before other fixtures will be loaded.
// By default this option is set to `InitDbFixture` to disable/enable integrity checks. You can specify several
// global fixtures separated by comma.
yii fixture User --globalFixtures='some\name\space\Custom'
~~~
## 卸载 Fixtures
运行如下命名去卸载 Fixtures:
~~~
// unload Users fixture, by default it will clear fixture storage (for example "users" table, or "users" collection if this is mongodb fixture).
yii fixture/unload User
// Unload several fixtures
yii fixture/unload User,UserProfile
// unload all fixtures
yii fixture/unload "*"
// unload all fixtures except ones
yii fixture/unload "*" -DoNotUnloadThisOne
~~~
同样的命名选项: `namespace`, `globalFixtures` 也可以应用于该命令。
## 全局命令配置
当命令行选项允许我们配置迁移命令, 有时我们只想配置一次。例如, 你可以按照如下配置迁移目录:
~~~
'controllerMap' => [
'fixture' => [
'class' => 'yii\console\controllers\FixtureController',
'namespace' => 'myalias\some\custom\namespace',
'globalFixtures' => [
'some\name\space\Foo',
'other\name\space\Bar'
],
],
]
~~~
## 自动生成 fixtures
Yii 还可以为你自动生成一些基于一些模板的 Fixtures。 你能够以不同语言格式用不同的数据生成你的 Fixtures。 这些特征由 [Faker](https://github.com/fzaninotto/Faker) 库和 `yii2-faker` 扩展完成。 关注 [guide](https://github.com/yiisoft/yii2-faker) 扩展获取更多的文档。