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());
}
執行結果如下:
這裡可以看到它返回了一個數組,每個元素的值是一個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/>';
}
執行結果如下:
為什麼可以這麼操作
既然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方法就實現了
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);
}
那麼,原因我想你應該知道了。嘻嘻~