ThinkPHP5 select出來的結果是個對象?居然還可以以數組形式訪問數據?

目錄

 

  • 目錄
  • 前言
  • TP5中的select方法
  • 爲什麼可以這麼操作
  • 總結
  • 思考

 

前言

  在使用TP5的過程中,我們會發現,使用select方法查詢數據庫中的數據後dump出來的結果是對應模型的對象。其中該對象有一個protected的data屬性。而在TP3中select出來的結果返回的是一個數組。那麼我們在使用TP5中select後得到了對應的結果——一個對象,怎麼通過這個對象獲取結果數據呢?
  結果是同樣可以以數組的方式獲取數據。同TP3的操作。在模板輸出中,同樣也使用。那爲什麼呢?明明這個是一個對象不是什麼數組呀,怎麼可以以數組的方式訪問結果集呢額?
  參考:PHP中__get()和__set()的用法實例詳解
     PHP實現對象屬性按數組方式訪問

TP5中的select方法

  【栗子】調用model創建Goods對應的模型,並使用select方法查詢數據,將查詢結果dump出來,如下:

public function test() {
        dump(model('Goods')->select());
    }
 

  運行結果如下:
TP5中的select方法返回結果
  這裏可以看到它返回了一個數組,每個元素的值是一個Goods對象(它繼承於Model類)。可以發現其protected的data屬性保存着我們想要的結果集。我們獲取結果集中的內容,同操作數組無異。如:

public function test() {
        //dump(model('Goods')->select());
        $res = model('Goods')->select();
        $goods = $res[0];
        echo 'id:' . $goods['id'] . '<br/>';
        echo 'name:' . $goods['name'] . '<br/>';
        echo 'brief:' . $goods['brief'] . '<br/>';
    }
 

  運行結果如下:
TP5操作select方法返回的對象

爲什麼可以這麼操作

  既然select方法返回的對象繼承於Model類,那其中的玄機一定來自這個Model類。
  (可以先閱讀一下,我上面提及的參考文正。)
  首先,我們應該考慮,爲什麼一個對象可以以數組的形式訪問。(要是一般情況下,這樣去操作一個對象會報錯的。)
  扣一個Model類的源碼,我們會發現Model實現了ArrayAccess接口,恰恰這就是奇妙的地方所在。實現了該接口,並實現了對象的抽象方法,那麼對象就可以以數組形式訪問對象內保存的某些數據。
  Model類型實現ArrayAccess接口中的方法,如下:

    // ArrayAccess
    public function offsetSet($name, $value)
    {
        $this->setAttr($name, $value);
    }

    public function offsetExists($name)
    {
        return $this->__isset($name);
    }

    public function offsetUnset($name)
    {
        $this->__unset($name);
    }

    public function offsetGet($name)
    {
        return $this->getAttr($name);
    }
 

  我們這裏先只考慮獲取屬性值,那麼offsetGet方法就實現了 obj[′key′]獲取值的功能。offsetGet方法中的參數obj[′key′]獲取值的功能。offsetGet方法中的參數name,即以數組操作時傳入的鍵的值。
  getArr的實現如下:

/**
     * 獲取器 獲取數據對象的值
     * @access public
     * @param string $name 名稱
     * @return mixed
     * @throws InvalidArgumentException
     */
    public function getAttr($name)
    {
        try {
            $notFound = false;
            $value    = $this->getData($name);
        } catch (InvalidArgumentException $e) {
            $notFound = true;
            $value    = null;
        }

        // 檢測屬性獲取器
        $method = 'get' . Loader::parseName($name, 1) . 'Attr';
        if (method_exists($this, $method)) {
            $value = $this->$method($value, $this->data);
        } elseif (isset($this->type[$name])) {
            // 類型轉換
            $value = $this->readTransform($value, $this->type[$name]);
        } elseif ($notFound) {
            $method = Loader::parseName($name, 1);
            if (method_exists($this, $method) && !method_exists('\think\Model', $method)) {
                // 不存在該字段 獲取關聯數據
                $value = $this->relation()->getRelation($method);
                // 保存關聯對象值
                $this->data[$name] = $value;
            } else {
                throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
            }
        }
        return $value;
    }
 

  在getAttr方法中中主要獲取值的方法又是getData方法。
  getData方法如下:

/**
     * 獲取對象原始數據 如果不存在指定字段返回false
     * @access public
     * @param string $name 字段名 留空獲取全部
     * @return mixed
     * @throws InvalidArgumentException
     */
    public function getData($name = null)
    {
        if (is_null($name)) {
            return $this->data;
        } elseif (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        } else {
            throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
        }
    }
 

  TMD發現了!!!getData方法返回的便是當前對象data屬性對應的值。

總結

  在這個過程中,最爲關鍵的是Model類實現ArrayAccess接口。通過實現接口的方法,select出來的結果對象便可以通過數組的形式訪問到我們想要的結果集,即對象的protected的data屬性的內容。

思考

  TP5中,在使用繼承於Model的模型對象時,會經常發現,我們會使用到如:$model->id的形式訪問屬性值,可是我們並沒有在對應的模型類聲明id屬性呀。
  同樣地,玄機還是在於這個Model類。它含有__get、__set這樣兩個魔術方法:

    /**
     * 修改器 設置數據對象的值
     * @access public
     * @param string    $name 名稱
     * @param mixed     $value 值
     * @return void
     */
    public function __set($name, $value)
    {
        $this->setAttr($name, $value);
    }

    /**
     * 獲取器 獲取數據對象的值
     * @access public
     * @param string $name 名稱
     * @return mixed
     */
    public function __get($name)
    {
        return $this->getAttr($name);
    }
 

  那麼,原因我想你應該知道了。嘻嘻~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章