1. 程式人生 > >ThinkPHP5 select出來的結果是個物件?居然還可以以陣列形式訪問資料?

ThinkPHP5 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]offsetGetname,即以陣列操作時傳入的鍵的值。
  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);
    }

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