Doctrine сохраняет только одно из многих многоотношений - PullRequest
1 голос
/ 26 февраля 2020

Есть три сущности, связанные отношениями ManyToOne: «Страница» может иметь много «Блоков». (Блок может быть связан с одной страницей). «Блок» может иметь много «кнопок», («кнопка» может находиться в одном блоке).

У меня есть система форм, в которой я могу добавить несколько блоков, а внутри каждого блока - несколько кнопок. Таким образом, есть встроенная форма внутри встроенного для (2 слоя)

Проблема заключается в том, что при сохранении нескольких файлов в базу данных сохраняется только одна кнопка.

Но когда я сохраняю одну кнопку Я могу go вернуться и сохранить несколько сразу. Я не могу понять это поведение.

/**
 * @ORM\Entity(repositoryClass="App\Repository\PageRepository")
 */
class Page
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    ...

    /**
    * @ORM\OneToMany(targetEntity="App\Entity\Block", mappedBy="page", orphanRemoval=true, cascade={"persist"})
     * @ORM\OrderBy({"ordering" = "ASC"})
     */
    private $blocks;
    ...

    public function __construct()
    {
         $this->blocks = new ArrayCollection();
     }

    /**
     * @return Collection|Block[]
     */
    public function getBlocks(): Collection
    {
        return $this->blocks;
    }

    public function addBlock(Block $block): self
    {
        if (!$this->blocks->contains($block)) {
            $this->blocks[] = $block;
            $block->setPage($this);
        }

        return $this;
    }

    public function removeBlock(Block $block): self
    {
         if ($this->blocks->contains($block)) {
            $this->blocks->removeElement($block);
            // set the owning side to null (unless already changed)
            if ($block->getPage() === $this) {
                $block->setPage(null);
            }
          }

        return $this;
    }
 }

Блок:

/**
 * @ORM\Entity(repositoryClass="App\Repository\BlockRepository")
 */
class Block
{
     /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
     private $id;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Block\Button", mappedBy="block", 
        orphanRemoval=true, cascade={"persist"})
     */
    private $buttons;

   /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Page", inversedBy="blocks")
     * @ORM\JoinColumn(nullable=false)
     */
    private $page;

    public function __construct()
    { 
        $this->buttons = new ArrayCollection();
    }

    /**
     * @return Collection|Button[]
     */
    public function getButtons(): Collection
    {
        return $this->buttons;
    }

    public function addButton(Button $button): self
    {
        if (!$this->buttons->contains($button)) {
            $this->buttons[] = $button;
            $button->setBlock($this);
        }

        return $this;
    }

    public function removeButton(Button $button): self
    {
        if ($this->buttons->contains($button)) {
            $this->buttons->removeElement($button);
            // set the owning side to null (unless already changed)
            if ($button->getBlock() === $this) {
                $button->setBlock(null);
            }
        }

        return $this;
    }   
}

Кнопка:

/**
 * @ORM\Entity(repositoryClass="App\Repository\Block\ButtonRepository")
 * @ORM\Table(name="block_button")
 */
class Button
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Block", inversedBy="buttons")
     * @ORM\JoinColumn(nullable=false)
     */
    private $block;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $label;

    public function getBlock(): ?Block
    {
        return $this->block;
    }

    public function setBlock(?Block $block): self
    {
        $this->block = $block;

        return $this;
    }
 }

Ниже приведены формы, соответствующие типам форм:

PageType:

class PageType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('blocks', CollectionType::class, [
                'entry_type' => BlockType::class,
                'entry_options' => ['label' => false],
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'help' => '<a data-collection="add" class="btn btn-info btn-sm" href="#">Add Block</a>',
                'help_html' => true
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Page::class,
        ]);
    }

}

BlockType

class BlockType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('buttons', CollectionType::class, [
                'entry_type' => BlockButtonType::class,
                'entry_options' => ['label' => false],
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'help' => '<a data-collection="add" class="btn btn-info btn-sm" href="#">Add Button</a>',
                'help_html' => true,
                'attr' => [
                    'data-field' => 'buttons'
                ]
            ])
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Block::class,
        ]);
    }
}

BlockButtonType

class BlockButtonType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('label');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Button::class,
        ]);
    }
}

Ответственные методы для обработки новой формы и измените их следующим образом:

/**
 * @Route("/admin/pages")
 */
class PageController extends AbstractController
{
    /**
     * @Route("/new", name="admin_page_new", methods={"GET","POST"})
     */
    public function new(Request $request, FileUploader $fileUploader):Response
    {
        $page = new Page();
        $form = $this->createForm(PageType::class, $page);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            if ($form->get('publish')->isClicked()) {
                $page->setPublished(true);
            }

            // handle uploads here
            $this->handleUploads($fileUploader, $page, $form);

            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($page);
            $entityManager->flush();

            if ($form->get('close')->isClicked()) {
                return $this->redirectToRoute('admin_page_index');
            }

            $this->addFlash('success', 'Page saved');

            return $this->redirectToRoute('admin_page_edit', [
                'id' => $page->getId(),
            ]);
         }

         return $this->render('admin/page/new.html.twig', [
            'page' => $page,
            'form' => $form->createView(),
        ]);
    }



/**
 * @Route("/{id}/edit", name="admin_page_edit", methods={"GET","POST"})
     */
    public function edit(Request $request, FileUploader $fileUploader, Page $page): Response
    {
        $form = $this->createForm(PageType::class, $page);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            if ($page->getParent() != null && $page->getParent()->getId() == $page->getId()) {
                $page->setParent(null);

                $this->addFlash('error', 'You cannot set a page to be a parent of itself');

                return $this->render('admin/page/edit.html.twig', [
                    'page' => $page,
                    'form' => $form->createView(),
                ]);
            }

            if ($form->get('publish')->isClicked()) {
                $page->setPublished(true);
            }

            // get all blocks and check if anything was uploaded?
            $this->handleUploads($fileUploader, $page, $form);

            $this->getDoctrine()->getManager()->flush();

            if ($form->get('close')->isClicked()) {
                return $this->redirectToRoute('admin_page_index');
            }

            $this->addFlash('success', 'Page saved');

            return $this->redirectToRoute('admin_page_edit', [
                'id' => $page->getId(),
            ]);
        }
}

Наконец, веточка html, которая отображает форму:

{% extends 'admin/layouts/admin.html.twig' %}

{% block body %}

{{ form_start(form) }}
<div class="tab-content">
    <div class="tab-pane show active" id="details">
        {{ form_row(form.blocks) }}
    </div>
</div>
{{ form_widget(form.save) }}
{{ form_widget(form.close) }}
{{ form_widget(form.publish) }}
{{ form_rest(form) }}
{{ form_end(form) }}

{% endblock %}
...