本文标题按照文章序号的结构层次顺序严格划分确保阅读体验格式参考链接
文章目录
一、运行原理初探
(一)Basic模板
Basic应用模板文件目录及其作用:
MVC:
- controllers 控制器类
- models 模型类
- views 视图文件
其他:
- commands 控制台命令类
- tests 测试相关的文件
- assets 资源文件
- config/ 应用配置及其它配置
- web Web应用根目录,包含Web入口文件
assets 资源文件(JavaScript和css)
index.php 应用入口文件 - runtime 运行时生成的,例如日志和缓存等文件
- vendor Yii框架自身及第三方的扩展
- yii Yii控制台命令执行脚本
(二)Yii请求到相应的生命周期
1.1 入口脚本
1.2 应用主体
Application可以代表一个应用系统,但是入口脚本里的应用主体是由yii\web\Application实例化的一个对象,并不是一个应用系统,在请求时创建,在响应后消失,最多存活20ms。应用主体:管理Yii应用系统整体结构和生命周期的对象,我们可以用\Yii::$app
来访问应用
(1)应用主体的配置
- $config变量给应用主体这个对象的属性进行初始化赋值
- $config变量是从配置文件web.php加载而来的
(2)应用主体的属性
- id属性用来区分其他应用的唯一标识ID
- basePath指定该应用的根目录
- components注册多个在其他地方使用的应用组件
- defaultRoute如何加载控制器
1.3 控制器
控制器:
- 是MVC结构中的C这个部分
- 从yii\web\Controller继承来
- 负责处理请求和生成响应
组成:
- 控制器主要由
动作
组成 动作
是控制器类中的方法- 一个动作对应一个页面
render():
Yii控制层处理结果返回前端views
public string render($ view,$ params=[] )
- $view指视图名
- $params是传给视图的数据,通常传递模型对象,可以传递多个对象
- 返回的是字符串,就是渲染好的结果
- 视图或布局文件不存在时,抛出的异常
1.4 视图
视图是MVC模式中View这一部分,视图是在yii\web\View应用组件的帮助下,依据视图模板文件,进行构造和渲染完成的。习惯上称视图模板文件为视图,视图模板文件主要由HTML代码和展示类PHP代码构成。
(1)什么是布局
布局是一种特殊的视图,表现多个视图的公共部分。
(2)如何创建布局
布局也是视图,它可像普通视图一样创建布局默认存储在@app/views/layouts里,layouts/main.php就是一个典型默认的布局。
- 布局开始处调用:$this->beginPage()
- 布局结尾处调用:$this->endPage()
- body标签开始处调用:$this->beginBody()
- body标签结尾处调用:$this->endBody()
(3)如何变换布局
新建布局/views/layouts/wx.php,设置布局为wx:$this->layout = 'wx';
,如果layout设置为false表示不使用任何布局。
- this是指向yii\web\view应用组件的例化,负责管理和渲染视图模板文件,除了$this.title设置页面标题还有其他的方法,参考:
https://www.yiichina.com/doc/api/2.0/yii-web-view
- $content是视图模板文件渲染出来的结果
1.5 模型
以Contact界面为例做分析:
(1)Contact流程是怎样的?
(2)模型是什么?ContactForm是什么类
模型:MVC中的一部分,是表现业务数据、规则和逻辑的对象,可通过继承yii\base\Model或它的子类定义模型类,基类yii\base\Model支持许多实用的特性:
- 属性:表现业务数据,可以像普通类属性或数组一样被访问
- 属性标签:指定属性显示出来的标签
- 块赋值:支持一步给许多属性赋值
- 验证规则:确保输入数据符合所申明的验证规则
- 数据导出:允许模型数据导出为自定义格式的数组
Model类也是更多高级模型如Active Record活动记录的基类,更多关于这些高级模型的详情请参考相关手册。
1.6 表单
(1)HTML帮助类
(2)$model的load方法如何实现数据赋值
(3)模型类的总结
(4)表单的总结
1.7 关键概念
岁月未曾饶过我 我亦未曾饶过岁月 2020年7月26日 午夜
二、制作自己的博客
(一)Advanced模板的学习
Advanced模板对于Basic模板的优点:
- 前后台分离
- 建好了基于数据库连接的用户模型,实现了用户模型的认证
- 写好了注册,重置密码等功能
Advanced模板相当于含2个Basic模板。可以当Advanced版应用里面有2个Basic版应用,分别是前台和后台,并且它们之间可以有联系。
- 在开发中常使用别名,方便。
- 对于配置文件,按照箭头的方向,越靠近index 优先级越高
(二)博客系统的需求
上述表中一些外键的解释:
- #status关联文章状态表,代表一些状态,草稿,已发布等等
- #author_id关联管理用户表,表示文章作者
- #post_id关联文章表,表示一篇文章可能有多个评论
- #staus关联评论状态,表示已审核,未审核等状态
- #userid关联会员用户表,表示评论作者
(三)Gii创建博客原型
3.1 什么是Gii
Gii是一个基于Web的代码生成器,可以用来生成模型,控制器,表单,增删查改等等这样类功能的代码。
3.2 如何使用Gii
- 首先确保Gii开启可用
- 访问Gii,根据提示填写表单即可生成代码
第一步:创建模型类,依次创建好几个模型
第二步:创建增删查改功能
修改导航:
目前效果:
(四)后台完善之Post部分——查看文章页面
- PostController.php中:
4.1 如何使用DAO访问数据库(不推荐)?
用SQL查询语句来创建一个yii\db\Command的对象,调用对象的方法来执行SQL查询,返回值是字符型的数组。 $ post = Yii::$app->db->createCommand(‘select * from post’)->queryAll();‘
4.2 什么是ActiveRecord?
4.3 ActiveRecord的静态方法findOne是怎么工作的?
第一句话:一个AR类关联一张数据表,每个AR对象对应表中的一行。
# 这俩句话一样 注:前面的Post是数据库表名
Post::find()->where(['id'=>1])->one();
Post::findOne(1))
# 这俩句话一样
Post::find()->where(['status'=>1])->all();
Post::findAll(['status'=>1])
findone和findall分别同作用于上面的语句···->one()和···->all()
其实find方法创建一个ActiveQueryInterface的实例对象,我们来关注这个对象所带的方法:
同时整理了一下where的写法:
所以我们查询文章的写法就是:
第二句话:AR对象的属性,对应为数据的列。
第三句话:可以直接以面对对象的方式来操纵数据表中的数据,这样就不需要写SQL语句就能实现数据库的访问。
AR类的sava()方法可以自动判断是新增还是更新。
ActiveRecord是框架中的灵魂,它把模型和数据库建立起了联系,让我们用面对对象的方式来访问和操纵数据库中的数据。
- 视图文件中:
4.4 什么是DetailView小部件?
Yii提供了一套数据小部件Widgets,这些小部件可以用于显示数据。
- DetailView小部件用于显示一条记录数据。
- ListView和GridView小部件能够用于显示一个拥有分页、排序和过滤功能的一个列表或者表格。
但是实际中,我们发现这些小部件部分功能是英文,日期格式也不符合中文的习惯,所以我们还要自定义一下小部件(小部件的修改)
前面三个修改比较简单,在backend/views/post文件夹下,面包屑和按钮名字去view.php中修改,文章的属性例如ID、Title、Concent等等去backend/common/models/Post.php的attributeLabels函数中修改。同时记得把backend/config/main.php加上language=‘zh-CN’
文章状态对应草稿、已发布、已归档,ID对应有作者的名字,其实就是把表中的ID号拿出来,去另外一个数据库表中查询这个ID所对应的含义,通过以下这个方法可以达到效果:
$thePost = Post::findOne(1));
$theStatus = Poststatus::findOne($thePost->status);
echo $theStatus->name;
但是我们不推荐以上这种方法,Gii会根据数据库表之鉴的关联关系,自动生成建立类之间的关联关系代码:
$thePost = Post::findOne(1));
echo $thePost->status0->name;
Gii已经生成了文章类和文章状态类的相关联的代码,相当于原来的文章对象中多了一个属性status0,这个属性的值就是一个文章状态类的对象,所以要拿到文章状态类的"已发布"文字就可以直接$thePost->status0->name;
这里展示下模型Post.php中拥有gii自动生成的方法,AR类不止是hasOne中还有hasmany方法,hasOne用于多对一或者一对一,例如一个id对应一个作者名字,hasMany用于一对多,例如一篇文章有多条评论。
ActiveRecord是一个好用且强大的类,当然这基于一个良好的数据库设计。
修改代码以实现效果:
4.5 它是怎么去展示一个对象的详细内容的?
对时间的格式和label标签宽度排版进行修改:
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'title',
'content:ntext', //设置的格式,参考widgets关于DetailView的手册
'tags:ntext',
//'status',
['label'=>'状态',
'value'=>$model->status0->name,
],
//'create_time:datetime',
['attribute'=>'create_time',
'value'=>date('Y-m-d H:i:s',$model->create_time)
],
//'update_time:datetime',
['attribute'=>'update_time',
'value'=>date('Y-m-d H:i:s',$model->update_time)
],
//'author_id',
//['label'=>'状态',
// 'value'=>$model->author->nickname,
// ],
# 下面和status比起来是一种更方便的写法,不用重写label
['attribute'=>'author_id',
'value'=>$model->author->nickname,
],
],
'template'=>'<tr><th style="width: 120px;">{label}</th><td>{value}</td></tr>',
'options'=>['class'=>'table table-striped table-bordered detail-view'],
]) ?>
[ ]里面的东西都属于配置,配置通常是一个关联数组,在Yii2框架中广泛的用来初始化,创建对象。 以下是对小部件的总结:
(五)后台完善之Post部分——修改新增文章页面
需要修改的部分:
我们回顾以下ActiveForm表单:表单以ActiveForm::begin()开始,ActiveForm::end()结束,它把模型和表单结合在一起,利用ActiveFiled对象填入内容,开发方便,提高了表单的可读性。额外的标签可以用HTML帮助类
5.1 ArrayHelper数组助手类、下拉菜单dropDownList
面包屑、按钮等处在对应的视图文件中修改,文章状态不仅要求显示数字,而要从数据库中找出对应的状态中文名显示出来,中文名有相应的ID。
在介绍ArrayHelper数组助手类之前,我们回顾HTML帮助类:
Yii2中HTML帮助类能让我们更好地处理Html标签,ArrayHelper数组助手类能让我们更好地处理数组。其中ArrayHelper提供了几个常用的静态方法:
getValue获取值:
获取列getColumn:
建立映射表map:
数组助手类中还有很多其他的静态方法,不多介绍:
//视图文件 _form.php
<?php
$psObjs = Poststatus::find()->all();
$allStatus = ArrayHelper::map($psObjs,'id','name');
?>
<?=$form->field($model,'status')->dropDownList($allStatus,['prompt'=>'请选择状态']);?>
效果如图:
5.2 查询构建器QueryBuilder
查询构建器建立在DAO (data access object) 数据访问对象模式之上,创建程序化的SQL语句,让SQL更易读、安全。
select方法:
- 使用字符串或者一个数组来指定需要查询的字段
$query->select('id','name'); //可以是字符串
$query->select(['id','email']);//可以是数组
$query->select('user.id AS user_id,email');//可以是别名
$query->select(["CONCAT(first_name,' ',last_name)AS full_name",'email']);//可以是SQL表达式
- 子查询
- 还可以用addselect选取附加字段
体现了QueryBuilder查询构建器的程序化。
如果没有调用select()方法,那么选择的是" * "
from()方法:
- 指定从哪张表拿数据
$query->from('user');
- 可包含数据库前缀,以及表别名
$query->from(['public.user u','public.post p']);
$query->from('public.user u',public.post p');
- 子查询
where方法:
$status是用户输入的时候,考虑安全性,使用数据绑定的方法。
orderBy()方法:
limit()和offset()方法:
groupBy和having()方法:
join()和union()方法:
indexBy()方法:
使索引数组按照某个元素的顺序排列,例如indexBy('id‘);
构建查询好查询后,执行查询:
5.3 数据库查询总结
(六)后台完善之Post部分——Active生命周期
6.1 创建、修改时间
这是AR类中sava方法的生命周期,程序员能掌握程序走向的是beforeValidate()到beforeSava(),通常重写beforeSava()达到我们的需求。
AR类中有很多方法,这些方法的生命周期不同,Active Record 的生命周期 大致划分为以下几种:
- 实例化生命周期(New Instance Life Cycle)
- 查询数据生命周期(Querying Data Life Cycle)
- 保存数据生命周期(Saving Data Life Cycle)
- 删除数据生命周期(Deleting Data Life Cycle)
- 刷新数据生命周期(Refreshing Data Life Cycle)
我们在models/Post.php
中重写beforeSave()方法以达到新增/修改的日期自动填入:
public function beforeSave($insert)
{
if(parent::beforeSave($insert)) {
if ($insert) { // 第一次创建文章的时候
$this->create_time = time();
$this->update_time = time();
}else{
$this->update_time = time(); //第二次就只自动更新update日期
}
return true; //程序继续走
}
else
{
return false; //程序保存失败
}
}
标签的处理:
6.2 标签次数的修改处理
tag数据表:
文章的标签只有三种操作:
- 新增,各项frequency加1
- 修改,相同tag的frequency不变,改变的考虑加1和减1
- 删除,各项frequency减1
<?php
public static function string2array($tags){
// 正则表达式
return preg_split('/\s*,\s*/',trim($tags),-1,PREG_SPLIT_NO_EMPTY);
}
public static function array2string($tags){
return implode(',',$tags);
}
public static function addTags($tags){
if(empty($tags))return ;
foreach ($tags as $name){
$aTag = Tag::find()->where(['name'=>$name])->one();
$aTagCount = Tag::find()->where(['name'=>$name])->count();
}
if(!$aTagCount){ //这个tag表里一个都没有,新建tag
$tag = new Tag;
$tag->name = $name;
$tag->frequency = 1;
$tag->save();
}else{
$aTag->frequency += 1;
$aTag->save();
}
}
public static function removeTags($tags){
if(empty($tags))return ;
foreach ($tags as $name){
$aTag = Tag::find()->where(['name'=>$name])->one();
$aTagCount = Tag::find()->where(['name'=>$name])->count();
}
if($aTagCount){ //表里存在要删除的tag
if($aTag->frequency<=1){
$aTag->delete();
}else{
$tags->frequency -= 1;
$aTag->save();
}
}
}
public static function updateFrequency($oldTags,$newTags){
if(!empty($oldTags) || !empty($newTags)){
$oldTagsArray = self::string2array($oldTags);
$newTagsArray = self::string2array($newTags);
self::removeTags(array_values(array_diff($newTagsArray,$oldTagsArray)));
self::addTags(array_values(array_diff($newTagsArray,$oldTagsArray)));
}
}
public function afterFind()
{
parent::afterFind(); // TODO: Change the autogenerated stub
$this->_oldtags = $this->tags;
}
public function afterSave($insert, $changedAttributes)
{
parent::afterSave($insert, $changedAttributes); // TODO: Change the autogenerated stub
Tag::updateFrequency($this->_oldtags,$this->tags);
}
public function afterDelete()
{
parent::afterDelete(); // TODO: Change the autogenerated stub
Tag::updateFrequency($this->tags,'');
}
}
方法如何根据生命周期执行:
新增标签:在新建文章后执行,afterSave()后调用updateFrequency(’ ',$ this->tags),参数中,$oldTags为空,即为新增。
删除标签:在删除文章后执行,afterDelete()后调用updateFrequency($ this->tags,’ '),参数中$newTags为空,
修改标签:在修改文章时执行,afterFind()后调用为了保存原来的标签有哪些,$ this->_oldTags=$ this->tags; ,在afterSave()后调用,进行修改updateFrequency($ this->_oldTags,$ this->tgas);
6.3 GridView, DataProvider
还需要做的修改:
什么是GridView数据小部件:
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'title',
'author_id',
//'content:ntext',
'tags:ntext',
'status',
//'create_time:datetime',
'update_time:datetime',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
效果如图:
GridView的列类由四个部分组成:
- 序号列
- 数据列
- 动作列
- 复选框列
数据列里的title其实是简写了
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
//['class' => 'yii\grid\SerialColumn'], 序列号,多余的
//'id',
['attribute'=>'id',
'contentOptions'=>['width'=>'30px'],
],
'title',
//'author_id',
['attribute'=>'author_id',
'value'=>'author.nickname',
],
//'content:ntext',
'tags:ntext',
//'status',
['attribute'=>'status',
'value'=>'status0.name',
'filter'=>\common\models\Poststatus::find()
->select(['name','id'])
->orderBy('position')
->indexBy('id')
->column(),
],
//'create_time:datetime',
//'update_time:datetime',
['attribute'=>'update_time',
'format'=>['date','php:Y-m-d H:i:s'],
],
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
效果如图:
什么是数据提供者dataProvider?
第一类:以查询构建器的方式从数据库取数据
第二类:以SQL语句的方式从数据库取数据
第三类:数组来提供数组
设置分页和可以排序的属性,和排序的规则,效果如图:
6.3 数据库搜索类PostSearch
一个AR类对应一个数据库表,属性对应的是表的字段,实例化出来的对象是一条记录。