William Espindola
William Espindola

Reputation: 29

How to make a query using Between with Doctrine and ORM

Work with Docrtine 2 and Zend Framework 2 I have a service that work like this:

class TransactionService 
{
    public function __construct(TransactionMapperInterface $transactionMapper)
    {
        $this->transactionMapper = $transactionMapper;
    }

    public function findBy($conditions = array(), $order = array(), $limit = null, $offset = null)
    {
        return $this->transactionMapper->findBy($conditions, $order, $limit, $offset);
    }
}

But I need to query with BETWEEN condition like this result:

select * from transactions where created between '2014-02-01' and '2014-03-01';

This is possible with findBy method?

Upvotes: 0

Views: 1745

Answers (2)

William Espindola
William Espindola

Reputation: 29

I found another simpler way to do this. Ok, with findBy this is not possible, but we can use createQueryBuilder, see a litte example:

$qb = $this->transactionMapper->createQueryBuilder('transaction');

$query = $qb->select('t')
            ->from('Transaction\Entity\Transaction', 't')
            ->where($qb->expr()->between('t.created', "'2014-02-01'", "'2014-03-01'"));

return $query->getQuery()->getResult();

See documentation The QueryBuilder

Upvotes: 2

user771071
user771071

Reputation:

In short, no. findBy is only used for simple querying, and your BETWEEN is not part of that.

However it can be done relatively simple: I would use a Doctrine\ORM\Query\Filter\SQLFilter to do this kind of query, which is a new feature in since Doctrine 2.2.

The way this works is that create a class that extends from the base SQLFilter class, and add your own filter constraints to them. When the filter is enabled, any query after that that is based on that specific entity will get this filter applied.

For your specific problem, you could do something like the following (assuming that the transactions table is actually an entity called ORM\Entity\Transactions.

<?php

namespace ORM\Filter;

use Doctrine\ORM\Mapping\ClassMetaData;
use Doctrine\ORM\Query\Filter\SQLFilter;

/**
 * This filter filters on records between two given dates.
 */
class BetweenFilter extends SQLFilter
{
    public function addFilterConstraint(ClassMetaData $targetEntity, $targetTableAlias)
    {
        // First check if the entity implements the BeteenAware interface
        if (!$targetEntity->reflClass->implementsInterface('ORM\Filter\BetweenAware')) {
            return "";
        }

        // We know now that our table supports the between entity, please note
        // that getParameter automatically quotes variables
        return sprintf(
            '(created BETWEEN %s AND %s)',
            $this->getParameter('betweenFrom'),
            $this->getParameter('betweenTo')
        );
    }
}

Now what we want to do is create a ORM\Filter\BetweenAware interface, that our ORM\Entity\Transactions needs to implement so that Doctrine knows that is able to filter on these fields. The interface is only for identification purposes only, so it is very short.

<?php

namespace ORM\Filter;

/**
 * BetweenAware defines that an Entity is able to filter on the
 * 'created' field.
 */
interface BetweenAware {}

All that is left at this point, is add the filter, set the parameters and enable the filtering.

// We'll ASSUME that the $config variable is a valid Doctrine configuration object retrieved 
// by instances such as `Setup::createAnnotationMetadataConfiguration`. On the other hand
// the variable $connection is assumed to contain valid connection details.

// First add the filter to the configuration
$config->addFilter('between', 'ORM\Filter\BetweenFilter');

// Now create the EntityManager
$entityManager = EntityManager::create($connection, $config);

// We will now enable our filter and set the parameters
$entityManager
->getFilters()
->enable('between')
->setParameter('betweenFrom', '2014-02-01')
->setParameter('betweenTo', '2014-03-01')
;

// Now for the moment supreme, lets get all the records back
$records = $entityManager
->getRepository('ORM\Entity\Transactions')
->findAll()
;

I have not tested the code, so I am not sure if it is without faults, but this is definitely a good way of filtering on records without all the mess.

Hopefully this will answer your question. I don't mind down voting, but please let me know why so we can all learn.

DO NOT FORGET to add the ORM\Filter\BetweenAware interface to the Entity class, otherwise the filter will be ignored.

Upvotes: 2

Related Questions