讓你的ORM條件篩選更加的優雅[合理性的偷懶]

Siam發表於2020-08-19

隔了很久再來分享一個小魔法
在laravel中,為模型新增篩選條件一般都是使用when方法,如下:

use App\Models\Article;

public function index(Request $request)
{
    $articles = Article::with('category:id,name', 'tags:id,name')
        ->when($request->filled('keyword'), function ($query) use ($request) {
            $query->where('title', 'LIKE', "%{$request->query('keyword')}%");
        })
        ->paginate(20, ['id', 'title', 'introduce', 'is_recommend', 'category_id']);
    return view('admin.article.index', compact('articles', 'request'));
}

雖然沒使用變數儲存模型控制程式碼,然後再使用if判斷,可是我覺得程式碼是還可以再精【tou】簡【lan】的,下面就來看看如何實現

精妙的使用陣列展開符

在你的基礎模型新增一個公用的scope方法

class Base extends Model
{
    /**
     * 按需進行條件篩選
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  bool   $bool
     * @param  array  $parameters
     * @return void
     */
    public function scopeWhenWhere(Builder $query, bool $bool, ...$parameters)
    {
        $bool && $query->where(...$parameters);
    }
}

你可能會在想這樣之後呢?這樣之後,就是偷懶的時刻!

/**
 * Display a listing of the resource.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\View\View
 */
public function index(Request $request)
{
    $articles = Article::with('category:id,name', 'tags:id,name')
        ->whenWhere($request->filled('keyword'), 'title', 'LIKE', "%{$request->query('keyword')}%")
        ->whenWhere($request->filled('category_id'), 'category_id', $request->query('category_id'))
        ->paginate(20, ['id', 'title', 'introduce', 'is_recommend', 'category_id']);
    return view('admin.article.index', compact('articles', 'request'));
}

這一行程式碼主要體現在使用陣列展開符,也就是說第一個引數用來做短路判斷,如原來when的第一個引數一樣,接下來的引數按位置傳給where函式,就會達到一模一樣的效果,而且擴充性也是一模一樣的,因為如果你要做複雜的判斷,那麼你也可以傳入閉包【雖然這個時候你使用原來的when也是一樣】

再進化,讓其支援自動執行scope

Article模型內的scopeRecommend方法

/**
 * 是否為推薦文章
 * 
 * @param  \Illuminate\Database\Eloquent\Builder  $query
 * @param  bool  $recommend
 * @return bool
 */
public function scopeRecommend(Builder $query, bool $recommend = true)
{
    $query->where('is_recommend', $recommend);
}

這個需求誕生在我近段時間寫開源的部落格和其附帶的app時遇到的,我不想笨笨的傳入閉包然後在閉包內呼叫scope,如:

$articles = Article::with('category:id,name', 'tags:id,name')
    ->when($request->filled('is_recommend'), function ($query) use ($request) {
        $query->recommend($request->query('is_recommend'));
    })
    ->paginate(20, ['id', 'title', 'introduce', 'is_recommend', 'category_id']);

或者

$articles = Article::with('category:id,name', 'tags:id,name')
    ->whenWhere($request->filled('is_recommend'), function ($query) use ($request) {
        $query->recommend($request->query('is_recommend'));
    })
    ->paginate(20, ['id', 'title', 'introduce', 'is_recommend', 'category_id']);

那麼讓我們對whenWhere這個scope方法進行小小的修改,雖然程式碼就沒有一行那麼精妙,但是如果有需要呼叫scope的同學,可以進行如下修改:

/**
 * 按需進行條件篩選
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $query
 * @param  bool   $bool
 * @param  array  $parameters
 * @return void
 */
public function scopeWhenWhere(Builder $query, bool $bool, ...$parameters)
{
    if ($bool) {
        if (isset($parameters[1]) && $parameters[1] === 'scope') {
            $query->{$parameters[0]}(...array_slice($parameters, 2));
        } else {
            $query->where(...$parameters);
        }
    }
}

接下來我就只需要這樣使用即可

$articles = Article::with('category:id,name', 'tags:id,name')
    ->whenWhere($request->filled('is_recommend'), 'recommend', 'scope', $request->query('is_recommend'))
    ->whenWhere($request->filled('keyword'), 'title', 'LIKE', "%{$request->query('keyword')}%")
    ->whenWhere($request->filled('category_id'), 'category_id', $request->query('category_id'))
    ->paginate(20, ['id', 'title', 'introduce', 'is_recommend', 'category_id']);

看完這篇文章,你學會偷懶的正確姿勢了嗎?
最後附帶另外一個小魔法,細心的同學可能還發現了一個地方,那就是我的with的寫法為什麼是冒號加欄位名,請不要誤會,這裡是laravel本身就有的寫法,冒號前面是關聯名稱,冒號後面是需要查詢的欄位,可以很方便的幫你避免使用閉包然後在閉包內使用select方法限定欄位。

預告

下一次發文可能就是開源部落格程式碼的時候了
部落格功能非常非常簡單,前臺使用媒體查詢進行自適應,app端使用flutter進行開發
開發這個僅僅是想提供開發思路給大家,如後臺的封裝,前臺的封裝,寫出更好的程式碼
laravel原始碼內會包含極度正規的程式碼規範和一些程式碼架構封裝【但是並不會使用Repository模式,一些小缺陷會在開源時說明】
如果你想學習php良好的開發姿勢,不妨關注一下

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章