Laravel 动态属性的实现 
                                                    
                        
                    
                    
  
                    
                    什么是动态属性
假设我们有一个 User 模型:
class User extends Model
{
    /**
     * 获取与用户关联的电话号码记录。
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
    public function getFullNameAttribute()
    {
        return "{$this->first_name} {$this->last_name}";
    }
}$user = User::find(1);
// 这种调用不存在的属性就是 laravel 中的动态属性
$user->full_name;
$user->phone动态属性的实现
1. 万恶之源 __get()
PHP 的魔术方法 __get(),当读取不可访问属性的值时,__get()会被调用。我们在在使用动态属性时
都会触发这个魔术方法。然后我顺利的在 Eloquent\Model.php 中找到了这个这个方法:
/**
* Dynamically retrieve attributes on the model.
*
* @param  string  $key
* @return mixed
*/
public function __get($key)
{
    return $this->getAttribute($key);
}2. getAttribute
继续追踪:
/**
     * Get an attribute from the model.
     *
     * @param  string  $key
     * @return mixed
     */
    public function getAttribute($key)
    {
        if (! $key) {
            return;
        }
        // If the attribute exists in the attribute array or has a "get" mutator we will
        // get the attribute's value. Otherwise, we will proceed as if the developers
        // are asking for a relationship's value. This covers both types of values.
        if (array_key_exists($key, $this->attributes) ||
            $this->hasGetMutator($key)) {
            return $this->getAttributeValue($key);
        }
        // Here we will determine if the model base class itself contains this given key
        // since we don't want to treat any of those methods as relationships because
        // they are all intended as helper methods and none of these are relations.
        if (method_exists(self::class, $key)) {
            return;
        }
        return $this->getRelationValue($key);
    }第二个 if 处理了model有这个attribute和model有对应访问器的情况。  
3. getRelationValue
继续追踪, 关联关系的动态属性用法:
/**
    * Get a relationship.
    *
    * @param  string  $key
    * @return mixed
    */
public function getRelationValue($key)
{
    // If the key already exists in the relationships array, it just means the
    // relationship has already been loaded, so we'll just return it out of
    // here because there is no need to query within the relations twice.
    if ($this->relationLoaded($key)) {
        return $this->relations[$key];
    }
    // If the "attribute" exists as a method on the model, we will just assume
    // it is a relationship and will load and return results from the query
    // and hydrate the relationship's value on the "relationships" array.
    if (method_exists($this, $key)) {
        return $this->getRelationshipFromMethod($key);
    }
}这里可以看到首先会读一个关联关系的缓存,若没有缓存才会判断是否存在和所调用属性同名的方法,
如果存在则调用 getRelationshipFromMethod($key) 方法。
4. getRelationshipFromMethod($method)
protected function getRelationshipFromMethod($method)
    {
        $relations = $this->$method();
        if (! $relations instanceof Relation) {
            throw new LogicException('Relationship method must return an object of type '
                .'Illuminate\Database\Eloquent\Relations\Relation');
        }
        $this->setRelation($method, $results = $relations->getResults());
        return $results;
    }setRelation 的意思是将没有加载的 relation 进行加载,那么下次需要时就可以在getRelationValue($key)的第一个 if 中即返回需要的结果。
此方法的返回值返回的是 Collection 类型,也就是说动态属性访问关联模型返回的是Collection类型,而如果我们直接调用方法返回的则是 Relation 类型。
本作品采用《CC 协议》,转载必须注明作者和本文链接
 
           KevinYang 的个人博客
 KevinYang 的个人博客
         
             
             
             
             
             
                     
                     
             
             
             
             
           
           关于 LearnKu
                关于 LearnKu
               
                     
                     
                     粤公网安备 44030502004330号
 粤公网安备 44030502004330号 
 
推荐文章: