Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EntityToIdObjectTransformer always null #2154

Closed
alexmosca opened this issue Apr 14, 2020 · 3 comments
Closed

EntityToIdObjectTransformer always null #2154

alexmosca opened this issue Apr 14, 2020 · 3 comments

Comments

@alexmosca
Copy link

alexmosca commented Apr 14, 2020

Hi,

I have an issue using the EntityToIdObjectTransformer on my form. I'm using Symfony 5 and latest version of FOSRestBundle. (With JMSSerializerBundle)

The Transformer always receive a null value causing the form to return "This value is not valid" for my customer field. I'm sending the following json object (POST) :

{lines: [], docDate: "2020-04-14T22:49:34.543Z", customer: {id: 1, name: "Trovato SA"}}

Response :

{ "code": 400, "message": "Validation Failed", "errors": { "children": { "docDate": {}, "lines": {}, "customer": { "errors": [ "This value is not valid." ] } } } }

My form :

class OfferType extends AbstractType
{
    private $om;

    public function __construct(EntityManagerInterface $om)
    {
        $this->om = $om;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $customerTransformer = new EntityToIdObjectTransformer($this->om, Address::class);

        $builder
            ->add('docDate', DateTimeType::class, array(
                'widget' => 'single_text'
            ))
            ->add('lines', CollectionType::class, array(
                'entry_type' => DocumentLineType::class,
                'entry_options' => array(
                    'label' => false
                ),
                'allow_add' => true,
                'by_reference' => false
            ))
            ->add('customer', TextType::class);

        $builder->get('customer')->addModelTransformer($customerTransformer);


    }

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

    public function getBlockPrefix()
    {
        return '';
    }
}

My Controller :

/**
     * @param Request $request
     * @return Offer|\Symfony\Component\Form\FormInterface
     * @Rest\Post(path="/api/offers" )
     */
    public function create(Request $request)
    {
        $offer = new Offer();

        $form = $this->createForm(OfferType::class, $offer, array());

        $form->handleRequest($request);


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

            return $offer;
        }


        return $form;
    }

My Entities :

/**
 * @ORM\Entity(repositoryClass="App\Repository\DocumentRepository")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type_doc", type="string")
 */
abstract class Document
{
    const STATE_EDITED = 'EDITED';
    const STATE_SENT = 'SENT';
    const STATE_CONFIRMED = 'CONFIRMED';
    const STATE_DECLINED = 'DECLINED';


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

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Address", inversedBy="documents")
     * @ORM\JoinColumn(nullable=false)
     */
    private $customer;

    /**
     * @ORM\Column(type="date")
     */
    private $docDate;

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

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\DocumentLine", mappedBy="document", orphanRemoval=true, cascade={"persist"})
     */
    private $lines;

    public function __construct()
    {
        $this->state = Document::STATE_EDITED;
        $this->lines = new ArrayCollection();
    }

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

    public function getCustomer(): ?Address
    {
        return $this->customer;
    }

    public function setCustomer(?Address $customer): self
    {
        $this->customer = $customer;

        return $this;
    }

    public function getDocDate(): ?\DateTimeInterface
    {
        return $this->docDate;
    }

    public function setDocDate(\DateTimeInterface $docDate): self
    {
        $this->docDate = $docDate;

        return $this;
    }

    public function getState(): ?string
    {
        return $this->state;
    }

    public function setState(string $state): self
    {
        $this->state = $state;

        return $this;
    }

    /**
     * @return Collection|DocumentLine[]
     */
    public function getLines(): Collection
    {
        return $this->lines;
    }

    public function addLine(DocumentLine $line): self
    {
        if (!$this->lines->contains($line)) {
            $this->lines[] = $line;
            $line->setDocument($this);
        }

        return $this;
    }

    public function removeLine(DocumentLine $line): self
    {
        if ($this->lines->contains($line)) {
            $this->lines->removeElement($line);
            // set the owning side to null (unless already changed)
            if ($line->getDocument() === $this) {
                $line->setDocument(null);
            }
        }

        return $this;
    }
}

/**
 * @ORM\Entity(repositoryClass="App\Repository\OfferRepository")
 */
class Offer extends Document
{

    public function __construct()
    {
        parent::__construct();
    }
}

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

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


    public function __construct()
    {
    }

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

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

Thanks for you help

Regards

@xabbuh
Copy link
Member

xabbuh commented Apr 16, 2020

For the customer field you are using a TextType, but do submit an array. I do not see how that could work.

@alexmosca
Copy link
Author

Hi xabbuh,

I'm following this documentation https://symfony.com/doc/master/bundles/FOSRestBundle/2-the-view-layer.html#data-transformation and they are using a TextType too. I have another project in an older version of Symfony where it is working.

@xabbuh
Copy link
Member

xabbuh commented Apr 16, 2020

That probably used to work in the past where the Symfony TextType did not check the submitted value. We changed that in a security patch some time ago. Now the form submission will fail with compound data being submitted to the TextType.

@xabbuh xabbuh closed this as completed Jun 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants