O
O
ozornick2019-12-16 14:38:25
symfony
ozornick, 2019-12-16 14:38:25

How to create custom forms with nested fields in Symfony 4?

The question stems from this post Do I need to use Symfony forms? A similar situation occurs. Before creating an object (that is, not editing a saved entity), I will not find how to process the form. There is a User object, it has nested dependencies.

$user = $this->getUser();
// Какое то значение
$client = $user->getClient()->getClientSomeData();
// Допустим теги, описания, которое являются ArrayCollection вообще тьма
$clientTags = $user->getClient()->getTags()->getTag()->getName();

Given the ManyToMany, OneToOne relationships, I don’t know how to implement the form. When you save, the connection is not generated.
Yes, as with the author, you first need to get the User, take the client_id of the connection key from him, then $user->getClient() itself, and so on. I know and use property_path, but it does not always work as it should, because somewhere I miss some little thing.
Also wondering how you extend the form by adding a custom field? I made my own type VehicleType, it auto-completes the entity. I would like to complement the Vehicle selection with this type everywhere, showing a text field. But at the output I have a new form framed by fieldset > legend. Thus, it is not an internal field, but a separate block.
5df76c797a0fc512527215.png

Answer the question

In order to leave comments, you need to log in

1 answer(s)
O
ozornick, 2019-12-17
@ozornick

The solution is, as often, simple. Do not create internal fields for your type

namespace App\Form\Type;

use ...

class UserSelectType extends AbstractType
{

    private $userRepository;

    public function __construct(UserRepository $repository)
    {
        $this->userRepository = $repository;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // Это фрагмент удаляем и вставляется обычное поле, а не форма
        $builder->add(
            'user',
            AutocompleteType::class,
            [
                'label' => false,
                'class' => User::class,
                'attr' => [
                    'placeholder' => 'Клиент',
                    'class' => 'user-autocomplete-property',
                ],
            ]
        );
       //dd($builder);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            [
                'label' => false,
                'class' => User::class,
                'attr' => [
                    'placeholder' => 'Клиент',
                    'class' => 'user-autocomplete-property',
                ],
            ]
        );
    }

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

namespace App\Form;

use ...

class VehicleFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        /**
         * @var Vehicle $vehicle
         */
        $vehicle = $options['data'] ?? null;
        $isEdit = $vehicle && $vehicle->getId();

        $builder
            ->add(
                'user',
                UserSelectType::class,
                [
                    'empty_data' => new ClientData(),
                    'property_path' => 'clientData',
                ]
            )
            ->add(
                'brand',
                TextType::class,
                [
                    'attr' => [
                        'placeholder' => 'Бренд',
                    ],
                    'label' => false,
                ]
            )
            ->add(
                'button',
                SubmitType::class,
                [
                    'attr' => [
                        'class' => 'btn btn-primary',
                    ],
                    'label' => $isEdit ? 'Изменить' : 'Сохранить',
                ]
            )
            ->add(
                'resetButton',
                ResetType::class,
                [
                    'attr' => [
                        'class' => 'btn btn-secondary',
                    ],
                    'label' => 'Сбросить',
                ]
            );
    }

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

Firstly, I added empty_data, now everything resolves and relations are picked up.
Secondly, I found why a new form is inserted and not a field in the current form. It is necessary to remove the fragment in the builder.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question