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);
}
}
对于所有过滤器,如 HasNoResponse
、ByPeriod
、ByName
、ByCityId
、ByCategoryId
等等 - 每个过滤器 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.
看一看,您可能会产生构建自己的想法。
希望对你有帮助。
所以我为我的网站制作了一个过滤器。使用此过滤器,用户可以按名称、类别(用户可以通过选中复选框选择多个类别)、城市 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);
}
}
对于所有过滤器,如 HasNoResponse
、ByPeriod
、ByName
、ByCityId
、ByCategoryId
等等 - 每个过滤器 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.
看一看,您可能会产生构建自己的想法。 希望对你有帮助。