Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Persistencia en una relación ManyToMany bidireccional

27 de junio de 2016

Hola,

Tengo una entidad Block y otra entidad Question con una relación ManyToMany básica. Este seria el mapeado de las entidades:

// AppBundle/Entity/Question
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Block", inversedBy="questions", cascade={"persist"})
* @ORM\JoinTable(name="question_block")
*/
private $blocks;
 
// AppBundle/Entity/Block
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Question", mappedBy="blocks", cascade={"persist"})
*/
private $questions;

Se trata de un juego y tanto desde Question, como desde Block, necesito añadir preguntas al bloque o asignar las preguntas a un bloque. Es decir, si estoy creando una pregunta, esta la asigno a uno o varios bloques y si estoy creando un bloque necesito también poder añadir preguntas directamente.

El primer caso no tengo problemas, pero buscando información he visto que desde el inversed side (Block) no lo hace Doctrine, ¿como lo hago entonces?. Si es nuevo no hay problemas, pero si estoy editando un bloque y quiero quitar preguntas y añadir otras, no me elimina las que haya desmarcado (un select multiple ahora mismo). Actualmente hago algo así:

// AppBundle/Controller/Admin/BlocksController
    ...
    /**
     * @Route("/{id}/edit", options={"expose"=true}, name="admin_game_blocks_edit")
     * @Method({"GET", "POST"})
     * @param Block $block
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function editAction(Block $block, Request $request)
    {
        $form = $this->createForm(BlockType::class, $block, ['em' => $this->getUser()->getCode()]);
 
        $oldQuestions = new ArrayCollection();
        foreach ($block->getQuestions() as $question) {
            $oldQuestions->add($question);
        }
 
        $form->handleRequest($request);
 
        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager($this->getUser()->getCode());
            $block->setUpdatedAt(new \DateTime());
            $block->setQuestions($oldQuestions);
 
            $em->flush();
 
            return $this->redirectToRoute('admin_game_blocks_index');
        }
 
        return $this->render('admin/game/blocks/edit.html.twig', [
            'block' => $block,
            'form'  => $form->createView()
        ]);
    }
    ...
// AppBundle/Entity/Block
    ...
    /**
     * Set questions
     *
     * @param ArrayCollection $questions
     * @return Block
     */
    public function setQuestions($questions = null)
    {
        if ($questions) {
            foreach ($questions as $question) {
                if (!$this->questions->contains($question)) {
                    $this->removeQuestion($question);
                }
            }
        }
 
        foreach ($this->getQuestions() as $question) {
            $this->addQuestion($question);
        }
 
        return $this;
    }
    ...

Ni que decir tiene que no funciona puesto que no elimina la que haya desmarcado, aunque en general no funciona muy bien, es como lo he dejado antes de escribir aquí jeje. A ver si alguien me puede echar una mano, seguro que os lo habéis encontrado antes jeje :P

Gracias.

Un saludo.


Respuestas

#1

Solucionado, últimamente parece que escribir aquí hace que se me aclaren las ideas jeje. Al final solo tuve que añadir 'by_reference' => false al formulario lo cual hace que los métodos addX y removeX sean llamados y me ahorro todo lo que estaba haciendo.

Ahora me surge otra duda. ¿Como podría hacer para que en vez de un select multiple sea un listado o una tabla a la que añadir registros?. Tengo un Block y quiero que se muestre un listado/tabla con las Question y poder añadir registros ya existentes a esta y que se guarde la relación. Tanto al crear como al editar, claro.

Gracias.

Un saludo.

@LoGaNsF

28 junio 2016, 9:30