Search code examples
symfonysymfony-forms

How to set an EntityType field to be readonly/disabled, so that value cannot be edited but is still sent with the form?


In Symfony 6, when I render en EntityType (HTML select field) setting readonly attribute, this field can actually be modified (since readonly does not work for <select> elements)..

If I set disabled, then, I get an error as this field is required.

How can I avoid this field to be modified but rendered?

class ClientFileType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('clientFileType', EntityType::class, ['label' => 'Tipo',
                                                        'required' => true,
                                                        'class' => TypeOfClientFile::class,
                                                        'query_builder' => function (EntityRepository $er) {
                                                            return $er->createQueryBuilder('T')
                                                                ->orderBy('T.name', 'ASC');
                                                        }])
    // (...)
}

This is twig template code, field is readonly only when modifying, but editable on new entity:

{{ form_row(form.clientFileType, {'attr': {'readonly': (client_file.id != null) }}) }}

Solution

  • You cannot use disabled and required at the same time. If you think about it, you'll realize it doesn't make sense: with required you are telling "this value needs to be filled in and sent", with disabled you are saying "ignore and do not send the value for this field".

    For this, you'll have to implement a slightly more sophisticated logic.

    If the value field is to be disabled, then maybe you no longer need to display it as a select. You do not need the options (the user can't choose a new one), so why query them and render them?

    In this case, simply add a different field type for different situations: If the field is to be disabled, show a different UI element, and send the appropriate data.

    A crude example (you'll have to polish it so it suits your use-case, haven't actually ran it in my project, but the idea works):

    class ClientFileType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options): void
        {
            if ($options['client_file']) {
                $builder->add('clientFileType', HiddenType::class, [
                     'value' => $options['client_file'],
                ]);
    
                $builder->add('clientFileTypeDisplay', TextType::class, [
                  'label' => 'Tipo',
                  'value' => $options['client_file_name'],
                  'disabled' => true,
                ]);
            } else {
                $builder
                    ->add('clientFileType', EntityType::class, ['label' => 'Tipo',
                        'required' => true,
                        'class' => TypeOfClientFile::class,
                        'query_builder' => function (EntityRepository $er) {
                            return $er->createQueryBuilder('T')
                                ->orderBy('T.name', 'ASC');
                        }]);
            }
        }
    
        public function configureOptions(OptionsResolver $resolver): void
        {
            $resolver->setDefaults([
                'client_file' => 'int',
                'client_file_name' => '',
            ]);
    
            $resolver->addAllowedTypes('client_file', 'integer');
            $resolver->addAllowedTypes('client_file_name', 'string');
        }
    }
    
    

    Then when creating your form in your controller, you'll set the options if necessary:

    $form = $this->createForm(ClientFileType::class, options: [
       'client_file' => $client_file_id, // or 0 if not applicable,
        'client_flie_name' => $client_file_name // the actual name, so you don't have to make a query in the form
    ]);
    

    And when rendering it:

    {{ form_row(form.clientFileType) }}
    
    {% if client_file.id %}
    {{ form_row(form.clientFileTypeDisplay) }}
    {% endif %}