Simple Filtering in Twig


In this video we will add in a very simple filter input to our Twig CRUD input, allowing the end user to add in some text which must be contained in the blog post's title field to be shown in the result set.

Before proceeding further with this it is best to be aware that this is a very simplistic approach. The query we will use is a LIKE query combined with %'s at the beginning and the end of the string. This will cause a full table scan which is particularly poor for performance, and doesn't scale well at all.

A better alternative - if you must use MySQL in this way - is to use FULLTEXT search. I haven't tried this with Doctrine myself, but this article may be a good starting place. This is a technique I used to use heavily prior to using frameworks, and I can vouch for how well it works.

Doctrine does support FULLTEXT indexes, but I cannot find any documentation on how this should be used.

To implement full text searching today I would be strongly advising you towards Elasticsearch. There is a bundle for this already, the FOSElasticaBundle.

In this example, however, the implementation is basic enough to illustrate the point, but note that you can easily change the implementation without having to change how the filter parameter is passed by the URL, as we shall see momentarily.

To put this another way, the URL we for filtering may look something like this:

http://oursite.dev/app_dev.php/?filter=the

Which behind the scenes would do the following:

    public function cgetAction(Request $request)
    {
        $queryBuilder = $this->getBlogPostRepository()->createFindAllQuery();

        if ($request->query->getAlnum('filter')) {
            $queryBuilder
                ->where('bp.title LIKE :title')
                ->setParameter('title', '%' . $request->query->getAlnum('filter') . '%');
        }

        return $this->get('knp_paginator')->paginate(
            $queryBuilder->getQuery(), /* query NOT result */
            $request->query->getInt('page', 1), /*page number*/
            $request->query->getInt('limit', 10)/*limit per page*/
        );
    }

The 'interesting' part here being:

        if ($request->query->getAlnum('filter')) {
            $queryBuilder
                ->where('bp.title LIKE :title')
                ->setParameter('title', '%' . $request->query->getAlnum('filter') . '%');
        }

Let's quickly dissect this:

$queryBuilder = $this->getBlogPostRepository()->createFindAllQuery();

This goes off to our BlogPostRepository class and runs the createFindAllQuery method:

<?php

// /src/AppBundle/Entity/Repository/BlogPostRepository.php

namespace AppBundle\Entity\Repository;

use Doctrine\ORM\EntityRepository;

class BlogPostRepository extends EntityRepository
{
    public function createFindAllQuery()
    {
        return $this->_em->getRepository('AppBundle:BlogPost')->createQueryBuilder('bp');
    }

    // * snip *
}

As you can see, this will return a QueryBuilder object - not a query itself.

We can then use this QueryBuilder object again in our controller to add a where clause, only if the Request object contained a filter parameter:

        if ($request->query->getAlnum('filter')) {
            // do stuff here
        }

As I mentioned earlier, in this example we use a basic LIKE query, but this is where you could switch to any other implementation - and still the URL would remain the same.

At this stage we have all the required knowledge to implement this exact setup for our API. So let's get on with that in the very next video.

Code For This Video

Get the code for this video.

Episodes