Laravel - 在 where 子句中循环数据过滤

Laravel - Loop data filtering in where clauses

所以我为我的网站制作了一个过滤器。使用此过滤器,用户可以按名称、类别(用户可以通过选中复选框选择多个类别)、城市 ID 和其他内容搜索任务。这是代码:

<?php

namespace App\Http\Services;

use App\Models\Task;

use Carbon\Carbon;

class SearchTaskService {
  public function execute(array $searchParameters) {
    $categories = $searchParameters['category'] ?? null;
    $is_remote = $searchParameters['remote_job'] ?? null;
    $is_no_responses = $searchParameters['no_responses'] ?? null;
    $period = $searchParameters['time'] ?? null;
    $name = $searchParameters['name'] ?? null;
    $city_id = $searchParameters['city_id'] ?? null;
    $category_id = $searchParameters['category_id'] ?? null;

    $tasks = Task::all();

    $tasks = Task::when($categories, function($query, $categories) {
        return $query->whereIn('category_id', $categories);
    })->when($is_remote, function($query) {
        return $query->where('remote', 1);
    })->when($is_no_responses, function($query) {
        return $query->withCount('feedbacks')->having('feedbacks_count', '=', 0);
    })->when($period === "in_a_day", function($query) {
        return $query->where('deadline', '<=', Carbon::now()->addDay());
    })->when($period === "in_a_week", function($query) {
        return $query->where('deadline', '<=', Carbon::now()->addWeek());
    })->when($period === "in_a_month", function($query) {
        return $query->where('deadline', '<=', Carbon::now()->addMonth());
    })->when($name, function($query, $name) {
        return $query->where('title', 'LIKE', '%'.$name.'%');
    })->when($city_id, function($query, $city_id) {
        return $query->where('city_id', $city_id);
    })->when($category_id, function($query, $category_id) {
        return $query->where('category_id', $category_id);
    });

    return $tasks
  }
}

这个过滤器工作正常,但我确信代码可以重构,因为它看起来像垃圾(太大)。有没有办法过滤 foreach 或任何其他循环中的数据?如果每个查询都像“where”,那么很容易循环这个,但有时它是“whereIn”或“withCount”,所以查询肯定不一样。

另一种方法是使用管道。

您可以在单独的 classes 中定义单独的过滤器(使测试更容易),例如:

<?php

namespace App\Filters;

class ByCategories
{
    public function handle($query, $next)
    {
        if(request()->has('categories') {
            $query->whereIn('category_id', request()->categories);
        }
        
        return $next($query);
    }
}
<?php

namespace App\Filters;

class IsRemoteJob
{
    public function handle($query, $next)
    {
        if(request()->has('remote_job') {
            $query->where('remote', 1);
        }

        return $next($query);
    }
}

对于所有过滤器,如 HasNoResponseByPeriodByNameByCityIdByCategoryId 等等 - 每个过滤器 class应该有 handle 方法。

然后在您的 SearchTaskService 中您可以将 execute 定义为


<?php

namespace App\Http\Services;

use Illuminate\Pipeline\Pipeline;

class SearchTaskService
{
    public function execute()
    {
        $query = Task::query();

        return app(Pipeline::class)
            ->send($query)
            ->through([
                \App\Filters\ByCategories::class,
                \App\Filters\IsRemoteJob::class,
                \App\Filters\HasNoResponse::class,
                \App\Filters\ByPeriod::class,
                \App\Filters\ByName::class,
                \App\Filters\ByCityId::class,
                \App\Filters\ByCategoryId::class,
            ])
            ->thenReturn()
            ->get();
    }
}

注意:此处您必须访问 request() 并直接从每个过滤器 class 中的请求获取搜索参数,而不是使用 $searchParameters 数组。

你可能会从我的代码中得到一些东西。

public function filterRecords($query, $request, $columns = [], $idType = 'code')
{
$data   = $request->all();
$ids    = $request->input('ids');

foreach ($data as $key => $value) {
    if ($value !== null && $value !== '') {
        if (gettype($value) === 'string') {
            if (str_contains($value, 'like@@')) {
                $likeValue = substr($value, 6);
                $query = $query->where($key, 'like', '%' . $likeValue . '%');
            } else if (str_contains($value, 'exact@@')) {
                $exactValue = substr($value, 7);
                $query = $query->where($key, $exactValue);
            }
        }


        if (gettype($value) === 'array' && \in_array('symbol', $value) && $value[2] !== 'undefined') {  // For smaller than and greater than operations
            if ($value[2] !== null && $value[2] !== 'undefined' && in_array($value[1], ['<=', '>=', '==', '>', '<']))
                $query = $query->where($key, $value[1], $value[2]);
            continue;
        }

        if (gettype($value) === 'array' && in_array('range', $value)) {    // For all betweens (date, range)
            $isDate = in_array('date', $value) ? true : false;
            $isDate ? array_splice($value, 0, 2) : array_splice($value, 0, 1);

            if (count($value) === 1)
                $value[] = $isDate ? '1800-11-11' : PHP_INT_MIN;

            $value[0]   = is_null($value[0]) && !$isDate ? PHP_INT_MIN : $value[0];
            $value[1]   = is_null($value[1]) && !$isDate ? PHP_INT_MAX : $value[1];

            $value[0]   = is_null($value[0]) && $isDate ? '1800-11-11' : $value[0];
            $value[1]   = is_null($value[1]) && $isDate ? '4000-12-12' : $value[1];

            $from       = $isDate ? date(min($value)) : min($value);
            $to         = $isDate ? date(max($value)) : max($value);
            $query      = $isDate ? $query->whereBetween(DB::raw("DATE($key)"), array($from, $to)) : $query->whereBetween($key, array($from, $to));
            continue;
        }

        if ($key === 'include' && $ids !== null) {
            $query = $value == 1 ? $query->whereIn($idType, $ids) : $query->whereNotIn($idType, $ids);    // For ID and code
        }
    }
}
return $query;

}

$columns is the table columns which you want to search in.

看一看,您可能会产生构建自己的想法。 希望对你有帮助。