美文网首页
laravel 对象以数组方式访问

laravel 对象以数组方式访问

作者: 小东班吉 | 来源:发表于2019-07-22 16:09 被阅读0次

    laravel 对象以数组方式访问

    laravle ORM查询出来的数据是一个对象,但支持我们以数组的方式访问,这里其实是继承了php的一个与定义接口ArrayAccess

    之前遇到过一个问题,举例如下:

    $test = [
        'postion' => 1,
    ];
    $oder_info = Order::whereBuyerId(133499)->first();
    $oder_info['test'] = $test;
    $oder_info['test']['postion'] = 2;
    var_dump($order_info);
    

    这个时候会抛一个异常

    Indirect modification of overloaded element of App\Models\Order has no effect
    

    但如果不修改test数组的值,就没问题,那么问题来了,这是为什么呢?

    Model基础的类继承了ArrayAccess接口\Illuminate\Database\Eloquent\Model。

    ArrayAccess {
    /* 方法 */
    abstract public offsetExists ( mixed $offset ) : boolean
    abstract public offsetGet ( mixed $offset ) : mixed
    abstract public offsetSet ( mixed $offset , mixed $value ) : void
    abstract public offsetUnset ( mixed $offset ) : void
    }
    

    \Illuminate\Database\Eloquent\Model类对这几个方法进行了重写,而我们上面的例子,对$order_info对象进行赋值就等于调用了offsetSet这个方法。

    public function offsetSet($offset, $value)
    {
        $this->setAttribute($offset, $value);
    }
    
    public function setAttribute($key, $value)
    {
        // First we will check for the presence of a mutator for the set operation
      // which simply lets the developers tweak the attribute as it is set on
      // the model, such as "json_encoding" an listing of data for storage.
      if ($this->hasSetMutator($key)) {
        return $this->setMutatedAttributeValue($key, $value);
      }
    
      // If an attribute is listed as a "date", we'll convert it from a DateTime
      // instance into a form proper for storage on the database tables using
      // the connection grammar's date format. We will auto set the values.
      elseif ($value && $this->isDateAttribute($key)) {
        $value = $this->fromDateTime($value);
      }
    
      if ($this->isJsonCastable($key) && ! is_null($value)) {
        $value = $this->castAttributeAsJson($key, $value);
      }
    
      // If this attribute contains a JSON ->, we'll set the proper value in the
      // attribute's underlying array. This takes care of properly nesting an
      // attribute in the array's value in the case of deeply nested items.
      if (Str::contains($key, '->')) {
        return $this->fillJsonAttribute($key, $value);
      }
      $this->attributes[$key] = $value;
      return $this;
    }
    

    由上面代码可知道,赋值的操作其实等于是给了\Illuminate\Database\Eloquent\Concerns\HasAttributes类的一个attributes属性,属性的名称就是我们定义的key,当访问的时候就从attributes属性里取出来,参考\Illuminate\Database\Eloquent\Concerns\HasAttributes::getAttribute方法。要修改数组里面的值除非对属性赋值为引用传递。而这其实是由php的重载机制实现的,PHP的重载和其他语言不一样,传统的重载是用于提供多个同名的类方法,但各方法的参数类型和个数不同,是由__set, __get等魔术方法实现的,魔术方法的参数是不能够引用传递。

    class PropertyTest
    {
        /**  被重载的数据保存在此  */
        private $data = array();
    
    
        /**  重载不能被用在已经定义的属性  */
        public $declared = 1;
    
        /**  只有从类外部访问这个属性时,重载才会发生 */
        private $hidden = 2;
    
        public function __set($name, $value)
        {
            echo "Setting '$name' to '$value'\n";
            $this->data[$name] = $value;
        }
    
        public function __get($name)
        {
            echo "Getting '$name'\n";
            if (array_key_exists($name, $this->data)) {
                return $this->data[$name];
            }
    
            $trace = debug_backtrace();
            trigger_error(
                'Undefined property via __get(): ' . $name .
                ' in ' . $trace[0]['file'] .
                ' on line ' . $trace[0]['line'],
                E_USER_NOTICE);
            return null;
        }
    }
    
    $obj = new PropertyTest;
    
    $obj->a = ['value' => 1];
    var_dump($obj->a);
    $obj->a['value'] = 2;
    var_dump($obj->a['value']);
    

    我们可以看到,其实 $obj->a['value'] = 2;其实是访问了__get方法,并没有赋值。

    相关文章

      网友评论

          本文标题:laravel 对象以数组方式访问

          本文链接:https://www.haomeiwen.com/subject/nkuwlctx.html