Symfony 5 - Using DataTransformer on Entity with another column than primary key

Viewed 405

Following this doc https://symfony.com/doc/current/form/data_transformers.html#harder-example-transforming-an-issue-number-into-an-issue-entity, we learn how to fill a field (which is an Entity) without using a choice field type or similar.

It seems to work, using the primary key of the Entity concerned (el famoso "id").

However, i want to use another field and store it in a table, but i have an error (see screeshot at bottom)

Entities :

<?php

namespace App\Entity;

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

/**
    * @ORM\Entity(repositoryClass=LandRepository::class)
    * @ORM\Table(name="land",indexes={@ORM\Index(columns={"uid"})})
    * @ORM\HasLifecycleCallbacks()
*/
class Land
{   
    /**
        * @ORM\Id()
        * @ORM\GeneratedValue()
        * @ORM\Column(type="integer")
    */
    private $id;
    
    /**
        * @ORM\Column(type="string", length=255)
    */
    private $libelle;
    
    /**
        * @ORM\Column(type="integer")
    */
    private $uid;   
    
    
    public function getId(): ?int
    {
        return $this->id;
    }
    
    public function getLibelle(): ?string
    {
        return $this->libelle;
    }
    
    public function setLibelle(string $libelle): self
    {
        $this->libelle = $libelle;
        
        return $this;
    }
    
    public function getUid(): ?int
    {
        return $this->uid;
    }
    
    public function setUid(int $uid): self
    {
        $this->uid = $uid;
        
        return $this;
    }
    
}



namespace App\Entity;

use App\Repository\RideRepository;
use Doctrine\ORM\Mapping as ORM;

/**
    * @ORM\Entity(repositoryClass=RideRepository::class)
    * @ORM\HasLifecycleCallbacks()
*/
class Ride
{   
    /**
        * @ORM\Id()
        * @ORM\GeneratedValue()
        * @ORM\Column(type="integer")
    */
    private $id;
    
    /**
        * @ORM\Column(type="string", length=255)
    */
    private $libelle;
    
    /**
        * @ORM\ManyToOne(targetEntity=Land::class)
        * @ORM\JoinColumn(name="areaparent", referencedColumnName="uid")
    */
    private $areaparent;
    
    
    public function getId(): ?int
    {
        return $this->id;
    }
    
    public function getLibelle(): ?string
    {
        return $this->libelle;
    }
    
    public function setLibelle(string $libelle): self
    {
        $this->libelle = $libelle;
        
        return $this;
    }
    
    public function getAreaparent(): ?Land
    {
        return $this->areaparent;
    }
    
    public function setAreaparent(?Land $areaparent): self
    {
        $this->areaparent = $areaparent;
        
        return $this;
    }
    
}

Expected final result :

"Land" table

enter image description here

"Ride" table

enter image description here

So you can see that in the "ride" table that the "areaparent" column is relative to the "land.uid" column.

"Land" form is classic

enter image description here

"Ride" form have a minor change : The "areaparent" is not a choice field, it's a text type which will call my DataTransformer

enter image description here

Extract of my form "RideType"

<?php
    
    namespace App\Form;
    
    use App\Entity\Ride;
    use App\Entity\Land;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Bridge\Doctrine\Form\Type\EntityType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    use App\Repository\LandRepository;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    use App\Form\DataTransformer\UIDTransformer;
    
    class RideType extends AbstractType
    {
    
        private $transformer;

        public function __construct(UIDTransformer $transformer)
        {
            $this->transformer = $transformer;
        }       
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
            ->add('libelle')
            ->add('areaparent', TextType::class, [
                'invalid_message' => 'That is not a valid areaparent number',
            ])
            ;
            
            $builder->get('areaparent')->addModelTransformer($this->transformer);   
        }
        
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
            'data_class' => Ride::class,
            ]);
        }
    }

Extract of my DataTransformer :

<?php
    namespace App\Form\DataTransformer;
    
    use App\Entity\Land;
    use Doctrine\ORM\EntityManagerInterface;
    use Symfony\Component\Form\DataTransformerInterface;
    use Symfony\Component\Form\Exception\TransformationFailedException;
    
    class UIDTransformer implements DataTransformerInterface
    {
        private $entityManager;
        
        public function __construct(EntityManagerInterface $entityManager)
        {
            $this->entityManager = $entityManager;
        }
        
        /**
            * Transforms an object (Land) to a string (areaparent).
            *
            * @param  Land|null $Land
            * @return string
        */
        public function transform($land)
        {
            if (null === $land) {
                return '';
            }
            
            return $land->getUid();
        }
        
        /**
            * Transforms a string (police) to an object (Land).
            *
            * @param  string $areaparent
            * @return Land|null
            * @throws TransformationFailedException if object (Land) is not found.
        */
        public function reverseTransform($areaparent)
        {
            // no areaparent? It's optional, so that's ok
            if (!$areaparent) {
                return;
            }
            
            $land = $this->entityManager->getRepository(Land::class)->findByUid($areaparent);
            if (count($land) == 0) {
                // causes a validation error
                // this message is not shown to the user
                // see the invalid_message option
                throw new TransformationFailedException(sprintf('Land with areaparent "%s" does not exist!',$areaparent));
            }
            return $land[0];
        }
    }           

Error enter image description here

I try to debug the symfony/doctrine code, and if i correctly understand, the error appears because the field "uid" is not considered as an "identifier".

See some dump.

enter image description here

Have you got some ideas ?

Thanks !

0 Answers
Related