Yii2 (opens new window) 是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用。 因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用,如门户网站、社区、内容管理系统(CMS)、电子商务项目和 RESTful Web 服务等。

这篇文章主要记录在使用 Yii2 的过程中碰到的一些坑。

# 下载安装

官网给出了两种安装 Yii2 的方法,一种是通过 composer 来安装,但是这种方法在安装的时候特别慢,我自己的电脑就没安装成功;另一种方法是通过归档文件来安装,这种方法比较省事,建议采用这种方式,把时间花在学习上,而不是花费大量时间在环境安装上。

(1)首先从 yiiframework.com (opens new window) 上下载 basic 项目,记住不要下载 advanced 项目

yii

(2)下载完之后把 basic 项目放到 xampp 的 htdocs 目录中

(3)打开 basic 项目,修改 config/web.php 文件,给 cookieValidationKey 配置项添加一个密钥,密钥值随意就行了

(4)运行命令 php yii serve,然后在浏览器中访问 localhost:8080 就可以访问到 yii 项目了。

yii

# 连接数据库

在连接数据库时,遇到了以下问题:

yii

解决方法是修改 config/db.php 文件里的配置,把 localhost 改成 127.0.0.1 就可以了。

<?php

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=localhost;dbname=yii2basic',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',

    // Schema cache options (for production environment)
    //'enableSchemaCache' => true,
    //'schemaCacheDuration' => 60,
    //'schemaCache' => 'cache',
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14

改成:

<?php

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=127.0.0.1;dbname=yii2basic',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',

    // Schema cache options (for production environment)
    //'enableSchemaCache' => true,
    //'schemaCacheDuration' => 60,
    //'schemaCache' => 'cache',
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14

重新访问就没问题了。

yii

# Gii 生成代码

  1. 在使用 Model Generator 时,需要先在数据库中建好一张表,比如叫:books,再去填写 Table Name 的值,也叫 books,否则无法生成,会提示所填写的表不存在。数据库设计的表如下:

yii

  1. model 和 controller 命名时一定要采用大驼峰式,不要用 TBooks 这种形式命名,不然生成之后会找不到页面路径,报404。

用 gii 生成一个图书管理系统,生成成功后的界面如下:

yii

  1. 生成出来的表头都是英文的,如果我们想把它改成中文的,直接在 Books.php 文件里改就行了。

yii

修改后效果如下:

yii

# 返回 JSON 数据给 Vue3 项目

  1. 首先需要将对应控制器里的 actionIndex 的返回结果改成如下形式。
