YII框架学习——有关ActiveRecord的一些坑

在这里我想总结一下我学习YII框架的过程中,踩过的那些坑。希望能给YII框架的初学者一些帮助,使大家能更快地熟悉YII框架。

1.显式地指明表名。
在使用ActiveRecord类时,你一般会去继承yii\db\ActiveRecord这个基类,在models文件夹中去创建一个自己的类,如下:

1
2
3
4
5
6
7
namespace app\models;
use yii\db\ActiveRecord;
class Customer extends ActiveRecord
{
}

一般在文档里会说,yii会根据类名自动去猜测表名(如以上代码,yii便会自动去关联customer表)。但是这个并不是一直都有效,所以最稳妥的办法还是指明表名,像下面这样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace app\models;
use yii\db\ActiveRecord;
class Customer extends ActiveRecord
{
/**
* @return string 返回该AR类关联的数据表名
*/
public static function tableName()
{
return 'customer';
}
}

2.在你继承自yii\db\ActiveRecord的类里面不要去声明和相对应表里面的字段同名的公有属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
namespace app\models;
use yii\db\ActiveRecord;
class Country extends ActiveRecord {
public $name;
public $population;
public function rules(){
return [
[['name', 'population'], 'required'],
];
}
public static function tableName()
{
return 'country';
}
}

在上面的代码中,我在Country类里面申明了2个公有属性name和population,我的country表里有3个字段,分别是自增主键id,name和population。

  • 当你通过$country->load(Yii::$app->request->post())的方式接受数据,然后调用$country->save()的时候,你会发现保存成功了,但是name和population字段都没有插入成功。然后我便尝试着在load数据之后打印了下$country对象。如下:
1
2
3
4
5
6
7
8
9
10
11
12
object(app\models\Country)#55 (10) {
["name"]=>
string(3) "France"
["population"]=>
string(4) "18886000"
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(1) {
["id"]=>
int(47)
}
...//多余的省略
}

可以看到其公有属性被成功赋值了,而其私有属性_attributes是一个数组,里面有一个元素id,值为47,查询数据库发现正是刚插入的那一行数据的id,而name和population字段都未插入成功。

  • 之后我将Country类里的两个公有属性删除后,继续执行上述操作,便插入成功了,我也将$country对象打印了出来,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace app\models;
use yii\db\ActiveRecord;
class Country extends ActiveRecord {
public function rules(){
return [
[['name', 'population'], 'required'],
];
}
public static function tableName()
{
return 'country';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
object(app\models\Country)#55 (10) {
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(3) {
["name"]=>
string(3) "France"
["population"]=>
string(4) "18886000"
["id"]=>
int(48)
}
...//多余的省略
}

可以看到这回name和population的值被填充进了私有属性_attributes中。所以由此我推断,当调用$country->save()方法时,会从_attributes数组中取出key-value对存入数据库对应字段。取数据时也是同理,取出来的字段也是自动填充进_attributes数组。

3.当使用model->load(Yii::$app->request->post())时,如何传递post数据

我用postman(模拟http请求的chrome扩展)模拟传递数据,下面是接受数据的方式。

1
2
3
$country = new Country();
$country->load(Yii::$app->request->post());
var_dump($country);

我在Country类里声明了如下rules:

1
2
3
4
5
6
public function rules()
{
return [
[['name', 'population'], 'required'],
];
}

当我用postman模拟post方式在form data里面传递了name和population2个参数时,将$country对象打印出来:

1
2
3
4
5
6
object(app\models\Country)#65 (8) {
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
...//多余的省略
}

可以看到其私有变量_attributes并没有被填充进数据。

阅读Model->load()的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function load($data, $formName = null)
{
$scope = $formName === null ? $this->formName() : $formName;
if ($scope === '' && !empty($data)) {
$this->setAttributes($data);
return true;
} elseif (isset($data[$scope])) {
$this->setAttributes($data[$scope]);
return true;
} else {
return false;
}
}

发现$scope变量为相对应的表名,然后isset($data[$scope])查看的是以对应表名为key的数组,$this->setAttributes($data[$scope])是将对应表名的数组里的key-value对填充进私有属性_attributes。

所以当你模拟post传参时,要传递Country[name]和Country[population],这样才能被load()方法正确接收。修改传参后再次打印$country对象:

1
2
3
4
5
6
7
8
9
10
object(app\models\Country)#65 (8) {
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(2) {
["name"]=>
string(3) "aaa"
["population"]=>
string(5) "34343"
}
...//多余的省略
}

当然你也可以不用load()方式接受数据,你也可以通过以下2种方式进行赋值。
(1)通过批量赋值的方式:
$country->attributes = Yii::$app->request->post();
(2)逐个赋值的方式:
$name = Yii::$app->request->post('name'); $country->name = $name;

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器