Skip to content

Commit b6de13b

Browse files
committed
Properly filter by id
2 parents 2ea1b1c + c31696b commit b6de13b

File tree

11 files changed

+77
-29
lines changed

11 files changed

+77
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* ZfrRest now supports coalesce filtering for "hasMany" or "findMany" requests type through the new, optional
66
`enable_coalesce_filtering` module option. If enabled, ZfrRest will be able to respond to queries like
77
/customers?ids[]=5&ids[]=64, where `ids` is a configurable primary key name.
8+
* Fix a bug with entry points. Previously, if you had an entry point configured as "/users", ZfrRest used to
9+
match URLs like "/userssssss".
810

911
## 0.3.3
1012

config/zfr_rest.global.php.dist

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ return [
5151

5252
/**
5353
* If enabled, it allows the REST router to filter a collection list by identifiers. For instance, considering
54-
* a query /customers?ids[]=1&ids[]=2, it will be able to return a filtered collections
54+
* a query /customers?$ids[]=1&$ids[]=2, it will be able to return a filtered collections
5555
*/
5656
// 'enable_coalesce_filtering' => false,
5757

5858
/**
59-
* The coalesce filtering key
59+
* The coalesce filtering query key
6060
*/
61-
// 'coalesce_filtering_key' => 'ids',
61+
// 'coalesce_filtering_query_key' => '$ids',
6262

6363
/**
6464
* Service manager configuration to configure the method handlers. A method handler handles a HTTP request

docs/07. Cookbook.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,12 @@ Instead, ZfrRest allows you to enable "coalesce filtering" for identifier. This
299299
/customers?ids[]=2&ids[]=3&ids[]=5... and ZfrRest will automatically returns you the filtered collection.
300300

301301
This feature is disabled by default, and you can enable it using the `enable_coalesce_filtering`. You can also customize
302-
the query key using the `coalesce_filtering_key` option (default to "ids"):
302+
the query key using the `coalesce_filtering_query_key` option (default to "$ids"):
303303

304304
```php
305305
'zfr_rest' => [
306-
'enable_coalesce_filtering' => true,
307-
'coalesce_filtering_key' => 'ids'
306+
'enable_coalesce_filtering' => true,
307+
'coalesce_filtering_query_key' => '$ids'
308308
]
309309
```
310310

src/ZfrRest/Factory/GetHandlerFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class GetHandlerFactory implements FactoryInterface
3434
*/
3535
public function createService(ServiceLocatorInterface $serviceLocator)
3636
{
37+
/** @var ServiceLocatorInterface $parentLocator */
3738
$parentLocator = $serviceLocator->getServiceLocator();
3839

3940
return new GetHandler($parentLocator->get('ZfrRest\Options\ModuleOptions'));

src/ZfrRest/Mvc/Controller/MethodHandler/GetHandler.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,18 @@ public function handleMethod(AbstractRestfulController $controller, ResourceInte
7373
if ($this->moduleOptions->isEnableCoalesceFiltering() && $data instanceof Selectable) {
7474
/** @var \Zend\Http\Request $request */
7575
$request = $controller->getRequest();
76-
$idsKey = $this->moduleOptions->getCoalesceFilteringKey();
76+
$idsKey = $this->moduleOptions->getCoalesceFilteringQueryKey();
77+
78+
if (is_array($ids = $request->getQuery($idsKey, null))) {
79+
$metadata = $resource->getMetadata();
80+
$identifierKey = $metadata->getClassMetadata()->getIdentifierFieldNames();
7781

78-
if ($ids = $request->getQuery($idsKey, null)) {
7982
$criteria = new Criteria();
80-
$criteria->where($criteria->expr()->in($idsKey, $ids));
83+
$criteria->where($criteria->expr()->in(current($identifierKey), $ids));
8184

8285
// @TODO: maybe it would make more sense to allow to change the data from a resource, instead of
8386
// having to recreate a new one everytime
84-
$resource = new Resource($data->matching($criteria), $resource->getMetadata());
87+
$resource = new Resource($data->matching($criteria), $metadata);
8588
$controller->getEvent()->getRouteMatch()->setParam('resource', $resource);
8689
}
8790
}

src/ZfrRest/Options/ModuleOptions.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
namespace ZfrRest\Options;
2020

2121
use Zend\Stdlib\AbstractOptions;
22+
use ZfrRest\Exception\InvalidArgumentException;
2223

2324
/**
2425
* @author Michaël Gallego <mic.gallego@gmail.com>
@@ -65,18 +66,18 @@ class ModuleOptions extends AbstractOptions
6566
* Is the enable coalesce filtering enabled?
6667
*
6768
* If enabled, it allows the REST router to filter a collection list by identifiers. For instance, considering
68-
* a query /customers?ids[]=1&ids[]=2, it will be able to return a filtered collections
69+
* a query /customers?$ids[]=1&$ids[]=2, it will be able to return a filtered collections
6970
*
7071
* @var bool
7172
*/
7273
protected $enableCoalesceFiltering = false;
7374

7475
/**
75-
* The coalesce filtering key
76+
* The coalesce filtering query key
7677
*
7778
* @var string
7879
*/
79-
protected $coalesceFilteringKey = 'ids';
80+
protected $coalesceFilteringQueryKey = '$ids';
8081

8182
/**
8283
* @param array|null $options
@@ -195,18 +196,23 @@ public function isEnableCoalesceFiltering()
195196
}
196197

197198
/**
198-
* @param string $coalesceFilteringKey
199+
* @param string $coalesceFilteringQueryKey
200+
* @throws InvalidArgumentException
199201
*/
200-
public function setCoalesceFilteringKey($coalesceFilteringKey)
202+
public function setCoalesceFilteringQueryKey($coalesceFilteringQueryKey)
201203
{
202-
$this->coalesceFilteringKey = (string) $coalesceFilteringKey;
204+
if (empty($coalesceFilteringQueryKey)) {
205+
throw new InvalidArgumentException('Coalesce filtering key cannot be an empty value');
206+
}
207+
208+
$this->coalesceFilteringQueryKey = (string) $coalesceFilteringQueryKey;
203209
}
204210

205211
/**
206212
* @return string
207213
*/
208-
public function getCoalesceFilteringKey()
214+
public function getCoalesceFilteringQueryKey()
209215
{
210-
return $this->coalesceFilteringKey;
216+
return $this->coalesceFilteringQueryKey;
211217
}
212218
}

src/ZfrRest/Router/Http/ResourceGraphRoute.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,16 @@ public function match(RequestInterface $request, $pathOffset = 0)
160160
$path = substr($path, strlen(trim($baseUrl, '/')));
161161
}
162162

163-
// If the URI does not begin by the route, we can stop immediately
164-
if (substr($path, 0, strlen($this->route)) !== $this->route) {
163+
$pathParts = explode('/', trim($path, '/'), 2);
164+
165+
// If the first path part does not match the entry point, we can stop immediately
166+
if ($pathParts[0] !== trim($this->route, '/')) {
165167
return null;
166168
}
167169

168170
// If we have only one segment (for instance "users"), then the next path to analyze is in fact
169171
// an empty string, hence the ternary condition
170-
$pathParts = explode('/', trim($path, '/'), 2);
171-
$subPath = count($pathParts) === 1 ? '' : end($pathParts);
172+
$subPath = count($pathParts) === 1 ? '' : end($pathParts);
172173

173174
if (!$match = $this->subPathMatcher->matchSubPath($this->getResource(), $subPath)) {
174175
// Although this is an error, we still want to create a route match, so that the request

tests/ZfrRestTest/Asset/Resource/Metadata/Annotation/User.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
/**
2626
* @ORM\Entity
2727
* @REST\Resource(hydrator="ClassMethods")
28+
* @REST\Collection(controller="UserListController")
2829
*/
2930
class User
3031
{

tests/ZfrRestTest/Mvc/Controller/MethodHandler/GetHandlerTest.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,13 @@ public function testCanCoalesceFiltering()
109109
$options = new ModuleOptions();
110110
$options->setEnableCoalesceFiltering(true);
111111

112-
$controller = $this->getMock('ZfrRest\Mvc\Controller\AbstractRestfulController', ['get', 'getEvent', 'getRequest']);
113-
$resource = $this->getMock('ZfrRest\Resource\ResourceInterface');
114-
$metadata = $this->getMock('ZfrRest\Resource\Metadata\ResourceMetadataInterface');
112+
$controller = $this->getMock('ZfrRest\Mvc\Controller\AbstractRestfulController', ['get', 'getEvent', 'getRequest']);
113+
$resource = $this->getMock('ZfrRest\Resource\ResourceInterface');
114+
$metadata = $this->getMock('ZfrRest\Resource\Metadata\ResourceMetadataInterface');
115+
$classMetadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata');
115116

116117
$request = new Request();
117-
$request->setQuery(new Parameters(['ids' => [1, 2]]));
118+
$request->setQuery(new Parameters(['$ids' => [1, 2]]));
118119

119120
$data = $this->getMock('Doctrine\Common\Collections\Selectable');
120121

@@ -138,14 +139,22 @@ public function testCanCoalesceFiltering()
138139
->method('getMetadata')
139140
->will($this->returnValue($metadata));
140141

142+
$metadata->expects($this->once())
143+
->method('getClassMetadata')
144+
->will($this->returnValue($classMetadata));
145+
146+
$classMetadata->expects($this->once())
147+
->method('getIdentifierFieldNames')
148+
->will($this->returnValue(['id']));
149+
141150
$data->expects($this->once())
142151
->method('matching')
143152
->with($this->callback(function(Criteria $criteria) {
144153
/* @var \Doctrine\Common\Collections\Expr\Comparison $comparison */
145154
$comparison = $criteria->getWhereExpression();
146155

147156
$this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $comparison);
148-
$this->assertEquals('ids', $comparison->getField());
157+
$this->assertEquals('id', $comparison->getField());
149158
$this->assertEquals([1, 2], $comparison->getValue()->getValue());
150159

151160
return true;

tests/ZfrRestTest/Options/ModuleOptionsTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function testSettersAndGetters()
4646
'object_manager' => 'doctrine',
4747
'register_http_method_override_listener' => false,
4848
'enable_coalesce_filtering' => true,
49-
'coalesce_filtering_key' => 'id',
49+
'coalesce_filtering_query_key' => 'id',
5050
'drivers' => [
5151
['class' => 'foo']
5252
],
@@ -56,7 +56,7 @@ public function testSettersAndGetters()
5656
$this->assertEquals('doctrine', $options->getObjectManager());
5757
$this->assertFalse($options->getRegisterHttpMethodOverrideListener());
5858
$this->assertTrue($options->isEnableCoalesceFiltering());
59-
$this->assertEquals('id', $options->getCoalesceFilteringKey());
59+
$this->assertEquals('id', $options->getCoalesceFilteringQueryKey());
6060
$this->assertEquals('myCache', $options->getCache());
6161
$this->assertCount(1, $options->getDrivers());
6262

0 commit comments

Comments
 (0)