@@ -177,9 +177,6 @@ custom repository, that will create the query, and wrap it under a paginator:
177177First, your service now delegate to the repository:
178178
179179``` php
180- use DoctrineModule\Paginator\Adapter\Selectable as SelectableAdapter;
181- use Zend\Paginator\Paginator;
182-
183180class TweetService
184181{
185182 private $tweetRepository;
@@ -225,6 +222,121 @@ filtering by first name, last name or age when retrieving a list of users).
225222* You have more complex filtering needs, when you can possibly also filter by properties of associations (for instance,
226223you want to filter by author first name when retrieving a list of tweets).
227224
225+ ### Simple filtering
226+
227+ For filtering fields on the same entity, you can use a Criteria object. The Criteria object is a Doctrine abstraction
228+ that allows to filtering collections (it also works on repository) efficiently. For instance, let's say you want
229+ to filter users by first name and last name. To do that, you allow ` first_name ` and ` last_name ` query params:
230+
231+ ``` php
232+ use Doctrine\Common\Collections\Criteria;
233+
234+ class UserListController extends AbstractRestfulController
235+ {
236+ private $userService;
237+
238+ public function get()
239+ {
240+ // Get all the query params
241+ $queryParams = $this->params()->fromQuery();
242+
243+ // Create the criteria object
244+ $criteria = new Criteria();
245+ $builder = $criteria->expr();
246+
247+ foreach ($queryParams as $key => $value) {
248+ switch ($key) {
249+ case 'first_name':
250+ $criteria->andWhere($builder->eq('firstName', $value));
251+ break;
252+
253+ case 'last_name':
254+ $criteria->andWhere($builder->eq('lastName', $value));
255+ break;
256+ }
257+ }
258+
259+ $users = $this->userService->getAllByCriteria($criteria);
260+
261+ return new ResourceViewModel(['users' => $users]);
262+ }
263+ }
264+ ```
265+
266+ With your ` getAllByCriteria ` creating a paginator, but with the Criteria object to further filtering the data set:
267+
268+ ``` php
269+ use DoctrineModule\Paginator\Adapter\Selectable as SelectableAdapter;
270+ use Zend\Paginator\Paginator;
271+
272+ class UserService
273+ {
274+ private $userRepository;
275+
276+ public function getAllByCriteria(Criteria $criteria)
277+ {
278+ return new Paginator(new SelectableAdapter($this->tweetRepository, $criteria));
279+ }
280+ }
281+ ```
282+
283+ > As you can see, we can easily combine filtering and pagination thanks to the power of the Criteria API!
284+
285+ However, there is a problem with this approach: our controller is now polluted with code that is difficult to
286+ test in isolation, and cannot be reused elsewhere. To solve this problem, we introduce a new kind of objects: the
287+ Criteria objects.
288+
289+ Those objects simply extend the base Criteria, but know how to build the criteria object. For instance, here is
290+ a UserCriteria object:
291+
292+ ``` php
293+ class UserCriteria extends Criteria
294+ {
295+ public function __construct(array $filters = [])
296+ {
297+ $builder = $this->expr();
298+
299+ foreach ($queryParams as $key => $value) {
300+ switch ($key) {
301+ case 'first_name':
302+ $this->andWhere($builder->eq('firstName', $value));
303+ break;
304+
305+ case 'last_name':
306+ $this->andWhere($builder->eq('lastName', $value));
307+ break;
308+ }
309+ }
310+ }
311+ }
312+ ```
313+
314+ Your controller now become:
315+
316+ ``` php
317+ use Doctrine\Common\Collections\Criteria;
318+
319+ class UserListController extends AbstractRestfulController
320+ {
321+ private $userService;
322+
323+ public function get()
324+ {
325+ $criteria = new UserCriteria($this->params()->fromQuery(null, []));
326+ $users = $this->userService->getAllByCriteria($criteria);
327+
328+ return new ResourceViewModel(['users' => $users]);
329+ }
330+ }
331+ ```
332+
333+ Much cleaner!
334+
335+ ### Complex filtering
336+
337+ This works well when filtering on fields that belong to the entity. However, the Criteria API is a rather limited API,
338+ and does not allow things such as filtering on association using joins. We therefore need to resolve to a similar
339+ approach, but without using the Criteria API itself.
228340
229341* Back to [ ** Using HTTP exceptions for reporting errors** ] (/docs/04. Using HTTP exceptions for reporting errors.md)
230342* Back to [ the Index] ( /docs/README.md )
0 commit comments