如何防止 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->setDefined(['myparent'])
或setRequired
或setDefault
添加到$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 在与同一实体(父)的多对一关系中显示当前对象?的主要内容,如果未能解决你的问题,请参考以下文章
Symfony 表单 - EntityType 通过实体 ID 选择选项