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工具比较复杂,需要熟悉一段时间,好处是无需离开编辑器。
';