前言
MVC设计模式是一个经典的设计模式。大多数框架都已mvc为基本架构。Model主要负责处理数据,可以理解为与数据库打交道。
Controller,主要处理view传来的请求,向model请求数据,并将请求的数据传给view。View,主要将用户所需的请求传给控制器、接收controller传来的数据,并在页面内渲染。
按照本人的理解,M负责数据,C负责业务逻辑,V负责页面交互。
这一节内容,通过一个简单的场景(博客)来主要梳理yii2框架的模型,重点说CURD操作(即create、update、read、delete)和关联模型,并会简单的说下控制器。
本文依旧先梳理思路,掌握整体结构,再一个个梳理知识点,建议结合文档看,list部分重点列举了用法,部分解释不清楚的参考文档。
One:Steps
场景
一个博客需要作者和文章内容。作者可以在后台管理自己的文章内容(增删改查),用户可以在前台看到作者的文章和并对文章评论。
step one tables
简单的分析以下需求,我们建立的数据库如下。大家可利用上一节学习的内容,使用迁移来建立自己的数据库。记得修改相关数据库配置。
用户表user
Columns |
Type |
Length |
Allow Null |
Default |
Comment |
id |
int |
11 |
no |
|
用户id |
username |
varchar |
255 |
no |
|
用户名 |
password |
varchar |
255 |
no |
|
密码 |
group |
int |
3 |
no |
1 |
分组 |
email |
varchar |
255 |
no |
|
邮箱 |
created_at |
bigint |
20 |
no |
|
创建时间 |
updated_at |
bigint |
20 |
yes |
|
更新时间 |
文章表post
Columns |
Type |
Length |
Allow Nul |
Default |
Comment |
id |
int |
11 |
no |
|
文章id |
author_id |
int |
11 |
no |
|
作者id |
title |
varchar |
255 |
no |
|
文章标题 |
body |
text |
|
no |
|
文章内容 |
created_at |
bigint |
20 |
no |
|
创建时间 |
updated_at |
bigint |
20 |
yes |
|
更新时间 |
deleted_at |
bigint |
20 |
yes |
|
删除时间 |
评论表comment
Columns |
Type |
Length |
Allow Nul |
Default |
Comment |
id |
int |
11 |
no |
|
评论id |
title_id |
int |
11 |
no |
|
评论的文章id |
user_id |
int |
11 |
no |
|
用户id |
content |
text |
|
no |
|
内容 |
created_at |
bigint |
20 |
no |
|
创建时间 |
updated_at |
bigint |
20 |
yes |
|
更新时间 |
deleted_at |
bigint |
20 |
yes |
|
删除时间 |
steps two 建立model
yii有很多类型的model,我们简单的了解下。
yii\base\Model
,是基本模型,一般情况下会用来处理用户模型操作。它有很多子类。
比如yii\db\ActiveRecord
,这也是我们经常用的高级模型类,进行相关数据库操作,当然它也具备yii\base\Model
所有功能。大多数情况我们主要使用此高级模型类。
想了解更多,可以点击连接。yii\base\Model-api
bankend
和frontend
都可以继承common\models\Post.php
,并把公共部分代码放置于这个文件。具体代码如下:
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 namespace common\models; use yii\db\ActiveRecord; use yii\behaviors\TimestampBehavior; class Post extends ActiveRecord { public static function tableName() { return 'post'; } public function behaviors() { return [ TimestampBehavior::className(), ]; } }
|
backend\models\Post
继承common\models\Post
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
| <?php namespace backend\models; use Yii; use common\models\Post as BasePost; class Post extends BasePost { public function rules() { return [ [['author_id', 'title', 'body'], 'required'], ]; } public function __construct() { $post = Yii::$app->request->post(); $this->load(['Post' => $post]); } * @return bool * 创建一篇文章 */ public function postCreate() { return $this->save(); } * @param $id * @return bool * 更新某篇文章 */ public function postUpdate($id) { $post = self::findPostById($id); foreach ($this->attributes as $key => $value) { $post->$key = $value ? :$post->$key; } return $post->save(); } * @param $id * @return bool * 删除某篇文章 */ public function postDelete($id) { $post = self::findPostById($id); $post->deleted_at = time(); return $post->save(); } * @param $id * @return static * 根据id返回某篇文章 */ public static function findPostById($id) { return Post::findOne(['id' => $id]); } * @return array|\yii\db\ActiveRecord[] * 返回未删除的文章并倒序排列 */ public static function postList() { return self::find()->where(['deleted_at'=>null])->orderBy('id desc')->asArray()->all(); } }
|
steps three 建立controller
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
| <?php * Created by PhpStorm. * User: yuan * Date: 16/10/31 * Time: 14:54 */ namespace backend\controllers; use Yii; use yii\base\Controller; use backend\models\Post; use yii\db\Query; class PostController extends Controller { public function actionIndex() { var_dump(Post::postList()); } public function actionView() { $id = Yii::$app->request->get('id'); var_dump(Post::findPostById($id)->attributes);die(); } public function actionCreate() { $post = new Post(); var_dump($post->postCreate()); } public function actionUpdate() { $id = Yii::$app->request->get('id'); $post = new Post(); var_dump($post->postUpdate($id)); } public function actionDelete() { $id = Yii::$app->request->get('id'); var_dump(Post::postDelete($id)); } }
|
steps four 调试
调试工具推荐:Postman
测试之前,我们美化一下url,在common\config\main.php
中,加入下面一段代码
1 2 3 4 5 6 7 8 9 10
| 'components' => [ 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'suffix'=>'.html', 'rules' => [ 'static/download/<type>/<filename>' => 'static/download' ], ], ],
|
测试路由示例:
- 路由:
yii-blog.backend.com/post/create.html
;方法post
;传入参数:author_id
,title
,body
;返回:true
或者false
;
- 路由:
yii-blog.backend.com/post/index.html
;方法get
;传入参数:;返回:一个数组;
steps five 练习
大家可依照post,完成comment和user相关代码。
Two: List
关联表
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
| class Customer extends \yii\db\ActiveRecord { public function getOrders() { return $this->hasMany(Order::className(), ['customer_id' => 'id']); } } class Order extends \yii\db\ActiveRecord { public function getCustomer() { return $this->hasOne(Customer::className(), ['id' => 'customer_id']); } } class Order extends \yii\db\ActiveRecord { public function getItems() { return $this->hasMany(Item::className(), ['id' => 'item_id']) ->viaTable('order_item', ['order_id' => 'id']); } } 或 class Order extends \yii\db\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'); } }
|
create
1 2 3 4
| $post = new Post(); $post->title = 'title one'; $post->body = 'body'; $post->save();
|
update
1 2 3
| $post = Post::findOne($id); $post->title = 'title change'; $post->save();
|
delete
1 2
| $post = Post::findOne($id); $post->delete();
|
find
- 返回所有数据
$post = Post::find()->all();
- 返回一条author_id的数据:
$post = Post::find()->where(['author_id'=>1])->one();
- 计算author_id为1的纪录条数总和:
$post = Post::find()->where(['author_id'=>1])->count();
- 以author_id为索引结果集,出现多条author_id相同的只会保留其中一条纪录返回:
$post = Post::find()->indexBy('author_id')->all();
- 用原生sql检索:
$sql = 'select * from post';$post = Post::findBySql($sql)->asArray()->all();
- 用原生sql检索:
$sql = 'select * from post';$post = Post::findBySql($sql)->asArray()->one();
- 返回一条id为1的纪录:
$post = Post::findOne(1);
- 返回一条id为1且author_id为1的纪录:
$post = Post::findOne(['id'=>1,'author_id'=>1]);
- 返回id为1、2、3的所有纪录,不能与
asArray()
组合:$post = Post::findAll([1,2,3]);
- 返回author_id为1的所有纪录,不能与
asArray()
组合:$post = Post::findAll(['author_id'=>1]);
select
- 返回所有纪录,但只返回id、author_id、title、bod字段:
$post = Post::find()->select(['id','author_id', 'title', 'body'])->asArray()->all();
- 指定别名: id指定别名post_id,
$post = Post::find()->select(['id AS post_id','author_id', 'title', 'body'])->asArray()->all();
- addSelect: 用法同
select
asArray
以数组形式返回:
$post = Post::find()->select(['id','author_id', 'title', 'body'])->asArray()->all();
distinct
以数组形式返回所有的author_id,并去除重复的author_id:$post = Post::find()->select('author_id')->distinct()->asArray()->all()
where
操作where的三种格式:
- 字符串格式,例如:‘status=1’:
$post = Post::find()->select(['id','author_id', 'title', 'body'])->where('author_id = 1')->asArray()->all();
$post = Post::find()->select(['id','author_id', 'title', 'body'])->where('author_id = :user_id',[':user_id'=>1])->asArray()->all();
- 哈希格式,例如: [‘status’ => 1, ‘type’ => 2]:
$post = Post::find()->select(['id','author_id', 'title', 'body'])->where(['author_id'=>1,'title'=>'title1'])->asArray()->all();
- 操作符格式,例如:[‘like’, ‘name’, ‘test’],如下:
and
1 2 3
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['and', 'author_id=1', ['or', "title='title1'", "title='title3'"]]) ->asArray()->all();
|
or
1 2 3
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['or', "title='title1'", "title='title3'"]) ->asArray()->all();
|
between
1 2 3
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['between','author_id',1,8]) ->asArray()->all();
|
not between
1 2 3
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['not between','author_id',1,8]) ->asArray()->all();
|
in
1 2 3 4 5 6 7
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['in','id',[1,2,3]]) ->asArray()->all(); 或 $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['id' => [1,2,3,4,5,6]]) ->asArray()->all();
|
not in
1 2 3
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['not in','id',[1,2,3,4,5,6,7,8]]) ->asArray()->all();
|
like:模糊查询
1 2 3 4 5 6 7 8 9 10 11
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['like','body',['body','update']]) ->asArray()->all(); 或 $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['like','body','body',]) ->asArray()->all();
|
or like
1 2 3 4
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['or like','title',['1','3']]) ->asArray()->all();
|
not like
1 2 3 4
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['not like','title',['1','2']]) ->asArray()->all();
|
or not like
1 2 3 4
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['or not like','title',['9','title']]) ->asArray()->all();
|
>,>=,<,<=
1 2 3 4 5 6 7 8 9
| $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['<','author_id',3]) ->asArray()->all(); $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where(['>=','author_id',3]) ->asArray()->all();
|
exists
1 2 3 4 5
| $query = Post::find()->where(['author_id'=>2]); $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where( ['exists',$query]) ->asArray()->all();
|
not exists
1 2 3 4 5
| $query = Post::find()->where(['author_id'=>2]); $post = Post::find()->select(['id','author_id', 'title', 'body']) ->where( ['exists',$query]) ->asArray()->all();
|
orderBy:排序
1 2 3 4 5 6 7 8 9 10 11 12
| $post = Post::find() ->orderBy(['id' => SORT_ASC, 'author_id' => SORT_DESC,]) ->all(); $post = Post::find() ->orderBy('id ASC, author_id DESC') ->all(); $post = Post::find() ->orderBy('id ASC') ->addOrderBy('author_id DESC') ->all();
|
having
1 2 3 4 5 6 7
| $post = Post::find() ->select(['COUNT(*) AS count','author_id','body']) ->having(['like','body','update']) ->groupBy('author_id') ->asArray() ->all();
|
groupBy
1 2 3 4 5 6
| $post = Post::find() ->select(['COUNT(*) AS count','author_id']) ->groupBy('author_id') ->asArray() ->all();
|
$post = Post::find()->groupBy('author_id')->asArray()->all();
判断id为2的纪录是否存在,存在返回true,不存在返回false:
$post = Post::find()->where( [ 'id' => 2 ] )->exists();
addXXX()
addXXX(),这种写法,一般作为条件补充。用法不多说,同上。
- addHaving()
- addSelect()
- addParams()
- addOrderBy()
- addGroupBy()
filterWhere
过滤用户输入的空值,比如用户登陆时输入的用户名或者email,如果值为空,则默认不作为查询条件。
1 2 3 4
| User::find()->filterWhere([ 'username' => $username, 'email' => $email, ]);
|
andFilterCompare(‘column_name’,‘value’,’$defaultOperator = ‘=’ ')
为特定列添加过滤条件,并允许用户选择过滤器运算符.如<
,>
,>=
,<=
,<>
,=
1 2 3 4
| $post = Post::find() ->andFilterCompare('author_id','4','<') ->asArray() ->all();
|
limit offset
limit 限制返回的条数,可以用空值或者负值禁用。
offset 比如返回10条数据,偏移量为3,则会返回后面7条数据.
1 2 3 4 5 6 7
| $post = Post::find() ->select(['id','author_id','body']) ->andFilterCompare('author_id','4','<') ->limit(3) ->offset(2) ->asArray() ->all();
|
join
join()
1 2 3 4
| $post = (new Query())->from('post') ->join('right join','user','user.id = post.author_id ') ->all();
|
innerJoin()
post
为左表,user
为右表。只有当左表和右表有对应的数据时,才会显示这条数据。比如,post
必须有author_id=1
且user
有id=1
的数据,才会返回。
leftJoin()
1 2 3
| $post = (new Query())->from('post') ->innerJoin('user','user.id = post.author_id ') ->all();
|
post
为左表,user
为右表。会以左表为中心,即post
,会全部加载左表的内容,如果右表没有匹配项,则右表相应字段值显示为null。
1 2 3
| $post = (new Query())->from('post') ->leftJoin('user','user.id = post.author_id ') ->all();
|
rightJoin()
post
为左表,user
为右表。会以右表为中心,即user
,会全部加载右表的内容,如果左表没有匹配项,则左表相应字段值显示为null。
1 2 3
| $post = (new Query())->from('post') ->rightJoin('user','user.id = post.author_id ') ->all();
|
union()
UNION 常用于数据类似的两张或多张表查询,如不同的数据分类表,或者是数据历史表等。
1 2 3 4 5 6 7 8
| $query_one = (new Query())->from('post') ->andFilterCompare('author_id','4','<='); $query_two = (new Query())->from('user') ->where(['id'=>1]); var_dump($query_two->union($query_one)->all());
|
批处理
yii\db\Query::all()
会把所有数据全部读出来,放到内存中。所以需要处理大数据时,不太适合。为了保持较低的内存占有,使用batch
和each
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $post = Post::find(); foreach($post->each() as $key=>$value) { var_dump($value->author_id); } $post = Post::find(); foreach($post->batch() as $posts) { foreach($posts as $key => $value) { var_dump($value->author_id); } }
|
后记,
后续内容:
- load()
- rules
- scenario
- validate
如有问题可通过以下方式联系我: