Laravel 模型過濾(Filter)設計


抽象類不能直接通過new去實例化一個對象,要獲取抽象類的對象, 需要先用一個類繼承抽象類, 然后去實例化子類。

在我們日常代碼開發中,可能最常見的功能就是列表篩選了。通過不同的參數,返回符合條件的內容。下面我分享一下自己的過濾代碼設計(其實是從 Laracasts 上學來的?)。

基本實現

假設我們有一個圖書的列表,想要進行篩選:

  • 標題含有 "我們" 的
  • 定價大於 30 元的
  • 出版社是 "大地出版社" 的
  • 按照 ID 進行倒序排列

針對這幾個條件我們可以很快的寫出下面的代碼


public function index()
{
    return Book::query()
        ->where('title', 'like', '%我們%')
        ->where('price', '>=', 30)
        ->where('publisher', '大地出版社')
        ->orderBy('id', 'DESC')
        ->get();
}

優化

雖然我們實現了需求,但是如果我們增加或者減少篩選條件呢,以及在請求參數存在的時候才進行過濾呢?我們將不斷的在上面的代碼中更改,當條件過多的時候,我們的代碼將會變成一團亂麻。

下面是我的優化方法。

BaseFilter

首先我們需要一個基礎的 QueryFilter,然后我們所有的 Filter 都繼承這個類。

abstract class QueryFilter
{

    protected $request;
    protected $builder;

    public function __construct(Request $request)                                                                        
    {                                                                                                                    
        $this->request = $request;                                                                                       
    }                                                                                                                    

    public function apply(Builder $builder)                                                                              
    {                                                                                                                    
        $this->builder = $builder;                                                                                       

        foreach ($this->filters() as $name => $value) {                                                                  
            if (method_exists($this, $name)) {                                                                           
                call_user_func_array([$this, $name], array_filter([$value]));                                            
        }                                                                                                            
     }                                                                                                                

        return $this->builder;                                                                                           
    }                                                                                                                    

    public function filters()
    {
        return $this->request->all();
    }
}

這個類的代碼很簡單,主要功能集中在 apply 函數中,我們檢查每個請求參數,如果這個方法存在,那么調用對應的方法。下面結合具體的實例進行解釋

BookFilter

我們的 BookFilter 代碼如下:

class BookFilter extends QueryFilter
{
    public function title($title)
    {
        return $this->builder->where('title', 'like', "%{$title}%");
    }

    public function price($price)
    {
        return $this->builder->where('price', '>=', "%{$price}%");
    }

    public function publisher($publisher)
    {
        return $this->builder->where('publisher', $publisher);
    }
}

在上面的代碼中,我們可以通過 URL 的參數來進行動態查詢。

books?title=我們    //  查找標題含有我們的圖書

books?title=我們&price=25   //  查找標題含有我們並且價格大於25元的圖書

這種結構的代碼將會很靈活的控制我們的過濾列表,並且我們的代碼也很整潔。

完結

當然,如果我們只進行到這一步是沒法進行查詢的,因為我們還沒有地方調用 QueryFilter 中的 apply 方法。有一個絕佳的地方可以進行調用,那就是模型中的 scope 。

注意:scopeFilter($query, QueryFilter $filters) 傳參是抽象類,並使用該類調用了方法,所以實際使用時要傳遞實例化的繼承類。

class Book extends Model
{
    public function scopeFilter($query, QueryFilter $filters)
    {
        return $filters->apply($query);
    }
}

最后,我們原來控制器的方法將改為下面的樣子:

public function index(BookFilter $filters)
{
    return Book::filter($filters)->get();
}

這樣我們就完成了一個比較靈活的篩選列表了。

// 假設 book 關聯了author
// 這里是 Book.php
public function author()
{
    return $this->belongsTo(Author::class);
}

// 那么Filter 中的方法可以這樣寫
// 這里是 BookFilter.php
public function author($name)
{
    return $builder->whereHas('author' function($query) use($name) {
        return $query->where('name', 'like', "%{$name}%");
    });
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM