我怎样才能获取大量 GET 参数并根据它们干净地过滤查询?

How can I take a big amount of GET parameters and filter a query depending on them cleanly?

我有这个控制器用于 RESTful API 我在 Laravel Lumen 中构建它需要相对大量的参数并将它们解析到查询和获取数据的位置取决于是否提供了它们。例如,

GET /nodes?region=California
GET /nodes?ip=127.0.0.1

我目前正在将它们带入构造函数,构建一个参数数组(因为我不知道如何在 Lumen 中获取原始 get 数组,这会很不方便,因为我已经有其他参数了) ,并过滤掉空值(如果它们不在查询中,我将值设置为空值)。

现在,当涉及到过滤数组中的每个值时,我是通过 foreach 数组来完成的。这是我能想出的最干净的方法,没有太多代码(我不想让我的控制器太胖。)。 有没有其他方法可以干净地做到这一点,也许与 functions/classes?

分开

这是我的构造函数代码:

/**
 * 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;    
}

所以每当我必须在 API 中过滤 resource-list 时,我就是这样做的。

首先,在我开始之前,关于在控制器方法中获取请求 object 的快速提示:如果您添加 Request $request 作为 [=13] 的参数=] 函数,你可以在那里访问 $request 变量,就像你的构造函数一样。所以完整的签名将是 public function all(Request $request)。控制器方法具有与其他 class 构造函数在 Laravel/Lumen 中相同的神奇依赖注入。或者,在您的函数中,您始终可以要求 app() 函数为您提供特定 class 的 object。由于请求 object 在容器中仅绑定到 'request',您可以请求完整的 class 名称,或仅请求 'request':$request = app('request');

因此,一旦我收到请求 object,在我的控制器方法中,我喜欢将每个过滤器作为一个组或 one-by-one 进行检查,具体取决于每个过滤器的复杂程度。有时过滤器很复杂,例如需要分解成数组的 comma-separated 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 端点时,您将要放入您的 headers(我的偏好)或响应 body 有关如何获取第一个、上一个、下一页和结果的最后一页。 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


最后,从概念上讲,它与您所做的并没有太大的不同。这里的优点是您将代码移动到需要它的方法中,而不是每次初始化控制器时都在构造函数中使用它 运行,即使将调用不同的方法也是如此。

希望对您有所帮助!