SonataAdminBundle 和 OneToOne 的关系
SonataAdminBundle and OneToOne relationship
我在 Page
和 SuperGridContent
之间存在一对一关系:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="page")
* @ORM\HasLifecycleCallbacks
*/
class Page
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
//...
/**
* @ORM\OneToOne(targetEntity="AppBundle\Entity\SuperGridContent", mappedBy="page", cascade={"persist", "remove"})
*/
protected $super_grid;
//...
}
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="super_grid_content")
* @ORM\HasLifecycleCallbacks
*/
class SuperGridContent
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\OneToOne(targetEntity="AppBundle\Entity\Page", inversedBy="super_grid")
*/
private $page;
//...
}
然后在页面管理 class 我有:
<?php
namespace AppBundle\Admin;
use AppBundle\Entity\Page;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Form\Type\ModelType;
class PageAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('super_grid', ModelType::class, [
'label' => 'Super Grid',
'required' => false,
])
// ...
;
}
// ...
}
问题是,当我创建或编辑 Page
时,super_grid
字段是一个 select 标签,所有现有 SuperGridContent
作为选项。即使他们已经有 Page
关系。如果我 select 其中之一,当然会失败,因为这种关系应该是唯一的。
我是不是遗漏了什么或者索纳塔有什么办法可以处理它吗?
我正在使用 SonataAdminBundle 3.4
如果我对你的问题理解正确,我认为你可以使用 Symfony 实体类型而不是 Sonata documentation.
中所述的模型类型
但我也认为,如果您反转 OneToOne 关系的所有权,您的生活会容易得多。在您的示例中,SuperGridContent
拥有关系,因此当您想要更新 Page
的 super_grid
时,您可能 运行 违反约束。如果您在 SuperGridContent
class 中将 inversedBy
更改为 mappedBy
并且在 Page
class 中将 mappedBy
更改为 inversedBy
(+ 如果你重新生成你的表并确保级联逻辑符合你的需要)你应该没问题。
如果我理解得很好,您希望任何新的 Page
都只有可用的 super_grid
作为选择,而任何现有的 Page
都只有当前的 super_grid
+所有可用的 super_grid
作为选择。
然后像下面这样的东西就可以完成这项工作(注意我使用的是 Symfony 4.4,可能与您的实现有轻微的语法差异):
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Form\FormMapper;
//use Sonata\AdminBundle\Form\Type\ModelType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use App\Entity\Page;
use App\Entity\SuperGridContent;
final class PageAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$subject = $this->getSubject();
if($subject->getId()){
$form
->add('super_grid', EntityType::class, [
'class' => SuperGridContent::class,
'query_builder' => function (EntityRepository $er) use($subject) {
return $er->createQueryBuilder('s')
->leftjoin('s.page', 'p')
->where('p.super_grid is null')
->orWhere('p = :my_page')
->setParameter('my_page', $subject)
;
},
]);
}
else{
$form
->add('super_grid', EntityType::class, [
'class' => SuperGridContent::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->leftjoin('s.page', 'p')
->where('p.super_grid is null')
;
},
]);
}
}
}
我在 Page
和 SuperGridContent
之间存在一对一关系:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="page")
* @ORM\HasLifecycleCallbacks
*/
class Page
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
//...
/**
* @ORM\OneToOne(targetEntity="AppBundle\Entity\SuperGridContent", mappedBy="page", cascade={"persist", "remove"})
*/
protected $super_grid;
//...
}
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="super_grid_content")
* @ORM\HasLifecycleCallbacks
*/
class SuperGridContent
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\OneToOne(targetEntity="AppBundle\Entity\Page", inversedBy="super_grid")
*/
private $page;
//...
}
然后在页面管理 class 我有:
<?php
namespace AppBundle\Admin;
use AppBundle\Entity\Page;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Form\Type\ModelType;
class PageAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('super_grid', ModelType::class, [
'label' => 'Super Grid',
'required' => false,
])
// ...
;
}
// ...
}
问题是,当我创建或编辑 Page
时,super_grid
字段是一个 select 标签,所有现有 SuperGridContent
作为选项。即使他们已经有 Page
关系。如果我 select 其中之一,当然会失败,因为这种关系应该是唯一的。
我是不是遗漏了什么或者索纳塔有什么办法可以处理它吗?
我正在使用 SonataAdminBundle 3.4
如果我对你的问题理解正确,我认为你可以使用 Symfony 实体类型而不是 Sonata documentation.
中所述的模型类型但我也认为,如果您反转 OneToOne 关系的所有权,您的生活会容易得多。在您的示例中,SuperGridContent
拥有关系,因此当您想要更新 Page
的 super_grid
时,您可能 运行 违反约束。如果您在 SuperGridContent
class 中将 inversedBy
更改为 mappedBy
并且在 Page
class 中将 mappedBy
更改为 inversedBy
(+ 如果你重新生成你的表并确保级联逻辑符合你的需要)你应该没问题。
如果我理解得很好,您希望任何新的 Page
都只有可用的 super_grid
作为选择,而任何现有的 Page
都只有当前的 super_grid
+所有可用的 super_grid
作为选择。
然后像下面这样的东西就可以完成这项工作(注意我使用的是 Symfony 4.4,可能与您的实现有轻微的语法差异):
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Form\FormMapper;
//use Sonata\AdminBundle\Form\Type\ModelType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use App\Entity\Page;
use App\Entity\SuperGridContent;
final class PageAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$subject = $this->getSubject();
if($subject->getId()){
$form
->add('super_grid', EntityType::class, [
'class' => SuperGridContent::class,
'query_builder' => function (EntityRepository $er) use($subject) {
return $er->createQueryBuilder('s')
->leftjoin('s.page', 'p')
->where('p.super_grid is null')
->orWhere('p = :my_page')
->setParameter('my_page', $subject)
;
},
]);
}
else{
$form
->add('super_grid', EntityType::class, [
'class' => SuperGridContent::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->leftjoin('s.page', 'p')
->where('p.super_grid is null')
;
},
]);
}
}
}