diff --git a/README.md b/README.md index 7ebb875..bb944a5 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,43 @@ You need to give the path to the `RAML` file describing your API. You can find a Then, browse your new API REST on the url defined in the `baseUrl` configuration of your `RAML` api file. +## Using request parameters + +You can specify some get request parameters. For example: + +Request param | Description | Type / Example | Default +--------------|--------------|--------------|-------------- +`_start` | specify start bound of selection | `number` | `0` +`_end` | specify **length** of selection | `number` |`20` +`_sort` | specify key ordering | `string` | +`_sortDir` | specify order direction | `ASC`, `DESC` | `ASC` +`_fields` | specify comma separated set of fields in result set | `string` | `*` +`_strongFilter[]` | specify conjunction filter like a ````id` = 8 AND `post_id` = 2``` as request params array | `array` | +`_strongFilterIn[]` | specify `IN` condition like a ````id` IN (1,2,3)``` | array | +`_searchOr[]` | specify search disjunction filter like a ````title` LIKE '%foo%' OR `post` LIKE '%bar%'``` | `array` | +`_searchAnd[]` | specify search conjunction filter like a ````title` LIKE '%foo%' AND `post` LIKE '%bar%'``` | `array` | +`_group` | set group part | `string` | + +You can combine one of `_strongFilter[]`, `_strongFilterIn[]`, `_searchOr[]`, `_searchAnd[]` with `_sort`, `_sortDir`, `_fields`, `_start` and `_end` params + +#### Warning! +You should use **only** one filer from +`_strongFilter[]`, `_strongFilterIn[]`, `_searchOr[]`, `_searchAnd[]` or you will get an HTTP error `400 Bad request`. + +### Query string examples + +Query string | Description +-------------|------------ +`/posts?_start=10&_end=15` | you will receive a 15 posts from 10 position as result set +`/posts?_sort=title&_sortDir=DESC` | you will receive a list sorted by `title` descending +`/posts?_fields=id,title` | you will receive a list with `id` and `title` field in response +`/posts?_strongFilter[id]=8&_strongFilter[title]=foo` | you will receive a list of items where ````id` = 8 AND `title` = 'foo'``` +`/posts?_strongFilterIn[id]=1,2,3` | you will receive a list of items with `id` in list: `1`, `2`, `3` +`/posts?_searchOr[title]=foo&_searchOr[body]=bar` | you will receive a list of items where ````title` LIKE '%foo%' OR `body` LIKE '%bar%'``` +`/posts?_searchAnd[title]=foo&_searchAnd[body]=bar` | you will receive a list of items where ````title` LIKE '%foo%' AND `body` LIKE '%bar%'``` +`/posts?_group=title` | should use for request distinct values of column instead +`/posts?_searchAnd[title]=foo&_searchOr[body]=bar` | you will receive HTTP error `400 Bad request` + ## Tests Run the tests suite with the following commands: diff --git a/src/RestController.php b/src/RestController.php index e1faa27..f42f3c8 100644 --- a/src/RestController.php +++ b/src/RestController.php @@ -25,11 +25,130 @@ public function homeAction($availableRoutes) public function getListAction($objectType, Request $request) { + // Prepare-prefixes + $strongFilterKeyPrefix = 'strong_filter_'; + $searchOrKeyPrefix = 'search_or_'; + $searchAndKeyPrefix = 'search_and_'; + $queryBuilder = $this->dbal ->createQueryBuilder() - ->select('o.*') - ->from($objectType, 'o') - ; + ->from($objectType, 'o'); + + $filterNames = array( + '_strongFilter', + '_strongFilterIn', + '_searchOr', + '_searchAnd', + ); + + // Fetching filters from request + $filters = array_combine( + $filterNames, + array_map( + function ($filter) use ($request) { + return $request->query->get($filter); + }, + $filterNames + ) + ); + + // Throw expression when count of filters in request greater then one + $count = array_reduce( + $filters, + function ($carry, $item) { + return $carry + ($item !== null); + }, + 0 + ); + if ($count > 1) { + return new JsonResponse( + array( + 'status' => 'ERROR', + 'status_code' => 400, + 'message' => 'You should use only one type of filters per request', + ), 400 + ); + } + + // Return only assigned fields + $fields = $request->query->get('_fields'); + $queryBuilder->select( + $fields ? preg_replace('/([^,]+)/', 'o.$1', $fields) : 'o.*' + ); + + // Strong filter implementing + // o.f1 = 'val1' AND o.f2 = 'val2' ... + if ($filters['_strongFilter']) { + $queryBuilder + ->where( + implode( + ' and ', + array_map( + function ($item) use ($strongFilterKeyPrefix) { + return "{$item} = :{$strongFilterKeyPrefix}{$item}"; + }, + array_keys($filters['_strongFilter']) + ) + ) + ) + ->setParameters( + array_combine( + array_map( + function ($key) use ($strongFilterKeyPrefix) { + return $strongFilterKeyPrefix . $key; + }, + array_keys($filters['_strongFilter']) + ), + $filters['_strongFilter'] + ) + ); + } + + // Searching with OR: + // o.f1 LIKE '%val1%' OR o.f2 LIKE '%val2%' + if ($filters['_searchOr']) { + foreach ($filters['_searchOr'] as $key => $value) { + $queryBuilder + ->orWhere( + $queryBuilder->expr()->like( + $key, + ":" . $searchOrKeyPrefix . $key + ) + ) + ->setParameter($searchOrKeyPrefix . $key, "%{$value}%"); + } + } + + if ($filters['_strongFilterIn']) { + foreach ($filters['_strongFilterIn'] as $key => $value) { + $queryBuilder + ->andWhere( + $queryBuilder->expr()->in( + $key, + explode(',', $value) + ) + ); + } + } + + // Searching with AND: + // o.f1 LIKE '%val1%' AND o.f2 LIKE '%val2%' + if ($filters['_searchAnd']) { + foreach ($filters['_searchAnd'] as $key => $value) { + $queryBuilder + ->andWhere( + $queryBuilder->expr()->like( + $key, + ":" . $searchAndKeyPrefix . $key + ) + ) + ->setParameter($searchAndKeyPrefix . $key, "%{$value}%"); + } + } + + if ($group = $request->query->get('_group')) { + $queryBuilder->groupBy($group); + } if ($sort = $request->query->get('_sort')) { $queryBuilder->orderBy($sort, $request->query->get('_sortDir', 'ASC'));