public function actionIndex()
{
    $searchModel = new UserSearch();
    $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

    // return $this->render('index', [
    //     'searchModel' => $searchModel,
    //     'dataProvider' => $dataProvider,
    // ]);

    YII::$app->response->format = Response::FORMAT_JSON;
    return $dataProvider->getModels();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 将 yii 的项目放到 xampp 的 htdocs 中,然后直接在浏览器中访问 http://localhost/basic/web/index.php?r=user/index 就可以看到数据返回了。

yii

注意

这里不要用 php yii serve 的方式去启动服务访问接口,因为用这种方式访问接口的话,vue 代理转发会有问题,解决方法不得而知。这是一个很坑的地方!!

  1. 在 vue3 项目中新建 vue.config.js 文件,并添加代理转发。
module.exports = {
  devServer: {
    port: '8081',
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    proxy: {
      '/api': {
        target: 'http://localhost',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        },
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

然后这么调用:

import axios from 'axios';

axios.get('/api/basic/web/index.php?r=user/index').then((res) => {
  // ...
})
1
2
3
4
5

自定义数据返回格式

如果想自定义返回数据的格式,并添加分页和查询功能,可以这么写:

use Yii;
use yii\web\Response;
use yii\data\Pagination;

// 根据条件查询
public function searchUser($allData)
{
  $params = Yii::$app->request->get();
  $data = $allData;
  if ($params['name']) {
    $data = $allData->where(['name' => $params['name']]);
  }
  if ($params['phone']) {
    $data = $allData->where(['phone' => $params['phone']]);
  }
  if ($params['name'] && $params['phone']) {
    $data = $allData->where(['name' => $params['name'],'phone' => $params['phone']]);
  }
  return $data;
}

public function actionIndex()
{
  $allData = (new \yii\db\Query())->from('user');
  $data = $this->searchUser($allData);
  $total = $data->count();
  $pageNumber = Yii::$app->request->get('pageNumber');
  $pageSize = Yii::$app->request->get('pageSize');

  // 使用总数来创建一个分页对象
  $pagination = new Pagination([
    'page' => $pageNumber - 1,
    'defaultPageSize' => $pageSize,
    'totalCount' => $total
  ]);

  $models = $data->offset($pagination->offset)
    ->limit($pagination->limit)
    ->all();

  return Yii::createObject([
    'class' => Response::className(),
    'format' => Response::FORMAT_JSON,
    'data' => [
      'code' => Yii::$app->response->statusCode,
      'success' => true,
      'data' => [
        'list' => $models,
        'pageNumber' => $pageNumber,
        'pageSize' => $pageSize,
        'total' => (int)$total,
      ],
    ],
  ]);   
}
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

修改后接口的访问路径为:http://localhost/basic/web/index.php?r=user/index&name=&phone=,得到的数据格式如下:

yii

# 关闭 CSRF 验证

有时候当我们请求 yii 接口时,会拒绝访问,这是因为 yii 默认开启了 csrf 验证,我们需要关闭它。

关闭 csrf 的方式有两种:

  • 局部关闭

在当前的控制器中关闭 csrf 验证,只对当前接口起作用。

class UserController extends Controller
{
  // 关闭 csrf 验证
  public $enableCsrfValidation = false;

  //...
}
1
2
3
4
5
6
7
  • 全局关闭

在 web.php 中全局关闭 csrf 验证,全部接口生效,不过一般不建议这么做。

$config = [
  // ...
  'components' => [
    'request' => [
      'cookieValidationKey' => 'sdadadaa',
      'enableCsrfValidation' => false, // 关闭 csrf 验证
    ]
    // ...
  ]
]
1
2
3
4
5
6
7
8
9
10

# basic/web/assets 文件夹拒绝读写访问

有的时候还会出现 basic/web/assets 文件夹拒绝读写访问的问题,此时我们需要先进入到 basic/web 目录下,然后执行以下命令修改 assets 文件夹的权限,就可以正常访问了。

chmod 777 assets
1

# 接收 post 请求参数并保存到数据库

注意,前端使用 axios 发送请求时,需要将参数放在一个 FormData 对象中,yii 接口才能接收的到。

const params = new FormData();
params.append('name', formData.name);
params.append('age', formData.age);
params.append('worktime', formData.worktime);
params.append('phone', formData.phone);
params.append('city', formData.city);
axios
  .post('/api/basic/web/index.php?r=user/create', params)
  .then((res) => {
    // ...
  })
1
2
3
4
5
6
7
8
9
10
11
use app\models\User;

public function actionCreate()
{
  $model = new User();
  // 获取请求参数
  $model->name = Yii::$app->request->post('name');
  $model->age = Yii::$app->request->post('age');
  $model->worktime = Yii::$app->request->post('worktime');
  $model->phone = Yii::$app->request->post('phone');
  $model->city = Yii::$app->request->post('city');
  // 保存数据
  $model->save();

  return Yii::createObject([
    'class' => Response::className(),
    'format' => Response::FORMAT_JSON,
    'data' => [
      'code' => Yii::$app->response->statusCode,
      'success' => true,
    ],
  ]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 编辑时数据库插入数据问题

在编辑保存的时候,报了下面这个错:

yii

这是因为对于相同主键(id)的数据,不能使用 INSERT INTO 命令重复插入,需要改一下命令。

在 basic 项目中全局搜索 INSERT INTO,可以找到 basic/vendor/yiisoft/yii2/db/QueryBuilder.php 文件中的 insert 方法,将其中的插入命令改成 REPLACE INTO,这条命令意味着当插入的记录遇到主键或者唯一重复时先删除表中重复的记录行再插入

yii

# MVC 知识

采用 MVC 模式写出来的东西是一个单体应用,就是前后端的东西都杂糅在一个项目里,前后端不分离。

# MVC 模式关系图

M 是 model,也就是数据。V 是 view,也就是视图,但是要注意 view 不是界面,它是帮助你去生成界面的,跟界面相关的东西归 view 去管。C 是 controller,也就是控制器,说白了就是路由,但是比路由复杂多了。

🔔 其实用户 User 也是 MVC 模式中很重要的一环,但是网上很多资料都没有把用户这一层体现出来,那是不完整的。需要用户参与进来才是完整的。

mvc

🔔 程序的本质就是数据结构加算法。数据永远是核心东西,算法就是用来处理数据的,它不可能独立存在。在这种模式中,model 正好是来处理数据的,数据最重要,因此这种模式叫 MVC 模式,而不是叫 CMV、VCM 或者别的。

# MVC 模式数据流向图

mvc

🔔 将数据写入数据库的操作叫作数据持久化。最开始数据是存储内存条中的,但是数据量一大的时候就会导致内存不足的情况,而且计算机一停电,存在内存条中的数据就没了,因此内存还有另外一种叫法:易失性存储器。后来又把数据存在了磁盘上,磁盘可以永久保存数据,但是用磁盘去管理数据并不方便,问题也多,所以就出现了数据库,专门把所有的数据集中起来,方便维护和管理,也能够将数据永久的保存下来。

🔔 前后端半分离就是指数据在浏览器端进行渲染,不是在服务器端进行渲染,比如用 ajax 就是一种前后端半分离。

🔔 如果采用前后端完全分离,就要用到 node.js 了。后端只负责业务逻辑。

🔔 一个系统或者项目最值钱的东西是流程机制。机制就是遇到哪种情况如何去处理。我们真正要学习的应该是这些东西,而不仅仅是框架语法、api。

上次更新时间: 2021年11月30日 16:37:18