如何获取大量 GET 参数并根据它们干净地过滤查询?
Posted
技术标签:
【中文标题】如何获取大量 GET 参数并根据它们干净地过滤查询?【英文标题】:How can I take a big amount of GET parameters and filter a query depending on them cleanly? 【发布时间】:2016-07-10 00:39:16 【问题描述】:我有这个控制器用于我在 Laravel Lumen 中构建的 RESTful API,它接受相对大量的参数并将它们解析到查询位置,并根据是否提供数据来获取数据。例如,
GET /nodes?region=California
GET /nodes?ip=127.0.0.1
我目前正在将它们放入构造函数中,构建参数数组(因为我无法弄清楚如何在 Lumen 中获取原始 get 数组,并且因为我已经有其他参数所以不方便),并且过滤掉空值(如果它们不在查询中,我将值设置为空)。
现在,当涉及到过滤数组中的每个值时,我是通过一个 foreach 数组来完成的。这是我能想到的最简洁的方法,无需太多代码(我不想让我的控制器太胖。)。 有没有其他方法可以干净地做到这一点,也许是函数/类的分离?
这是我的构造函数代码:
/**
* Get some values before using functions.
*
* @param Request $request Instance of request.
*/
public function __construct(Request $request)
$this->offset = (int) $request->input('offset', 0);
// TODO: I'm not sure how to implement this, code in question
$this->filters = [
'region' => $request->input('region', null),
'name' => $request->input('name', null),
'ip' => $request->input('ip', null)
];
$this->filters = array_filter($this->filters, function ($v)
return !is_null($v);
);
// Set a sane SQL limit.
$this->limit = 5;
$this->request = $request;
以及控制器代码:
/**
* List all nodes.
*
* @return [string] [JSON containing list of nodes, if sorted.]
*/
public function all()
try
// use filters provided
$data = Nodes::limit($this->limit)->offset($this->offset);
foreach ($this->filters as $filter => $value)
$data->where($filter, $value);
$data = $data->get();
$response = $this->respond($data);
catch (\Exception $e)
$response = $this->respondServerError('Could not retrieve data from database.');
return $response;
【问题讨论】:
【参考方案1】:所以每当我必须在 API 中过滤资源列表时,我都是这样做的。
首先,在我开始之前,关于在控制器方法中获取 Request 对象的快速提示:如果将 Request $request
添加为 all()
函数的参数,您将可以访问$request 变量在那里,与您的构造函数相同。所以完整的签名是public function all(Request $request)
。控制器方法具有与 Laravel/Lumen 中其他类构造函数相同的神奇依赖注入。或者,在您的函数中,您始终可以要求 app()
函数为您提供特定类的对象。因为 Request 对象在 Container 中被绑定到只是 'request',你可以要求提供完整的类名,或者只是 'request':$request = app('request');
因此,一旦我有了请求对象,在我的控制器方法中,我喜欢将每个过滤器作为一个组或一个接一个地检查,具体取决于每个过滤器的复杂程度。有时过滤器很复杂,例如需要将逗号分隔的 ID 列表分解为数组。如果它只是简单的字符串过滤器,我倾向于将列表放入一个数组中并运行它。
这里有一个示例函数来说明一些想法:
public function getIndex(Request $request)
//Create a User object to append WHERE clauses onto
$user = app('App\Models\User');
//Run through our simple text fields
foreach(['first_name', 'last_name', 'region', 'ip'] as $field)
if ($request->has($field))
$user->where($field, $request->input($field));
//This field uses a LIKE match, handle it separately
if ($request->has('email'))
$user->where('email', LIKE, '%' . $request->input('email') . '%');
//This field is a list of IDs
if ($request->has('id'))
$ids = explode(',', $request->input('id'));
$user->whereIn('id', $ids);
//Use pagination
$users = $user->paginate(25);
/**
* Continue with the rest of response formatting below here
*/
您会注意到我使用了分页功能来限制我的结果。在构建列出资源的 API 端点时,您需要在标题(我的偏好)或响应主体信息中放入有关如何获取结果的第一页、上一页、下一页和最后一页的信息。 Laravel 中的分页功能让这一切变得简单,因为它可以使用 links()
方法构建大部分链接。
不幸的是,您需要告诉它在请求中传递了哪些过滤器参数,以便它可以确保将这些参数添加到它生成的链接中。否则,您将在没有过滤器的情况下返回链接,这对客户端的分页非常不利。
这里有一个更完整的记录过滤器参数的例子,以便它们可以附加到分页链接上:
public function getIndex(Request $request)
//Create a User object to append WHERE clauses onto
$user = app('App\Models\User');
//List of filters we found to append to links later
$appends = [];
//Run through our simple text fields
foreach(['first_name', 'last_name', 'region', 'ip'] as $field)
if ($request->has($field))
$appends[$field] = $request->input($field);
$user->where($field, $request->input($field));
//This field uses a LIKE match, handle it separately
if ($request->has('email'))
$appends['email'] = $request->input('email');
$user->where('email', LIKE, '%' . $request->input('email') . '%');
//This field is a list of IDs
if ($request->has('id'))
$appends['id'] = $request->input('id');
$ids = explode(',', $request->input('id'));
$user->whereIn('id', $ids);
//Use pagination
$users = $user->paginate(25);
//Make sure we append our filter parameters onto the pagination object
$users->appends($appends);
//Now calling $users->links() will return the correct links with the right filter info
/**
* Continue with the rest of response formatting below here
*/
分页文档可以在这里找到:https://laravel.com/docs/5.2/pagination
有关如何出色地完成分页链接的示例,请查看 Github 的 API 文档:https://developer.github.com/v3/#pagination
最终,从概念上讲,它与您正在做的事情并没有太远。这样做的好处是您将代码移动到需要它的方法中,而不是在每次初始化控制器时都在构造函数中运行它,即使会调用不同的方法。
希望有帮助!
【讨论】:
谢谢伙计!真的让我走上了正轨。标记为已回答。以上是关于如何获取大量 GET 参数并根据它们干净地过滤查询?的主要内容,如果未能解决你的问题,请参考以下文章
Django Admin:获取根据 GET 字符串过滤的 QuerySet,与更改列表中看到的完全一样?