您好,登錄后才能下訂單哦!
安裝了laravel-debugbar后打開一個列表頁面,發現頁面輸出有兩個 select count(*) 語句,這是一個嚴重的設計缺陷呀。
查看代碼
$users = User::where('votes', '>', 100)->paginate(15); $count = User::where('votes', '>', 100)->count();
之前就感覺paginate分頁應該是使用了count,但是不知道怎么取總量數據所以又寫了一個count()。
var_dump($users);
object(Illuminate\Pagination\LengthAwarePaginator)[306] protected 'total' => int 4289 protected 'lastPage' => int 143 protected 'items' => object(Illuminate\Database\Eloquent\Collection)[307] protected 'items' => array (size=30) 0 => ....
返回數據里面 的確有 protected 'total',但是protected不能訪問呀!
仔細看了一下文檔,$results->total()
,原來取total需要的是方法,而不是屬性。
弄明白total的獲取以后,對paginate這個分頁方法產生了興趣。于是看了一下源碼。
paginate這個方法最后使用了
Illuminate\Pagination\LengthAwarePaginator
這個類是怎么調用的呢?
無論是User::where('votes', '>', 100)->paginate(15)還是User::paginate(15),
User繼承著Illuminate\Database\Eloquent\Model這個ORM類,但是在Model并沒有where和paginate這些方法或靜態方法,這是laravl使用的一種代碼設計模式,
/** * Handle dynamic static method calls into the method. * * @param string $method * @param array $parameters * @return mixed */ public static function __callStatic($method, $parameters) { $instance = new static; return call_user_func_array([$instance, $method], $parameters); }
__callStatic是php類的魔術方法
http://php.net/manual/zh/language.oop5.overloading.php#object.callstatic
public static mixed __callStatic ( string $name , array $arguments )
在靜態上下文中調用一個不可訪問方法時,__callStatic() 會被調用。
$name 參數是要調用的方法名稱。$arguments 參數是一個枚舉數組,包含著要傳遞給方法 $name 的參數。
laravel Model的__callStatic實現的業務
new static;
new當前model,也就是new User,這個是static靜態延遲綁定的使用,可以和“new self;”使用進行比較。
call_user_func_array([$instance, $method], $parameters);
主要是這段,call_user_func_array 調用了new static類即User的where或paginate方法,傳遞 $parameters參數。
然后,然后,然后,User這個繼承Model類里面仍然沒有where或paginate方法,那么使用 Model的__call這魔術方法。
/** * Handle dynamic method calls into the model. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (in_array($method, ['increment', 'decrement'])) { return call_user_func_array([$this, $method], $parameters); } $query = $this->newQuery(); return call_user_func_array([$query, $method], $parameters); }
開始還以為這么處理多了一層魔術方法,浪費效率,想想突然明白了,
User::where();
$user = new User; $user->paginate(15);
代碼是一樣的,只不過用__callStatic的 new static代替了“new User;”,
用call_user_func_array代替了調用函數和傳參,lavarel的簡潔可見一斑。
繼續分析,如果調用的方法是 increment或者decrement,那么使用的是User類的方法。
否則使用 $this->newQuery();
newQuery這個方法的層級太深,沒能理解,不過看注釋,主要是調用
'Illuminate\Database\Eloquent\Builder' /** * Get a new query builder for the model's table. * * @return \Illuminate\Database\Eloquent\Builder */ public function newQuery()
Builder這個類里面的where和paginate方法,就是ORM使用的方法;
其中paginate方法
/** * Paginate the given query. * * @param int $perPage * @param array $columns * @param string $pageName * @param int|null $page * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator * * @throws \InvalidArgumentException */ public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null) { $page = $page ?: Paginator::resolveCurrentPage($pageName); $perPage = $perPage ?: $this->model->getPerPage(); $query = $this->toBase(); $total = $query->getCountForPagination(); $results = $total ? $this->forPage($page, $perPage)->get($columns) : new Collection; return new LengthAwarePaginator($results, $total, $perPage, $page, [ 'path' => Paginator::resolveCurrentPath(), 'pageName' => $pageName, ]); }
paginate調用最后使用了LengthAwarePaginator類,
所以最后var_dump($user) 是“object(Illuminate\Pagination\LengthAwarePaginator)[306]”
LengthAwarePaginator只是分頁類,與數據層無關,即Eloquent ORM和分頁是分離的。
以上就是Lavavel的Eloquent ORM分頁源碼分析,很多地方有待深入,不過看一次源碼,提高很大。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。