Sonata Admin => 只选择给定的鉴别器映射类型

Posted

技术标签:

【中文标题】Sonata Admin => 只选择给定的鉴别器映射类型【英文标题】:Sonata Admin => Select just the given Discriminator Map type 【发布时间】:2017-07-31 23:51:48 【问题描述】:

主题

当我有一组带有 Doctrine Discriminator Map 的实体时,我无法添加过滤器来仅获取所有映射实体的一种类型,因为 SonataAdminBundle 和/或 SonataDoctrineORMAdminBundle 会引发错误。

示例:

具有 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;
    

然后我添加奏鸣曲管理员过滤器:

protected function configureDatagridFilters(DatagridMapper $filter)

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

预期结果

获取一个新过滤器以仅选择 joinedother 活动。

实际结果

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

堆栈跟踪

根据greg0ire 的要求,这是 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

知道如何解决吗?

谢谢,

【问题讨论】:

你有堆栈跟踪吗? 嗨@greg0ire - 我已按要求添加了堆栈跟踪。希望这可以帮助找到一种方法来获得我需要的东西。 【参考方案1】:

我面临同样的问题。我做了一个工作 - 我使用 doctrine_orm_callback 类型和教义 INSTANCE OF 运算符。

代码如下:

->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(),
            ]
        )

而且它正在工作。也许对你有帮助。

【讨论】:

【参考方案2】:

我认为 Sonata 很困惑,因为它希望 type 出现在您的 Doctrine 映射的字段中。我不认为type 的过滤是支持的,但我记得对单继承有一些支持,尤其是在创建新对象时。不过找不到了。

【讨论】:

嗨@greg0ire - 所以,你认为这是 Doctrine 本身而不是 Sonata 引发的错误,即使 Sonata 有自己的 Doctrine 适配器(因为最终是一个适配器)? 我从 Doctrine 获得了关于我们如何使用鉴别器映射列按该值进行过滤的反馈:github.com/doctrine/doctrine2/issues/6337 - 但是看起来 Sonata 无法管理它(或者我不知道如何去做) 可能是一个不错的贡献。在奏鸣曲学说orm包中查找INSTANCE,如果没有找到,可能是不支持 我会的 - 谢谢! 显然 Filter/ClassFilter.php 有 3 个对 INSTANCE OF 的引用,并且有 1 个自动化测试 (Tests/Filter/ClassFilterTest.php) 应该管理这种情况,因此捆绑包应该已经允许它。根据初步研究,应用此过滤器的方法是管理查询生成器(并添加andWhere 子句以使用o INSTANCE OF :filterValue),但我还没有时间测试它。【参考方案3】:

不建议在创建后修改继承实体与其超类之间的链接。如果你需要它,你应该考虑改用合成。

这就是 Doctrine 不允许直接管理鉴别器字段的原因。

以下所有文字均来自此very useful response,这对于全局和更好地理解非常宝贵。

当对象实例的类型需要随时间改变时,这不是一个好兆头。我在这里不是在谈论向下转换/向上转换,而是在谈论改变对象的真实类型的需要。

首先,让我告诉你为什么这是一个坏主意:

    子类可以定义更多的属性并做一些额外的工作 在它的构造函数中。我们应该再次运行新的构造函数吗?什么 如果它覆盖了我们旧对象的一些属性? 如果您在代码的某些部分中处理该 Person 的实例,然后它突然转变为 Employee(可能有一些您不期望的重新定义行为)怎么办?!

这就是为什么大多数语言不允许您在执行期间更改对象的真实类类型(当然还有内存,但我不想详细说明)的部分原因。有些让你这样做(有时以扭曲的方式,例如 JVM),但这真的不是一个好习惯!

通常情况下,这样做的必要性在于糟糕的面向对象设计决策。

出于这些原因,Doctrine 不允许您更改实体对象的类型。当然,您可以编写纯 SQL(在本文结尾处 - 但请通读!)无论如何都要进行更改,但这里有两个我建议的“干净”选项:

我知道你已经说过第一个选项不是一个选项,但我花了一段时间写下这篇文章,所以我觉得我应该尽可能完整以供将来参考。

    当您需要将类型从Person 更改为Employee 时,创建一个新的Employee 实例并将您想要从旧Person 对象复制的数据复制到Employee 对象。不要忘记删除旧实体并保留新实体。 使用组合而不是继承(参见wiki article了解详细信息和其他文章的链接)。 编辑:该死的,here's a part of a nice conversation with Erich Gamma 关于“组合优于继承”!

查看相关讨论here 和here。


现在,这是我之前谈到的普通 SQL 方法 - 我希望你不需要使用它!

确保您的查询已被清理(因为查询将在没有任何验证的情况下执行)。

$query = "UPDATE TABLE_NAME_HERE SET discr = 'employee' WHERE id = ".$entity->getId();
$entity_manager->getConnection()->exec( $query );

这里是 DBAL\Connection 类中的 exec 方法的文档和代码(供您参考):

/**
 * Execute an SQL statement and return the number of affected rows.
 *
 * @param string $statement
 * @return integer The number of affected rows.
 */
public function exec($statement)

    $this->connect();
    return $this->_conn->exec($statement);

【讨论】:

以上是关于Sonata Admin => 只选择给定的鉴别器映射类型的主要内容,如果未能解决你的问题,请参考以下文章

Sonata Admin 的 sonata_type_model 字段的自定义选项列表

Sonata admin:默认选择马赛克

Sonata Admin - 只允许显示登录用户创建的内容

Sonata Admin+ACL-不同角色的不同仪表板

如果在 Sonata Admin 中选择了单选按钮,如何禁用单选按钮

Sonata admin PRE_SUBMIT 表单事件使 admin twig 变量为空