Task: registration form, among other things, there are three drop-down lists. In the first country is selected, then in the second Ajax'om loaded regions and, when choosing a region, in the third loaded cities. All three fields load data from three models: Country, Region, City.
buildForm
code:
$builder ->add('country', EntityType::class, ['class' => 'AppBundle:Country', 'choice_label' => 'name', 'placeholder' => '--- Выберите страну ---']) ->add('region', RegionSelectorType::class, [ 'required' => false]) ->add('city', EntityType::class, ['class' => 'AppBundle:City', 'choice_label' => 'name', 'placeholder' => '--- Выберите город ---', 'required' => false]) ->add('post_code', null, ['required' => false]) ->add('address', null, ['required' => false])
The country is loaded immediately from the entity, there are few of them. But for the regions and cities, I wanted to make a dynamic load so as not to waste a lot of data.
While stalled in the region, the city will be on the same principle. The problem is that when the form is submitted, the field does not transform, by ID
, into the essence. Tried and Using Transformer , Creating a Reusable issue_selector Field , is written in the profiler
Unable to reverse value for property path "region": The choice of "15"
The code for my RegionSelectorType
type RegionSelectorType
:
namespace AppBundle\Form; use AppBundle\Form\DataTransformer\RegionToNumberTransformer; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class RegionSelectorType extends AbstractType { private $manager; public function __construct(ObjectManager $manager) { $this->manager = $manager; } public function buildForm(FormBuilderInterface $builder, array $options) { $transformer = new RegionToNumberTransformer($this->manager); $builder->addModelTransformer($transformer); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'invalid_message' => 'Выбранный регион не существует', )); } public function getParent() { return ChoiceType::class; } }
Transformer code:
namespace AppBundle\Form\DataTransformer; use AppBundle\Entity\Region; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; class RegionToNumberTransformer implements DataTransformerInterface { private $manager; public function __construct(ObjectManager $manager) { $this->manager = $manager; } /** * Transforms an object (region) to a string (number). * * @param Region|null $region * @return string */ public function transform($region) { if (null === $region) { return ''; } return $region->getId(); } /** * Transforms a string (number) to an object (region). * * @param string $regionNumber * @return Region|null * @throws TransformationFailedException if object (region) is not found. */ public function reverseTransform($regionNumber) { // no region number? It's optional, so that's ok if (!$regionNumber) { return; } $region = $this->manager ->getRepository('AppBundle:Region') // query for the issue with this id ->find($regionNumber) ; if (null === $region) { // causes a validation error // this message is not shown to the user // see the invalid_message option throw new TransformationFailedException(sprintf( 'An region with number "%s" does not exist!', $regionNumber )); } return $region; } }
Service is registered as:
services: app.form.type.region_selector: class: AppBundle\Form\RegionSelectorType arguments: ['@doctrine.orm.entity_manager'] tags: - { name: form.type, alias: region_selector }
In general, all the manual. The reverseTransform()
function does not work at all. It is only ChoiceType::class
to replace ChoiceType::class
with TextType::class
in getParent()
, as in the example everything works: reverseTransform()
works, the entity is created. With EntityType
instead of my type, everything also works. I create a region of type EntityType
, in the choices
an empty array, - an error. That is, the EntityType
must be present in the form field, which will then be returned by the submit.
The first thing that comes to mind is to create an EventListеner
, catch the value transmitted by ChoiceType
and create an entity on the fly, but why then DataTransformer
?
I use 3 symfony, but I think the second one will have roughly the same problems and solution.