PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Saturday, March 5, 2022

[FIXED] Doctrine - using ManyToMany with additional fields and want to save more than one row in association table

 March 05, 2022     doctrine, doctrine-orm, many-to-many, php, symfony     No comments   

Issue

I started building up database by using ManyToMany relation with doctrine. I switched to another approach cause I needed additional fields in the association table. Im using symfony 5

I habe 3 Entities:

namespace App\Entity;

use App\Repository\TemplateRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=TemplateRepository::class)
 * @ORM\Table(options={"collate"="utf8mb4_general_ci"})
 */
class Template
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity=TemplateSection::class, mappedBy="section", cascade={"persist"})
     */
    private $sections;

    ....
}


namespace App\Entity;

use App\Repository\SectionRepository;
use App\DBAL\Types\SectionElementType;
use Doctrine\ORM\Mapping as ORM;
use Fresh\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;

/**
 * @ORM\Entity(repositoryClass=SectionRepository::class)
 * @ORM\Table(options={"collate"="utf8mb4_general_ci"})
 */
class Section
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity=TemplateSection::class, mappedBy="template")
     */
    private $template;

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

    ....

}


namespace App\Entity;

use App\Repository\TemplateSectionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=TemplateSectionRepository::class)
 * @ORM\Table(options={"collate"="utf8mb4_general_ci"})
 */
class TemplateSection
{

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity=Template::class, inversedBy="id", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     */
    private $template;

    /**
     * @ORM\ManyToOne(targetEntity=Section::class, inversedBy="id", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     */
    private $section;

    /**
     * @ORM\Column(type="smallint", options={"default": "0"})
     */
    private $sortOrder;

    ....

}

I have a form where I can define new entries for a template. There is a field secInput where I can define more than 1 section to be used in this template. In secInput is a comma separated list of values(Ids) to be used for chosen sections.

When I try to save the form, only the last record is saved in Template.sections

What do I have to change to save all given data to database?

My code in TemplateController:



    /**
     * @Route("/new", name="adminTemplateNew", methods={"GET","POST"})
     */
    public function new(Request $request): Response
    {
        $template = new Template();
        $form = $this->createForm(TemplateType::class, $template);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $entityManager = $this->getDoctrine()->getManager();
            $data = $form->getData();
            $repo = $entityManager->getRepository(Section::class);
            $templateSection = new TemplateSection();
            $template->setCreatedAt(new DateTime('NOW'));

            $sections = explode(',', $form->get('secInput')->getData());
            $count = 1;
            foreach ($sections as $secId) {
                if ( null !== $section = $repo->find($secId) ) {
                    $templateSection->setSortOrder($count);
                    $templateSection->setTemplate($template);
                    $templateSection->setSection($section);
                    $template->addSection($templateSection);
                    $entityManager->persist($templateSection);
                    $count++;
                }
            }

            $entityManager->persist($templateSection);
            $entityManager->persist($template);
            $entityManager->flush();

            return $this->redirectToRoute('template_index', ['data' => $data], Response::HTTP_SEE_OTHER);
        }


Solution

You are iterating over the selected sections and overwriting them without saving. When you call $entityManager->persist($templateSection), you tell the EntityManager to keep track of it, but in the end, when you call $entityManager->flush(), only one object is ever persisted. And it happens to be with the most recent data.

Try constructing a new object and persisting it, like this:

    public function new(Request $request): Response
    {
        $form = $this->createForm(TemplateType::class, $template);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $entityManager = $this->getDoctrine()->getManager();
            $data = $form->getData();
            $repo = $entityManager->getRepository(Section::class);

            $template = new Template();
            $template->setCreatedAt(new DateTime('NOW'));

            $sections = explode(',', $form->get('secInput')->getData());
            $count = 1;
            foreach ($sections as $secId) {
                if ( null !== $section = $repo->find($secId) ) {
                    $templateSection = new TemplateSection(); // This is new
                    $templateSection->setSortOrder($count);
                    $templateSection->setTemplate($template);
                    $templateSection->setSection($section);
                    $template->addSection($templateSection);
                    $entityManager->persist($templateSection);
                    $count++;
                }
            }

            $entityManager->persist($template);
            $entityManager->flush();

            return $this->redirectToRoute('template_index', ['data' => $data], Response::HTTP_SEE_OTHER);
        }



Answered By - CrazyMuffin
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing