Search code examples
symfonydoctrine-ormsonata-adminsonata

Sonata Admin => Select just the given Discriminator Map type


Subject

When I have a set of entities with a Doctrine Discriminator Map then I cannot add a filter to get just one type of all mapped entities, due SonataAdminBundle and/or SonataDoctrineORMAdminBundle are throwing an error.

Example:

Entities with a Doctrine Discriminator Map

/**
 * @ORM\Table(name="activities")
 * @ORM\Entity()
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({
 *     "joined" = "...\JoinedActivity",
 *     "other" = "...\OtherActivity"
 * })
 */
abstract class Activity()
{
    abstract public function getType();
}

/**
 * @ORM\Entity()
 */
class JoinActivity extends Activity()
{
    const TYPE = 'joined';

    public function getType()
    {
        return self::type;
    }
}

/**
 * @ORM\Entity()
 */
class OtherActivity extends Activity()
{
    const TYPE = 'other';

    public function getType()
    {
        return self::type;
    }
}

Then I add the Sonata Admin filter:

protected function configureDatagridFilters(DatagridMapper $filter)
{
    $filter->add(
        'type',
        null,
        [
            'label' => 'Activity Type',
        ],
        'choice',
        [
            'choices' => [
                JoinActivity::TYPE => ucfirst(JoinActivity::TYPE),
                OtherActivity::TYPE => ucfirst(OtherActivity::TYPE),
            ],
        ]
    );
}

Expected results

Get a new filter to select just joined or other activities.

Actual results

Notice: Undefined index: type
500 Internal Server Error - ContextErrorException

Stack trace

As requested by greg0ire this is the Stack Trace returned by Symfony/Sonata:

[1] Symfony\Component\Debug\Exception\ContextErrorException: Notice: Undefined index: type
    at n/a
        in /path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Guesser/FilterTypeGuesser.php line 69

    at Symfony\Component\Debug\ErrorHandler->handleError('8', 'Undefined index: type', '/path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Guesser/FilterTypeGuesser.php', '69', array('class' => 'AppBundle\EntityBundle\Entity\Activity', 'property' => 'type', 'modelManager' => object(ModelManager), 'ret' => array(object(ClassMetadata), 'type', array()), 'options' => array('field_type' => null, 'field_options' => array(), 'options' => array(), 'parent_association_mappings' => array()), 'metadata' => object(ClassMetadata), 'propertyName' => 'type', 'parentAssociationMappings' => array()))
        in /path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Guesser/FilterTypeGuesser.php line 69

    at Sonata\DoctrineORMAdminBundle\Guesser\FilterTypeGuesser->guessType('AppBundle\EntityBundle\Entity\Activity', 'type', object(ModelManager))
        in /path/to/symfony/project/app/cache/dev/classes.php line 15104

    at Sonata\AdminBundle\Guesser\TypeGuesserChain->Sonata\AdminBundle\Guesser\{closure}(object(FilterTypeGuesser))
        in /path/to/symfony/project/app/cache/dev/classes.php line 15111

    at Sonata\AdminBundle\Guesser\TypeGuesserChain->guess(object(Closure))
        in /path/to/symfony/project/app/cache/dev/classes.php line 15105

    at Sonata\AdminBundle\Guesser\TypeGuesserChain->guessType('AppBundle\EntityBundle\Entity\Activity', 'type', object(ModelManager))
        in /path/to/symfony/project/vendor/sonata-project/doctrine-orm-admin-bundle/Builder/DatagridBuilder.php line 105

    at Sonata\DoctrineORMAdminBundle\Builder\DatagridBuilder->addFilter(object(Datagrid), null, object(FieldDescription), object(ActivityAdmin))
        in /path/to/symfony/project/app/cache/dev/classes.php line 13069

    at Sonata\AdminBundle\Datagrid\DatagridMapper->add('type', null, array('label' => 'Activity Type', 'field_options' => array('choices' => array('joined' => 'Joined')), 'field_type' => 'choice', 'field_name' => 'type'), 'choice', array('choices' => array('joined' => 'Joined')))
        in /path/to/symfony/project/src/AppBundle/SonAdminBundle/Admin/ActivityAdmin.php line 64

    at AppBundle\SonAdminBundle\Admin\ActivityAdmin->configureDatagridFilters(object(DatagridMapper))
        in /path/to/symfony/project/app/cache/dev/classes.php line 10609

    at Sonata\AdminBundle\Admin\AbstractAdmin->buildDatagrid()
        in /path/to/symfony/project/app/cache/dev/classes.php line 10910

    at Sonata\AdminBundle\Admin\AbstractAdmin->getDatagrid()
        in /path/to/symfony/project/vendor/sonata-project/admin-bundle/Controller/CRUDController.php line 104

    at Sonata\AdminBundle\Controller\CRUDController->listAction()
        in  line 

    at call_user_func_array(array(object(CRUDController), 'listAction'), array())
        in /path/to/symfony/project/app/bootstrap.php.cache line 3222

    at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), '1')
        in /path/to/symfony/project/app/bootstrap.php.cache line 3181

    at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), '1', true)
        in /path/to/symfony/project/app/bootstrap.php.cache line 3335

    at Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle(object(Request), '1', true)
        in /path/to/symfony/project/app/bootstrap.php.cache line 2540

    at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
        in /path/to/symfony/project/web/app_dev.php line 15

    at require('/path/to/symfony/project/web/app_dev.php')
        in /path/to/symfony/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php line 40

Any idea how I can fix it?

Thanks,


Solution

  • I face the same issue. I did a work around - I use doctrine_orm_callback type with doctrine INSTANCE OF operator.

    Code looks like this:

    ->add('userType',
                'doctrine_orm_callback',
                [
                    'callback' => function ($queryBuilder, $alias, $field, $value) {
                        if (!is_array($value) || !array_key_exists('value', $value) || empty($value['value'])) {
                            return false;
                        }
    
                        $queryBuilder->andWhere($alias . ' INSTANCE OF :userType');
                        $queryBuilder->setParameter('userType', $value['value']);
    
                        return true;
                    },
                ],
                ChoiceType::class,
                [
                    'choices' => array_flip(UserType::getChoices()),
                    'translation_domain' => $this->getTranslationDomain(),
                ]
            )
    

    And it's working. Maybe it helps you.