简体   繁体   中英

Sonata Admin MongoDB DataGrid filter

Situation

We are running Symfony 2.8 and the latest version of Sonata Admin along with Mongo as a data store. Please consider the following object which has been simplified for the sake of this question; it does work.

class Entry
{
    /* @ID */
    protected $id;
    /* @String */
    protected $type;
    /* @String */
    protected $content;
}

With the above, there will be lots of entries and from the Admin itself, we would like to filter by type.

Here's an example of the dataset

示例数据集

Problem

We can't create a set of selectable filters in the dataGrid function which are UNIQUE for type.

Attempts

Note that where needed, the EntryRepository is included as a namespace at the start of the file

NUMBER 1

With the below, we get the type duplicated many times

->add('type', null, array(), 'document', array(
    'expanded' => true,
    'class' => 'Application:Entry',
    'query_builder' => function(EntryRepository $dr) { 
        return $dr->createQueryBuilder();
    }
))

第一次尝试

NUMBER 2

With the below, we get a 500 error with only the message "string". I think this is because when using distinct, Mongo prepares a set of arrays instead of unexecuted QueryBuilder object?

->add('type', null, array(), 'document', array(
    'expanded' => true,
    'class' => 'Application:Entry',
    'query_builder' => function(Entryepository $dr) { 
        return $dr->createQueryBuilder()
        ->distinct('type');
    }
))

第二次尝试

NUMBER 3

The attempt below is to use Map reduce to perform the equivalent of an SQL "GROUP BY" however, the same STRING error as above is provided.

->add('type', '', array(), 'document', array(
    'expanded' => true,
    'class' => 'Application:Entry',
    'query_builder' => function(EntryRepository $dr) {
        return $dr->createQueryBuilder()
            ->group(array(), array('type'))
            ->reduce('function (obj, prev) { prev.type; }');
        }
     ))

CRUDE WORKAROUND...discouraged

The below is a demonstration using the filter (as listed in the Sonata documentation) and it DOES work...for one type at a time.

->add('type', 'doctrine_mongo_callback', array(
    'callback' => function($queryBuilder, $alias, $field, $value) {
        if (!$value || $value['value'] == false) {
            return true;
        }
        $queryBuilder
           ->field('type')->equals('fds');
           return true;
    },
    'field_type' => 'checkbox'
))

Taken this approach, I think I'd have to go ahead and query the whole dataset getting the distinct values for type and then loop around each constructing the filter. This would work but be horribly messy.

QUESTION

What is the "best practise" way of performing this without turning the code in to a ruddy mess? Putting the query in a repository will still go and create a similar effect?

Thanks for reading

I'm posting the answer for anyone else who may be facing the same issue. I realised I was approaching the situation from the wrong angle and instead of any of the above, did the following

/* Prepare the options to be used in the filter */
$tagRepo = $this->getConfigurationPool()->getContainer()->get('repository.tag');
$types = $tagRepo->findTypes();
$choices = array();
/* Create a simple choice compatible array */
foreach ($types as $type) { $choices[$type] = $type; }

/* In the actial filter itself */
$datagridMapper
->add('type', 'doctrine_mongo_choice', array(), 'choice',
  array('choices' => $choices))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM