Sonata Admin =>选择给定的Discriminator Map类型

时间:2017-03-10 15:13:32

标签: symfony doctrine-orm sonata-admin sonata

主题

当我有一组带有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;
    }
}

然后我添加了Sonata Admin过滤器:

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返回的Stack Trace:

[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

知道如何解决它吗?

谢谢,

3 个答案:

答案 0 :(得分:2)

我面临同样的问题。我做了一个解决方法 - 我使用 doctrine_orm_callback 类型与doctrine INSTANCE OF 运算符。

代码如下所示:

var db = 'telegraf'

var rp = 'autogen'

var measurement = 'procstat'

var groupBy = []

var whereFilter = lambda: ("process_name" == 'dnsmasq')

var period = 1m

var name = 'Dnsmasq Deadman'

var idVar = name + ':{{.Group}}'

var message = 'Dnsmasq not responding. {{.Time}} Restarting dnsmasq. {{ index .Tags "host"}}'

var idTag = 'alertID'

var levelTag = 'level'

var messageField = 'message'

var durationField = 'duration'

它正在发挥作用。也许它可以帮助你。

答案 1 :(得分:0)

我认为Sonata很困惑b / c它希望toArray()出现在你的Doctrine映射的字段中。我不认为$response = $paginated->toArray(); $response['filters'] = []; 的过滤是支持的,但我记得对单继承有一些支持,特别是在创建新对象时。但是找不到它。

答案 2 :(得分:0)

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

这就是为什么Doctrine不允许直接管理区分项字段的原因。

以下所有文本均从此very useful response复制而来,这对于全面和更好地理解是宝贵的。

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

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

  1. 子类可能会定义更多属性并进行其他工作 在它的构造函数中。我们是否应该再次运行新的构造函数?什么 是否覆盖了旧对象的某些属性?
  2. 如果您在代码的某个部分中正在处理该Person的实例,然后突然变成了Employee(它可能具有某些您不希望看到的重新定义的行为),该怎么办?

这就是大多数语言不允许您在执行过程中更改对象的真实类类型的原因的一部分(当然,还有内存,但我不想详细介绍)。有些人可以让您做到这一点(有时以多种方式使用,例如JVM),但这确实不是一个好习惯!

通常,这样做的需要在于糟糕的面向对象设计决策。

由于这些原因,Doctrine不允许您更改实体对象的类型。当然,您仍然可以编写普通的SQL(在本文结尾-但请通读!)来进行更改,但是我建议您使用以下两个“干净”选项:

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

  1. 每当您需要将类型从Person更改为Employee时,请创建一个新的Employee实例,然后将要复制的数据从旧的Person对象复制到Employee对象。不要忘记删除旧实体并保留新实体。
  2. 使用合成代替继承(有关详细信息和指向其他文章的链接,请参见此wiki article)。 编辑:关于“构成继承”的问题here's a part of a nice conversation with Erich Gamma

请参阅相关讨论herehere


现在,这是我之前提到的简单的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);
}