如何获取大量 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 参数并根据它们干净地过滤查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据传入参数的值有条件地应用 GraphQL 过滤器?

Django Admin:获取根据 GET 字符串过滤的 QuerySet,与更改列表中看到的完全一样?

过滤查询的php参数化查询

如何从数据库中获取大量数据并通过输入输入将其显示到下拉列表过滤器中,但只能从数据库中选择

如何在java中有效地解析查询参数?

干净地将参数列表传递给 ProcessStartInfo