溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Lavavel筆記 Eloquent ORM分頁源碼分析

發(fā)布時間:2020-06-28 18:09:21 來源:網(wǎng)絡(luò) 閱讀:1501 作者:daweilang 欄目:web開發(fā)

安裝了laravel-debugbar后打開一個列表頁面,發(fā)現(xiàn)頁面輸出有兩個 select count(*) 語句,這是一個嚴(yán)重的設(shè)計缺陷呀。


查看代碼

$users = User::where('votes', '>', 100)->paginate(15);
$count = User::where('votes', '>', 100)->count();

之前就感覺paginate分頁應(yīng)該是使用了count,但是不知道怎么取總量數(shù)據(jù)所以又寫了一個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 => ....

返回數(shù)據(jù)里面 的確有 protected 'total',但是protected不能訪問呀!

仔細(xì)看了一下文檔,$results->total(),原來取total需要的是方法,而不是屬性。

弄明白total的獲取以后,對paginate這個分頁方法產(chǎn)生了興趣。于是看了一下源碼。

paginate這個方法最后使用了

Illuminate\Pagination\LengthAwarePaginator

這個類是怎么調(diào)用的呢?

無論是User::where('votes', '>', 100)->paginate(15)還是User::paginate(15),

User繼承著Illuminate\Database\Eloquent\Model這個ORM類,但是在Model并沒有where和paginate這些方法或靜態(tài)方法,這是laravl使用的一種代碼設(shè)計模式,

    /**
     * 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類的魔術(shù)方法

http://php.net/manual/zh/language.oop5.overloading.php#object.callstatic

 public static mixed __callStatic ( string $name , array $arguments )

在靜態(tài)上下文中調(diào)用一個不可訪問方法時,__callStatic() 會被調(diào)用。
$name 參數(shù)是要調(diào)用的方法名稱。$arguments 參數(shù)是一個枚舉數(shù)組,包含著要傳遞給方法 $name 的參數(shù)。


laravel Model的__callStatic實現(xiàn)的業(yè)務(wù)

new static;

new當(dāng)前model,也就是new User,這個是static靜態(tài)延遲綁定的使用,可以和“new self;”使用進(jìn)行比較。


call_user_func_array([$instance, $method], $parameters);

主要是這段,call_user_func_array 調(diào)用了new static類即User的where或paginate方法,傳遞 $parameters參數(shù)。

然后,然后,然后,User這個繼承Model類里面仍然沒有where或paginate方法,那么使用 Model的__call這魔術(shù)方法。

    /**
     * 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);
    }

開始還以為這么處理多了一層魔術(shù)方法,浪費效率,想想突然明白了,

User::where();
$user = new User; 
$user->paginate(15);

代碼是一樣的,只不過用__callStatic的 new static代替了“new User;”,

用call_user_func_array代替了調(diào)用函數(shù)和傳參,lavarel的簡潔可見一斑。


繼續(xù)分析,如果調(diào)用的方法是 increment或者decrement,那么使用的是User類的方法。

否則使用 $this->newQuery();


newQuery這個方法的層級太深,沒能理解,不過看注釋,主要是調(diào)用

'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調(diào)用最后使用了LengthAwarePaginator類,

所以最后var_dump($user) 是“object(Illuminate\Pagination\LengthAwarePaginator)[306]”

LengthAwarePaginator只是分頁類,與數(shù)據(jù)層無關(guān),即Eloquent ORM和分頁是分離的。


以上就是Lavavel的Eloquent ORM分頁源碼分析,很多地方有待深入,不過看一次源碼,提高很大。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI