Arash Mousavi
Arash Mousavi

Reputation: 2148

Doctrine add condition to createQueryBuilder if condition provided

I have following function:

public function latestNews($tags = array(), $categories = array(), $authors = array(), $lang = 'en', $source = '', $limit = 20) {
    return $this->createQueryBuilder('News')
    ->field('tags')->in($tags)
    ->field('categories')->in($category)
    ->field('authors')->in($authors)
    ->field('lang')->equals($lang)
    ->sort('date' -> 'DESC')
    ->field('source')->equals($source)
    ->limit($limit)
    ->getQuery()
    ->execute();
}

I want if variables such as $tags, $categories, $authors or $source provided by function caller this variables affect on the createQueryBuilder, but if each of them does not provide by the function caller(variable with default value) they don't affect createQueryBuilder and make this condition neutral on query. One way is I make the query with many if condition but it is very messy. Is there any better solution?

Upvotes: 1

Views: 3048

Answers (2)

xthiago
xthiago

Reputation: 484

The chalasr's response is good, but I suggest extract the logic of build conditions to a specific trait, so you avoid code duplication (DRY).

You can do something like:

<?php

namespace Xthiago\My\Path;

use \Doctrine\DBAL\Query\QueryBuilder;

trait DoctrineQueryHelper
{
    public function in(QueryBuilder $qb, array $filter, $field)
    {
        if (empty($filter[$field])) {
            return $this;
        }

        $qb->field($field)->in($filter[$field]);

        return $this;
    }

    public function equals(QueryBuilder $qb, array $filter, $field)
    {
        if (empty($filter[$field])) {
            return $this;
        }

        $qb->field($field)->equals($filter[$field]);

        return $this;
    }

    public function lang(QueryBuilder $qb, $value = 'en')
    {
        $qb->field('lang')->equals($value);

        return $this;
    }

    public function limit(QueryBuilder $qb, $value = 20)
    {
        $qb->limit($value);

        return $this;
    }

    // you can create a lot of helper methods here in order to avoid duplicity.
}

Then your class make use of the trait in this way:

<?php

namespace Xthiago\My\Path;

use \Doctrine\DBAL\Query\QueryBuilder;

class NewsRepository
{
    use DoctrineQueryHelper;

    /**
     * @param array $filter as follow:
     * <code>
     * [
     *  'tags' => [],
     *  'categories' => [],
     *  'authors' => [],
     *  'lang' => 'en',
     *  'source' => '',
     *  'limit' => 20,
     * ]
     * </code>
     *
     * @return array with results.
     */
    public function latestNews(array $filter = []) 
    {
        $qb = $this->createQueryBuilder('News');

        $this->in($qb, $filter, 'tags')
             ->in($qb, $filter, 'categories')
             ->in($qb, $filter, 'authors')
             ->equals($qb, $filter, 'source')
             ->lang($qb, $filter)
             ->limit($qb, $filter);

        return $qb->sort('date', 'DESC')
                  ->getQuery()
                  ->execute();
    }
}

Upvotes: 1

chalasr
chalasr

Reputation: 13167

Something like this should do the trick:

public function latestNews($tags = array(), $categories = array(), authors = array(), $lang = 'en', $source = '', $limit = 20) {
    $inClauses = ['tags', 'categories', 'authors'];
    $equalClauses = ['lang', 'source'];
    $qb = $this->createQueryBuilder('News');

    foreach ($inClauses as $field) {
        $realVar = ${$field};

        if (!empty($realVar)) {
            $qb->field($field)->in($realVar);
        }
    }

    foreach ($equalClauses as $field) {
        $realVar = ${$field};

        if ($realVar) {
            $qb->field($field)->equals($realVar);
        }
    }

    return $qb
        ->sort('date' -> 'DESC')
        ->limit($limit)
        ->getQuery()
        ->execute();
}

A bit ugly, but I don't see any better alternative.

Upvotes: 4

Related Questions