ActiveRecord
最后更新于:2022-04-01 11:17:48
## Active Record
### 列出全部Restaurant
~~~
$model = Restaurant::find()->orderBy('name')->all();
return $this->render('index', ['model' => $model]);
~~~
### 按Reviews数量排序
~~~
$query = Restaurant::find()
->select(['restaurant.*', 'COUNT(review.id) AS reviewsCount'])
->joinWith('reviews');
$model = $query->orderBy('reviewsCount DESC')->groupBy('restaurant.id')->all();
return $this->render('index', ['model' => $model]);
~~~
### 计算Total Rating和Average Rating
~~~
public function getTotalRating()
{
$totalRating = 0;
foreach ($this->reviews as $review) {
$totalRating += $review->rating;
}
return $totalRating;
}
public function getAverageRating()
{
return $this->totalRating / count($this->reviews);
}
~~~
### 搜索
~~~
public function actionIndex($search = null)
{
if ($search) {
//$query->where("restaurant.name like :name",[':name' => '%'.$search.'%']);
$query->where(['like', 'name', $search]);
}
}
~~~
添加评论
最后更新于:2022-04-01 11:17:46
### 添加评论
上传图片
最后更新于:2022-04-01 11:17:44
### 上传图片
>[info] 新需求:要求每个餐馆配一张门店图片
数据库操作(进阶)
最后更新于:2022-04-01 11:17:41
使用Gii
最后更新于:2022-04-01 11:17:39
### 回顾
- Git的使用(代码的获取、推送、冲突)
- 前端资源的存放
### Gii的作用
http://localhost:8080/yii-demo/frontend/web/gii
>[warning] 因为Gii生成的代码过于理想化,建议在实际开发过程中不使用,而只是把它生成的代码作为参考范例。
- 生成模型
- 生成增删改查(含控制器和视图)
- 生成控制器
- 生成表单
- 生成Module
- 生成Extension
### 生成模型
~~~
yii gii/model --tableName=restaurant --modelClass=Restaurant --ns=common\models
~~~
### 生成增删改查
>[info] 新案例:新建省份表,并对此表中的字段进行增删改查。
~~~
yii migrate/create create_province
$this->createTable('province', [
'id' => $this->primaryKey(),
'name' => $this->string(20)->notNull(),
'capital' => $this->string(10)->notNull(),
'area' => $this->float(),
'city_number' => $this->integer()
]);
yii migrate
# 生成Model
yii gii/model --tableName=province --modelClass=Province --ns=common\models
# 插入原始数据
INSERT INTO province (name, capital, area, city_number) VALUES ( '江苏省', '南京市', 10.72, 13 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '安徽省', '合肥市', 13.94, 16 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '浙江省', '杭州市', 10.18, 11 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '江西省', '南昌市', 16.69, 11 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '山东省', '济南市', 15.80, 17 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '甘肃省', '兰州市', 45.37, 12 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '广东省', '广州市', 17.97, 21 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '湖北省', '武汉市', 18.59, 11 );
INSERT INTO province (name, capital, area, city_number) VALUES ( '黑龙江省', '哈尔滨市', 47.3, 12);
INSERT INTO province (name, capital, area, city_number) VALUES ( '辽宁省', '沈阳市', 14.8, 14)
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705d0d113beb.jpg)
接下来,简要分析下生成的Controllers和Views。
添加餐馆(二)
最后更新于:2022-04-01 11:17:37
>[danger] 本篇内容非常重要,务必加深理解!
[TOC]
### 后端代码
~~~
public $enableCsrfValidation = false;
public function actionCreate()
{
if (Yii::$app->request->isPost) {
$name = $_POST['name'];
$city = $_POST['city'];
$province = $_POST['province'];
# 写INSERT语句或使用下面的对象操作方法
$model = new Restaurant();
$model->name = $name;
$model->city = $city;
$model->province = $province;
$model->save();
return $this->redirect('index');
}
return $this->render('create');
}
~~~
### 后端验证
>[info] 验证要求:三个字段均不为空,且餐馆名称不能大于5个汉字。
**更改数据库字段类型**
~~~
$this->alterColumn('restaurant', 'name', $this->string(5)->notNull());
$this->alterColumn('restaurant', 'city', $this->string()->notNull());
$this->alterColumn('restaurant', 'province', $this->string()->notNull());
~~~
**重新生成Model**
~~~
yii gii/model --tableName=restaurant --modelClass=Restaurant --ns=common\models
~~~
**实现后端验证**
~~~
public function actionCreate()
{
$model = new Restaurant();
if (Yii::$app->request->isPost) {
$name = $_POST['name'];
$city = $_POST['city'];
$province = $_POST['province'];
$model->name = $name;
$model->city = $city;
$model->province = $province;
if ($model->save()) {
return $this->redirect('index');
}
}
return $this->render('create', ['model' => $model]);
}
/* @var $model common\models\Restaurant */
<pre><?= json_encode($model->errors) ?></pre>
~~~
### 后端代码(第二版)
**修改前端提交参数名**
~~~
<input type="text" class="form-control" id="name" name="Restaurant[name]">
<input type="text" class="form-control" id="city" name="Restaurant[city]">
<input type="text" class="form-control" id="province" name="Restaurant[province]">
~~~
**重写后端代码**
~~~
public function actionCreate()
{
$model = new Restaurant();
if ($model->load(Yii::$app->request->post())) {
if ($model->save()) {
return $this->redirect(['index']);
}
}
return $this->render('create', ['model' => $model]);
}
~~~
### 探索
- load()函数何时返回true,何时返回false?返回true或false的时候,都做了什么工作?
- save()函数何时返回true,何时返回false?返回true或false的时候,都做了什么工作?
- 使用这样的模型绑定函数,有什么好处?
添加餐馆(一)
最后更新于:2022-04-01 11:17:35
### 添加餐馆(前端部分)
新建Action
~~~
public function actionCreate()
{
return $this->render('create');
}
~~~
列表页面
~~~
<p><a href="#"><i class="fa fa-plus"></i> 添加新餐馆</a></p>
~~~
>[info] 请参考Bootstrap手册 http://v3.bootcss.com/css/#forms
添加页面
~~~
<form>
<div class="form-group">
<label for="name">名称</label>
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="form-group">
<label for="city">城市</label>
<input type="text" class="form-control" id="city" name="city">
</div>
<div class="form-group">
<label for="province">省份</label>
<input type="text" class="form-control" id="province" name="province">
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
~~~
删除餐馆
最后更新于:2022-04-01 11:17:32
### 讨论:页面开发的步骤
1. 前端页面设计
2. 后端代码编写
3. 前后端整合
### 删除餐馆(前端部分)
~~~
<td><a href="#">编辑</a></td>
<td><a href="#" data-confirm="确认删除吗?">删除</a></td>
~~~
### 讨论:解决复杂问题的思路
(以删除功能为例)
- 需要弹出提示
- post方法
- 需要新建一个action
- 需要重定向到列表页
- 需要传入id参数
- 需要从数据库中删除
### 删除餐馆(后端部分)
~~~
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
protected function findModel($id)
{
$model = Restaurant::findOne($id);
if ($model) {
return $model;
}
throw new NotFoundHttpException('找不到对应的餐馆');
}
~~~
### 与前端代码整合
~~~
<a href="<?= Url::to(['delete', 'id' => $item->id]) ?>" data-method="post" data-confirm="确认删除吗?">删除</a>
~~~
列出餐馆
最后更新于:2022-04-01 11:17:30
### 讨论
- 为什么要使用ORM?(对象关系隐射)
- 为什么要使用原生SQL语句?
### 列出餐馆
>[info] 请查看Active Record部分
>http://www.yiiframework.com/doc-2.0/guide-db-active-record.html
~~~
$model = Restaurant::find()->all();
$model = Restaurant::findBySql('SELECT * FROM restaurant ORDER BY name DESC')->all();
<?php foreach ($model as $item): ?>
<tr>
<td><?= $item->name ?></td>
<td><?= $item->city ?></td>
<td><?= $item->province ?></td>
<td><a href="#">编辑</a></td>
<td><a href="#">删除</a></td>
</tr>
<?php endforeach ?>
~~~
新建数据库
最后更新于:2022-04-01 11:17:28
### 讨论
为什么使用迁移文件来创建数据库?
1. 对数据库的变更操作有日志记录。
2. 便于生成一对多和多对多结构的数据库。
### 新建数据库
>[info] 请参考 http://www.yiiframework.com/doc-2.0/guide-db-migrations.html
1. 编写数据库迁移文件
2. 应用迁移文件,创建数据库
3. 生成Model
### 案例:Restaurant & Review
~~~
# 添加Restaurant表
yii migrate/create create_restaurant
$this->createTable('restaurant', [
'id' => $this->primaryKey(),
'name' => $this->string(),
'city' => $this->string(),
'province' => $this->string()
]);
yii migrate
# 添加Review表
yii migrate/create create_review
$this->createTable('review', [
'id' => $this->primaryKey(),
'rating' => $this->integer(),
'body' => $this->text()
]);
$this->addColumn('review', 'restaurant_id', $this->integer());
$this->createIndex('idx-review-restaurant_id', 'review', 'restaurant_id');
$this->addForeignKey('fk-review-restaurant_id', 'review', 'restaurant_id', 'restaurant', 'id', 'CASCADE');
yii migrate
# 生成Model
yii gii/model --tableName=restaurant --modelClass=Restaurant --ns=common\models
yii gii/model --tableName=review --modelClass=Review --ns=common\models
~~~
### 添加初始数据
1. 通过phpMyAdmin
2. 通过程序方式
~~~
yii migrate/create seed_restaurant_review
$restaurant = new Restaurant();
$restaurant->name = "老乡鸡";
$restaurant->city = '合肥';
$restaurant->province = '安徽省';
$restaurant->save();
$review = new Review();
$review->rating = 9;
$review->body = '真是美味的快餐';
$review->restaurant_id = $restaurant->id;
$review->save();
$review = new Review();
$review->rating = 6;
$review->body = '随便吃吃';
$review->restaurant_id = $restaurant->id;
$review->save();
yii migrate
~~~
### 添加字段
~~~
yii migrate/create add_reviewer_to_review --fields="reviewer_name:string"
$this->addColumn('review', 'reviewer_name', $this->string());
yii migrate
~~~
### 修改字段
~~~
yii migrate/create alter_reviewer_to_review
$this->alterColumn('review', 'reviewer_name', $this->string(100));
yii migrate
~~~
数据库操作
最后更新于:2022-04-01 11:17:25
## 从数据库中获取数据
Action Filter
最后更新于:2022-04-01 11:17:23
## Filter
>[info] 请参考 http://www.yiiframework.com/doc-2.0/guide-structure-filters.html
Filter是一种特殊的Behavior。
### AccessControl
~~~ php
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['create', 'update'],
'rules' => [
// allow authenticated users
[
'allow' => true,
'roles' => ['@'],
],
// everything else is denied by default
],
],
];
}
~~~
### ContentNegotiator
~~~ php
public function behaviors()
{
return [
[
'class' => ContentNegotiator::className(),
'formats' => [
'application/json' => Response::FORMAT_JSON,
'application/xml' => Response::FORMAT_XML,
],
'languages' => [
'en-US',
'de',
],
],
];
}
~~~
### HttpCache
~~~ php
public function behaviors()
{
return [
[
'class' => HttpCache::className(),
'only' => ['index'],
'lastModified' => function ($action, $params) {
$q = new \yii\db\Query();
return $q->from('user')->max('updated_at');
},
],
];
}
~~~
### PageCache
~~~ php
public function behaviors()
{
return [
'pageCache' => [
'class' => PageCache::className(),
'only' => ['index'],
'duration' => 60,
'dependency' => [
'class' => DbDependency::className(),
'sql' => 'SELECT COUNT(*) FROM post',
],
'variations' => [
\Yii::$app->language,
]
],
];
}
~~~
### VerbFilter
~~~ php
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'index' => ['get'],
'view' => ['get'],
'create' => ['get', 'post'],
'update' => ['get', 'put', 'post'],
'delete' => ['post', 'delete'],
],
],
];
}
~~~
### Cors
~~~ php
public function behaviors()
{
return ArrayHelper::merge([
[
'class' => Cors::className(),
'cors' => [
'Origin' => ['http://www.myserver.net'],
'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],
],
'actions' => [
'login' => [
'Access-Control-Allow-Credentials' => true,
]
]
],
], parent::behaviors());
}
~~~
生成视图
最后更新于:2022-04-01 11:17:21
### 在Controller中
- render(): 渲染一个View并使用Layout返回到结果。
- renderPartial(): 渲染一个View并且不使用Layout。
- renderAjax(): 渲染一个View并且不使用Layout,并注入所有注册的JS/CSS脚本和文件,通常使用在响应AJAX网页请求的情况下。
- renderFile(): 渲染一个View文件目录或别名下的View文件。
- renderContent(): 渲染一个静态字符串,嵌入到当前Layout中。
### 在View中
- render(): 渲染一个View
- renderAjax(): 渲染一个View,并注入所有注册的JS/CSS脚本和文件,通常使用在响应AJAX网页请求的情况下。
- renderFile(): 渲染一个View文件目录或别名下的View文件。
### 分部视图(Partial View)
~~~
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a href="<?= Yii::$app->homeUrl ?>" class="navbar-brand">My Company</a>
</div>
<?= $this->render('menu') ?>
</div>
</nav>
~~~
路由和控制器
最后更新于:2022-04-01 11:17:18
>[info] 请参考 http://www.yiiframework.com/doc-2.0/guide-runtime-routing.html
### 路由的原理
默认路由 site/index
### 创建URL
~~~ php
use yii\helpers\Url;
echo Url::to(['post/index']);
echo Url::to(['post/view', 'id' => 100]);
~~~
### URL静态化
frontend\config\main.php
~~~
[
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => false,
'rules' => [
// ...
],
],
],
]
~~~
控制器
最后更新于:2022-04-01 11:17:16
## Yii 2.0 控制器
小部件
最后更新于:2022-04-01 11:17:14
### 常用Widgets
>[warning] 应尽量避免使用Widgets控件,但以下Widgets可以极大的减少代码,推荐使用。
- 创建静态资源
- 创建Link
- 创建Form和建Input
- 创建Pagination(分页)
### 创建静态资源
~~~
<?= Html::cssFile('@web/assets/bootstrap/css/bootstrap.min.css') ?>
<?= Html::jsFile('@web/assets/jquery/jquery.min.js') ?>
~~~
### 创建Link
~~~
<a href="<?= Url::to(['review/edit', 'id' => $item->id]) ?>">Edit</a>
# ReviewController.php
public function actionEdit($id)
{
}
~~~
### 创建Form和Input
~~~
<?php $form = ActiveForm::begin() ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'city') ?>
<button type="submit" class="btn btn-primary">Save</button>
<?php ActiveForm::end() ?>
~~~
### 自定义Widget
MyForm 和 MyField (比较复杂,单独讲解)
母版页
最后更新于:2022-04-01 11:17:12
### 静态资源引用
>[danger] Yii自带的Assets功能极大的增加了学习成本,因此暂定去除此功能。
>请参考 http://www.yiichina.com/doc/guide/2.0/structure-assets
1. 明确web目录下assets、css、js、images这几个目录的作用。
2. 新建web\lib目录,把第三方资源库放到这个目录中。
3. 修改母版页代码。
~~~
//AppAsset::register($this);
# 头部
<?php $this->head() ?>
<?= Html::cssFile('@web/lib/bootstrap/css/bootstrap.min.css') ?>
<?= Html::cssFile('@web/css/bootstrap-theme.css') ?>
<?= Html::cssFile('@web/css/site.css') ?>
# 尾部
<?= Html::jsFile('@web/lib/jquery/jquery.min.js') ?>
<?= Html::jsFile('@web/lib/bootstrap/js/bootstrap.min.js') ?>
<?= Html::jsFile('@web/lib/yii/yii.js') ?>
<?= Html::jsFile('@web/lib/yii/yii.activeForm.js') ?>
<?= Html::jsFile('@web/lib/yii/yii.validation.js') ?>
<?= Html::jsFile('@web/js/site.js') ?>
<?php if (isset($this->blocks['script'])): ?>
<?= $this->blocks['script'] ?>
<?php endif ?>
<?php $this->endBody() ?>
</body>
~~~
### 改造Navbar
>[success] 避免使用Widgets控件,尽量做到前后端分离。
~~~
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a href="<?= Yii::$app->homeUrl ?>" class="navbar-brand">My Company</a>
</div>
<ul class="nav navbar-nav navbar-right">
...
</ul>
</div>
</nav>
~~~
### 数据块
默认数据块:$content
自定义数据块:
1、在母版页中
~~~
<?php if (isset($this->blocks['script'])): ?>
<?= $this->blocks['script'] ?>
<?php else: ?>
... 默认内容 ...
<?php endif ?>
~~~
2、在内容View中
~~~
<?php $this->beginBlock('script'); ?>
...content of script block...
<?php $this->endBlock(); ?>
~~~
View介绍
最后更新于:2022-04-01 11:17:09
### 案例:餐馆评价
frontend\controllers\Review.php
frontend\views\review\index.php
### 模板引擎
Yii使用PHP作为默认的模板引擎
Template + Data = 生成界面
### XSS
跨站脚本攻击 Cross-site Scripting
~~~
use yii\helpers\Html;
<?= Html::encode($item->city) ?>
~~~
### CSRF
跨站请求伪造 Cross-site Request Forgery
~~~
<head>
<meta charset="<?= Yii::$app->charset ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?= Html::csrfMetaTags() ?>
</head>
~~~
### 代码表达式
~~~
<?= count($model) ?>
<?= $firstReview->name ?>
~~~
### 代码片段
~~~
$firstReview = $model[0];
<?php foreach ($model as $item): ?>
<li><?= $item->rating ?></li>
<?php endforeach ?>
~~~
视图
最后更新于:2022-04-01 11:17:07
## Yii 2.0 视图
本章主要说一些和视图(View)有关的概念。
Git工作流
最后更新于:2022-04-01 11:17:05
### 简单的协同开发
准备远程项目
1. 在远程代码库中新建项目。
2. 由项目发起人(徐飞)提交代码到远程代码库中。
获取远程项目
1. 其他开发人员克隆该代码库到自己的电脑上。
2. 使用PhpStorm或Visual Studio Code进行代码的修改和提交。
### 实际项目中的协同开发
通过实际项目“餐馆评论网”来磨合团队,比如项目分工、代码冲突、分支管理等问题。
- Visual Studio Code的Git界面设计简洁,容易上手。
- PhpStorm自带的Git工具比较复杂,需要熟悉一段时间,好处是无需离开编辑器。