深入理解 yii2的Active Record
yii2 中的 $model->attribute() , $model->attributes , $model->attributes= [...], model->fields(), $model->toArray();
以下依次进行剖析:
1.
$this->attributes()
public function attributes() { return array_keys(static::getTableSchema()->columns); }
array (size=14) 0 => string 'id' (length=2) 1 => string 'username' (length=8) 2 => string 'password_hash' (length=13) 3 => string 'password_reset_token' (length=20) 4 => string 'email' (length=5) 5 => string 'auth_key' (length=8) 6 => string 'status' (length=6) 7 => string 'created_at' (length=10) 8 => string 'updated_at' (length=10) 9 => string 'password' (length=8) 10 => string 'role' (length=4) 11 => string 'access_token' (length=12) 12 => string 'allowance' (length=9) 13 => string 'allowance_updated_at' (length=20)
var_dump($this->attributes) 执行的是函数: \yii\base\model->getAttributes(),该函数代码如下
public function getAttributes($names = null, $except = []) { $values = []; if ($names === null) { $names = $this->attributes(); } foreach ($names as $name) { $values[$name] = $this->$name; } foreach ($except as $name) { unset($values[$name]); } return $values; }
然后 依次遍历,查看各个属性,$this->$name获取各个字段对应的值,而这个访问的是对象的属性,是通过魔术方法__get 从private属性 \yii\db\BaseActiveRecord::$_attributes 获取的,也就是说数组的结构是先用数据库字段作为数组的key, value是从数组 \yii\db\BaseActiveRecord::$_attributes中取值
然后拼起来的数组,也就是只有数据库里面的部分,对于model中定义的成员变量和其他的属性,这里是不做输出的。仅仅是数据库的字段部分。
如果您想得到一些定义的成员变量,但是又不想定义fields那么麻烦,您可以通过
$table_attr = $this->attributes(); $public_x = [ 'password_repeat']; $arr = array_merge($table_attr,$public_x ); $model->getAttributes($arr);
public function __get($name) { if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) { return $this->_attributes[$name]; } elseif ($this->hasAttribute($name)) { return null; } else { if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) { return $this->_related[$name]; } $value = parent::__get($name); if ($value instanceof ActiveQueryInterface) { return $this->_related[$name] = $value->findFor($name, $this); } else { return $value; } } }
public function __set($name, $value) { if ($this->hasAttribute($name)) { $this->_attributes[$name] = $value; } else { parent::__set($name, $value); } }
yii\base\model->setAttributes($values, $safeOnly = true);
代码如下:
public function setAttributes($values, $safeOnly = true) { if (is_array($values)) { $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes()); foreach ($values as $name => $value) { if (isset($attributes[$name])) { $this->$name = $value; } elseif ($safeOnly) { $this->onUnsafeAttribute($name, $value); } } } }
原理就是 将
1.过滤出来场景允许的安全属性,然后进行赋值
2.如果存在成员变量,那么首先进行成员变量的赋值。
如果成员变量不存在,则会使用到__set魔兽方法,添加到 yii\db\BaseActiveRecord 的private属性$_attributes 数组中
因此,如果在数据库中有一个字段password,如果你在AR类中也定义了一个password,那么就不会保存到 private属性$_attributes 数组中
当然,这个不会影响到保存,因为$model->attributes 是通过 $this->$name 的方式读取,而不是通过 private属性$_attributes 数组读取,
通过 $this->$name 的方式读取 ,定义的成员变量也是会保存到数据库中的
造成的后果就是 private属性$_attributes 数组 没有password这个key,fields函数返回就没有password,进而toArray()返回的也没有password。
public function fields() { $fields = array_keys($this->_attributes); return array_combine($fields, $fields); }
这个函数的输出,是将 fields 返回的数组输出,从第4部分,可以看到,fields默认是由 yii\db\BaseActiveRecord 的private属性$_attributes 的key得到
所以,toArray默认是将$_attributes的值得出,如果想在这里添加类的成员变量,可以fields函数中添加:
public function fields() { $fields = parent::fields(); $fields['password_repeat'] = 'password_repeat'; //$fields['rememberMe'] = function ($model) { // return $model->rememberMe . ' ' . $model->password_repeat; // }; return $fields; }
如果想自定义,可以通过函数的方式获取。
6.实践
当findOne得到一个AR实例后
$_attributes 里面保存的是所有的属性public function __set($name, $value) { if ($this->hasAttribute($name)) { $this->_attributes[$name] = $value; } else { parent::__set($name, $value); } } public function __get($name) { if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) { return $this->_attributes[$name]; } elseif ($this->hasAttribute($name)) { return null; } else { if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) { return $this->_related[$name]; } $value = parent::__get($name); if ($value instanceof ActiveQueryInterface) { return $this->_related[$name] = $value->findFor($name, $this); } else { return $value; } } }
当然,如果在model中进行了定义新字段,
public $password_repeat;public function fields() { $fields = parent::fields(); $fields['password_repeat'] = 'password_repeat'; //$fields['rememberMe'] = function ($model) { // return $model->rememberMe . ' ' . $model->password_repeat; // }; return $fields; }
对于new的新的AR对象
$model = new Useradmin(); $model->attributes = $this->getValues1(); public function getValues1(){ return [ 'username' => 'terry', 'password' => 'passfdafds', //'password' => 'passfdafds', 'password_repeat' => 'passfdafds', 'email' => 'email', 'created_at' => '2014-09-09 11:11:11', 'access_token' => null, 'rememberMe' => 44, ]; }
$model->attributes = $this->getValues1(); 这个是块赋值,执行的是setAttributes方法,先通 场景 scrnarios函数 验证是否是安全属性,
如果是,则把数据插入到 private $_attributes中
var_dump($model->attributes());
var_dump($model->toArray());
var_dump($model->attributes);
依次输出
array (size=14) 0 => string 'id' (length=2) 1 => string 'username' (length=8) 2 => string 'password_hash' (length=13) 3 => string 'password_reset_token' (length=20) 4 => string 'email' (length=5) 5 => string 'auth_key' (length=8) 6 => string 'status' (length=6) 7 => string 'created_at' (length=10) 8 => string 'updated_at' (length=10) 9 => string 'password' (length=8) 10 => string 'role' (length=4) 11 => string 'access_token' (length=12) 12 => string 'allowance' (length=9) 13 => string 'allowance_updated_at' (length=20) array (size=6) 'username' => string 'terry' (length=5) 'password' => string 'passfdafds' (length=10) 'email' => string 'email' (length=5) 'created_at' => string '2014-09-09 11:11:11' (length=19) 'access_token' => null 'password_repeat' => string 'passfdafds' (length=10) array (size=14) 'id' => null 'username' => string 'terry' (length=5) 'password_hash' => null 'password_reset_token' => null 'email' => string 'email' (length=5) 'auth_key' => null 'status' => null 'created_at' => string '2014-09-09 11:11:11' (length=19) 'updated_at' => null 'password' => string 'passfdafds' (length=10) 'role' => null 'access_token' => null 'allowance' => null 'allowance_updated_at' => null因为rememberMe不是安全属性,所以块赋值失败
$model->attributes() 返回的是数据库中所有的字段数组
$model->toArray()返回的是 $_attributes 属性数组
7
对于model的foreach
foreach($model as $k=>$v){ echo $k."=>".$v."<br/>"; }
对应 model的其他的成员变量是不输出的。需要通过
$model->xxxx调用