如何防止 EntityType 在与同一实体(父)的多对一关系中显示当前对象?

Posted

技术标签:

【中文标题】如何防止 EntityType 在与同一实体(父)的多对一关系中显示当前对象?【英文标题】:How to prevent EntityType to display current object in a ManyToOne relationship to the same Entity (Parent)? 【发布时间】:2019-08-29 14:07:06 【问题描述】:

我有一个与自身有ManyToOne 关系的实体,因为它的对象可以有相同类型的父对象。

我扩展了EntityType 以显示实体中的对象,但我不希望当前对象显示在选择列表中,因为对象不能是其自身的父对象。

要按所有者过滤对象,我正在做这样的事情

public function configureOptions(OptionsResolver $resolver)

    $resolver->setDefault('query_builder', function (Options $options) 
        return function (EntityRepository $er) use ($options) 
            return $er->createQueryBuilder('con')
                ->orderBy('con.name', 'ASC')
                ->andWhere('con.owner = :owner')
                ->setParameter('owner', $this->getLoggedUser());
        ;
    );

但我不知道如何让当前对象添加 andWhere 子句以将其从选择列表中删除。

那么,知道如何从 EntityType 的选择列表中删除正在编辑的对象吗?

【问题讨论】:

所以,你想要 con!=loggedUser?那么......只是->andWhere('con != :owner')(因为在这种非常特殊的情况下它的所有者相同)还是我错过了什么? 不,我有登录用户。假设实体是文件夹。所有者创建 FolderA 和 FolderB。 entitytype 将列出 FolderA 和 FolderB 被选为 FolderB 父级,这是没有意义的。我想从实体列表中排除正在编辑的对象。这是我的真实代码github.com/devaneando/CWCompanion/blob/master/src/Admin/Type/… 我假设您的仓库是私有的;o) 但是,您可以通过选项注入“父”对象并在您的函数中使用它来过滤掉它,不是吗? 一点也不。可能有错别字:github.com/devaneando/CWCompanion/blob/master/src/Admin/Type/… 【参考方案1】:

感谢michał-tomczuk 和感谢jakumi,你们都非常有帮助,但我猜你们的建议不起作用,因为FormType 是在AdminType 之前创建的,并且“数据”在选项中不可用上下文呢。至少我在任何地方都找不到。

我尝试了另一种可行的方法,即使它有点矫枉过正。

由于表单类型总是用于编辑上下文,在我的例子中,在 SonataAdmin 范围内,我将路由器注入到类型中,并通过我能够从 PathInfo 中检索到的对象 id 过滤查询:

<?php
use Symfony\Component\Routing\RouterInterface;

/** @var RouterInterface $router */
private $router;  


/** @inheritdoc */
public function configureOptions(OptionsResolver $resolver)

    $resolver->setDefault('query_builder', function (Options $options) 
        return function (EntityRepository $er) use ($options) 
            $matches = [];
            preg_match(
                '/^.*\/([0-9a-z\-]+)\/edit$/',
                $this->router->getContext()->getPathInfo(),
                $matches
            );

            $queryBuilder = $er->createQueryBuilder('ent')
                ->orderBy('ent.name', 'ASC')
            if (true === array_key_exists('1', $matches)) 
                $queryBuilder
                    ->andWhere('ent.id != :id')
                    ->setParameter('id', $matches[1]);
            
            return $queryBuilder;
        ;
    );

我知道这是一个肮脏的解决方案,但有效。

【讨论】:

出于好奇:尝试通过$options 提供父对象时,您可能不应该将其设置为data,而是设置为尚未保留的内容,例如...“myparent”或一些东西,你必须用$resolver-&gt;setDefined(['myparent'])setRequiredsetDefault添加到$resolver,这样OptionsResolver就不会阻塞它。 (见symfony.com/doc/current/components/options_resolver.html)我觉得这种方法会更干净 要去检查一下。谢谢!【参考方案2】:

为了修复您当前的代码,您可以使用$options['data'],因为它包含表单的基础数据。我相信在您的情况下,这将是(例如)Folder 实体。试试这个(注意这里我假设底层实体是持久化的):

$resolver->setDefault('query_builder', function (Options $options) 
    return function (EntityRepository $er) use ($options) 
        return $er->createQueryBuilder('con')
            ->orderBy('con.name', 'ASC')
            ->andWhere('con != :current')
            ->andWhere('con.owner = :owner')
            ->setParameter('current', $options['data'])
            ->setParameter('owner', $this->getLoggedUser())
        ;
    ;
);

如果基础实体可能尚未持久化(例如添加新的Folder),您必须稍微修改代码以解决此问题:

$resolver->setDefault('query_builder', function (Options $options) 
    return function (EntityRepository $er) use ($options) 
        $qb = $er->createQueryBuilder('con')
            ->andWhere('con.owner = :owner')
            ->setParameter('owner', $this->getLoggedUser())
            ->orderBy('con.name', 'ASC')
        ;

        $current = $options['data'];
        if (null !== $current->getId()) 
            $qb
                ->andWhere('con != :current')
                ->setParameter('current', $options['data'])
            ;
        

        return $qb;
    ;
);

希望对您有所帮助。

【讨论】:

以上是关于如何防止 EntityType 在与同一实体(父)的多对一关系中显示当前对象?的主要内容,如果未能解决你的问题,请参考以下文章

扩展 EntityType,仅将选项设置为选定的实体

Symfony 表单 - EntityType 通过实体 ID 选择选项

在与父进程相同的监视器上运行进程

symfony2 对子实体的验证防止编辑父实体

当属性值来自引用表时,应该如何填充实体(bean)对象属性?

如何防止 iframe 访问父框架?