Symfony - form - many to many relation - collectio

2019-06-09 22:40发布

Here is the situation:

I have an entity Property

class Property
{
    /**
     * @ORM\Id
     * @ORM\Column(type="string")
     * @ORM\GeneratedValue(strategy="UUID")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="PropertyEquipment", inversedBy="properties")
     */
    protected $propertyEquipments;

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

    public function getId()
    {
        return $this->id;
    }

    public function addPropertyEquipment(\AppBundle\Entity\PropertyEquipment $propertyEquipment)
    {
        $this->propertyEquipments[] = $propertyEquipment;

        return $this;
    }

    public function removePropertyEquipment(\AppBundle\Entity\PropertyEquipment $propertyEquipment)
    {
        $this->propertyEquipments->removeElement($propertyEquipment);
    }

    public function getPropertyEquipments()
    {
        return $this->propertyEquipments;
    }
}

And the entity PropertyEquipment:

class PropertyEquipment
{

    /**
     * @ORM\Id
     * @ORM\Column(type="string")
     * @ORM\GeneratedValue(strategy="UUID")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Property", mappedBy="propertyEquipments")
     */
    protected $properties;

    /**
     * @ORM\Column(type="string", length=100)
     * @Gedmo\Translatable
     */
    protected $equipmentName;


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

    /**
     * Get id
     *
     * @return string
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return mixed
     */
    public function getEquipmentName()
    {
        return $this->equipmentName;
    }

    /**
     * @param mixed $equipmentName
     */
    public function setEquipmentName($equipmentName)
    {
        $this->equipmentName = $equipmentName;
    }

    public function addProperty(Property $property)
    {
        $this->properties[] = $property;

        return $this;
    }

    public function removeProperty(Property $property)
    {
        $this->properties->removeElement($property);
    }

    public function getProperties()
    {
        return $this->properties;
    }
}

The form PropertyCreation

class PropertyCreation extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            //with this I see the values coming from DB in the template
            ->add("propertyEquipments", PropertyEquipmentCreation::class)

            //with this it's empty :/
            /*->add("propertyEquipments", CollectionType::class, array(
                "entry_type" => PropertyEquipmentCreation::class,
            ))*/
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Property::class
        ));
    }
}

Here is the form PropertyEquipmentCreation:

class PropertyEquipmentCreation extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder      
            ->add('propertyEquipment', EntityType::class, [
                'class' => 'AppBundle\Entity\PropertyEquipment',
                'choice_label' => 'equipmentName',
                'expanded' => true,
                'multiple' => true
            ]);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => PropertyEquipment::class,
        ]);
    }
}

And the controller

public function createPropertyAction(Request $request)
{
    $property = new Property();

    $form = $this->createForm(PropertyCreation::class, $property);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($property);
        $entityManager->flush();

        return $this->redirectToRoute('homepage');
    }
    return $this->render('form/owner_create_property.html.twig', ["form" => $form->createView()]);
}

My error:

Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Property#$propertyEquipments", got "Doctrine\Common\Collections\ArrayCollection" instead.

Must I transform these with something like class PropertyEquipmentTransformer implements DataTransformerInterface?

3条回答
乱世女痞
2楼-- · 2019-06-09 23:08

Try using Doctrine ArrayCollection instead of ArrayCollections:

$this->propertyEquipments = new \Doctrine\Common\Collections\ArrayCollection();

This should work!

查看更多
迷人小祖宗
3楼-- · 2019-06-09 23:27

I think you should use getParent() function in PropertyEquipmentCreation and inherit from EntityType::class then put all your field configs in the configureOptions() function (remove the buildForm function) and it should work.

You are having this problem because it is a compound form in your implementation and no simple form and symfony is unable to resolve which field created inside the subform needs to be used as source for the entity field

查看更多
干净又极端
4楼-- · 2019-06-09 23:29

First !

Big thanks to Nickolaus !

Here is the solution (PropertyEquipmentCreation):

namespace AppBundle\Form\Type;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;


class PropertyEquipmentCreation extends AbstractType
{

/**
 * @param OptionsResolver $resolver
 */
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'class' => 'AppBundle\Entity\PropertyEquipment',
        'choice_label' => 'equipmentName',
        'expanded' => true,
        'multiple' => true,
    ]);
}

public function getParent()
{
    return EntityType::class;
}
}

And for (PropertyCreation)

<?php


namespace AppBundle\Form;


use AppBundle\Form\Type\PropertyEquipmentCreation;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PropertyCreation extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('description', TextareaType::class)
        ->add('name', TextType::class)
        ->add("propertyEquipments", PropertyEquipmentCreation::class)
        ->add('save', SubmitType::class);
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => Property::class
    ));
}
}

Many thanks !

查看更多
登录 后发表回答