:::: MENU ::::

Doctrine e Symfony2 como filtrar dados e aproveitar o Lazy Loading

Fala gurizada medonha da internet, hoje vou falar de como melhorar uma coisa que ja é boa, o lazy loading do doctrine.

Muitos vezes temos inúmeras relações em nossas entidades, e o lazy loading nos ajuda muito pois ele deixa para fazer a query dessa relação somente quando for necessário.

 

Mas em muitos casos precisamos colocar uma regra em certo ponto de nossa relação, digamos que eu busque todos os meus usuarios do site, e usando o lazy loading eu faço o seguinte


Usuarios->getGrupos()->getRegras()

Isso é muito comum de fazermos, mas digamos que em certo ponto queremos buscar somente os grupos do tipo 1 que é admin, bom e agora, como colocamos uma regra no lazy loading? se ele caminha usando as nossas chaves estrangeiras da relação?

Eis que surge o Doctrine Filter, uma das 10 maravilhas do mundo programático, uma maneira elegante e reutilizarmos de termos filtros customizados.

bom sem mais lero lero, vou mostrar na pratica como podemos fazer.

Parte 1: criando uma referencia a o filtro, podemos usar uma annotation ou uma interface na entidade, dai fica a critério de quem for implementar.

Meu exemplo eu crio uma annotation

<?php

namespace Acme\DemoBundle\Annotation;

use Doctrine\Common\Annotations\Annotation;

/**
 * @Annotation
 * @Target("CLASS")
 */
final class UserAware  
{
    public $userFieldName;
}

Parte 2:

Agora na minha entidade eu uso esta annotation

<?php

namespace Acme\DemoBundle\Entity;

use Acme\DemoBundle\Annotation\UserAware;

/**
 * Order entity
 *
 * @UserAware(userFieldName="user_id")
 */
class Order { ... }  

Basicamente isso ai que ira fazer a magica de adicionar mais um WHERE em nossa query.

Parte 3: o filtro

Bom agora é so criarmos o filtro, quando estendemos de SQLFilter uma classe do Doctrine, ele nos da a opção de sobrescrever o método addFilterConstraint que por padrão recebe uma entidade e o alias da mesma na query.

<?php

namespace Acme\DemoBundle\Filter;

use Doctrine\ORM\Mapping\ClassMetaData;  
use Doctrine\ORM\Query\Filter\SQLFilter;  
use Doctrine\Common\Annotations\Reader;

class UserFilter extends SQLFilter  
{
    protected $reader;

    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
    {
        if (empty($this->reader)) {
            return '';
        }

        // The Doctrine filter is called for any query on any entity
        // Check if the current entity is "user aware" (marked with an annotation)
        $userAware = $this->reader->getClassAnnotation(
            $targetEntity->getReflectionClass(),
            'Acme\\DemoBundle\\Annotation\\UserAware'
        );

        if (!$userAware) {
            return '';
        }

        $fieldName = $userAware->userFieldName;

        try {
            // Don't worry, getParameter automatically quotes parameters
            $userId = $this->getParameter('id');
        } catch (\InvalidArgumentException $e) {
            // No user id has been defined
            return '';
        }

        if (empty($fieldName) || empty($userId)) {
            return '';
        }

        $query = sprintf('%s.%s = %s', $targetTableAlias, $fieldName, $userId);

        return $query;
    }

    public function setAnnotationReader(Reader $reader)
    {
        $this->reader = $reader;
    }
}

Parte 4:

Agora no config.yml da nossa aplicação, vamos ativar nosso filtro

doctrine:  
    orm:
        filters:
            user_filter:
                class:   Acme\DemoBundle\Filter\UserFilter
                enabled: true

se deixarmos enabled como true. esse filtro sera sempre feito, em todas as queryes que a entidade fizer.

Caso queira ativar somente em certos momentos pode ser feito algo do tipo

$filter = $em->getFilters()->enable("user_filter");

Quem quiser dar mais uma lida sobre os filtros do Doctrine segue a documentação do mesmo

http://doctrine-orm.readthedocs.org/en/latest/reference/filters.html

Espero ter ajudado, um grande abraço!