SonataAdminBundle 和 OneToOne 的关系

SonataAdminBundle and OneToOne relationship

我在 PageSuperGridContent 之间存在一对一关系:

<?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 拥有关系,因此当您想要更新 Pagesuper_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')
                        ;
                },
            ]);
        } 
    }
}