Luca M Luca M - 24 days ago 5
PHP Question

Symfony Doctrine Many to Many insert

I have a problem with my entities and controllers in Symfony. I would insert on my DB value in a many to many table generated.

Entity Requests with only many to many elements

class Requests {
/**
* @ORM\ManyToMany(targetEntity="Tipi", inversedBy="requests")
* @ORM\JoinTable(name="tipi_richieste")
*/
private $tipi;


public function __construct() {
$this->tipi = new \Doctrine\Common\Collections\ArrayCollection();
}


/**
* Add tipi
*
* @param \AppBundle\Entity\Tipi $tipi
*
* @return Requests
*/
public function addTipi(\AppBundle\Entity\Tipi $tipi) {
$this->tipi[] = $tipi;

return $this;
}

/**
* Remove tipi
*
* @param \AppBundle\Entity\Tipi $tipi
*/
public function removeTipi(\AppBundle\Entity\Tipi $tipi) {
$this->tipi->removeElement($tipi);
}

/**
* Get tipi
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTipi() {
return $this->tipi;
}
}


Entity Tipi with only many to many elements

class Tipi {
/**
* @ORM\ManyToMany(targetEntity="Requests", mappedBy="tipi")
*/
private $requests;


/**
* Constructor
*/
public function __construct() {
$this->requests = new \Doctrine\Common\Collections\ArrayCollection();
}

/**
* Add request
*
* @param \AppBundle\Entity\Requests $request
*
* @return Tipi
*/
public function addRequest(\AppBundle\Entity\Requests $request)
{
$this->requests[] = $request;

return $this;
}

/**
* Remove request
*
* @param \AppBundle\Entity\Requests $request
*/
public function removeRequest(\AppBundle\Entity\Requests $request)
{
$this->requests->removeElement($request);
}

/**
* Get requests
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getRequests()
{
return $this->requests;
}
}


The Form type for insert is an EntityType

->add('tipi', EntityType::class, array(
'label' => 'Tipo',
'class' => 'AppBundle:Tipi',
'mapped' => false,
'attr' => array('class' => 'form-control'),
'multiple' => true,
'by_reference' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('t');
},
))


And in my controller I work in this way:

public function indexAction(Request $request) {

$requests = new Requests();
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(RequestsType::class, $requests);
$form->handleRequest($request);


if ($form->isSubmitted() && $form->isValid()) {
$requests->setCreateAt(new \DateTime('now'));

$request_tipi = $form["tipi"]->getData();
$tipi_array = [];
die($form["tipi"]->getData());
$tipi_array = $em->getRepository('AppBundle:Tipi')->findOneBy(array('codice' => $form["tipi"]->getData()));
$tipi = new Tipi();
$requests->addTipi($form["tipi"]->getData());
$em->persist($requests);
$em->flush();

//return $this->redirectToRoute('immovable_edit', array('id' => $immovables->getId()));
}
return $this->render('AppBundle:Requests:index.html.twig', array(
'requests' => $requests,
'form' => $form->createView(),
));
}


When I put die for return the value of
$form["tipi"]->getData()
I get an array collection :

Doctrine\Common\Collections\ArrayCollection@000000005b52ae6b00000000731dd0b4


But I get this error


Catchable Fatal Error: Argument 1 passed to AppBundle\Entity\Requests::addTipi() must be an instance of AppBundle\Entity\Tipi, instance of Doctrine\Common\Collections\ArrayCollection given, called in C:\xampp\htdocs\bugaro\src\AppBundle\Controller\RequestsController.php on line 31 and defined

Answer

Request::addTipi() fuction adds single tipi to its internal collection. Therefore you can't add whole ArrayCollection in call.

You have two options.

Do a foreach

foreach($form["tipi"]->getData() as $tipi) {
    $requests->addTipi($tipi);
}

Create multisetter like Requests::addCollections(ArrayCollection[])

public function addTipis($tipis) {

    foreach($tipis as $tipi) {
        $this->tipi[] = $tipi;
    }

    return $this;
}

Anyway there's a lot to fix in your code. Too much for a single post here.

But the most important of these fixes is that you don't need most of your controller code. ;-)

Since you pass $requests object into the form, it's already bined two-way, which means that Symfony's Form Component should automatically fill it's properties with new values. That includes many to many relation's collections.

Also if you wouldn't pass that object, $form->getData() should return a new Resnponses instance, therefore you don't need to create it and pass manually until it's edition of existing instance.