Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
365 views
in Technique[技术] by (71.8m points)

Symfony 5 - Custom form_widget and missing data

I can't find an answer to the problem I'm facing and I wonder if it's something simple that I'm missing. Also, I am quite new to Symfony framework and didn't get the chance to learn everything yet.

I'm using Symfony 5.1

The Problem: TL;DR

My main task was to create a custom form_widget, so that I could write my own HTML for what I need to show. After some digging and going through the Symfony's documentation I was finally able to achieve that, but this caused another issue. Now, when I submit the form, the field for which I created the custom form_widget is missing from the submitted data. Below there is more explanation on what and how I tried to solve this problem. Thanks in advance to anyone reading this and wanting to give me some directions.

Form:

$builder
    ->add('newUsers', CollectionType::class, [
        'entry_type' => UserType::class, //Embedded Form
        'allow_add' => true,
        'allow_delete' => true,
        'by_reference' => false,
        'mapped' => false
    ])
    ->add('users', CollectionType::class, [
        'entry_type' => UserType::class,
        'allow_delete' => true,
        'by_reference' => false,
        'mapped' => true
    ]);

As you can see, I am using an embedded form twice for users. One of them allows me to add new Users to the Form (and has different HTML structure). The other one displays all users added at the previous step (Different part of the process).

I thought the problem may be with using the same embedded form twice, but when I don't use my custom widget, everything work as expected (just with the standard HTML).

My custom widget is in a separate Form Theme file and contains only this (the naming of the block is correct):

{% block _invitation_users_widget %}
    {% for user in value %}
        <tr>
            <td>{{ user.name }}</td>
            <td>{{ user.email }}</td>
            <td class="text-center">
                <a href="#">
                    <i class="fas fa-user-minus"></i>
                </a>
            </td>
        </tr>
    {% endfor %}
{% endblock %}

Finally, I use this block after importing a form theme in the actual form html.twig file, like so:

{% form_theme form 'path/to/theme' %}

<table>
    <thead>
        <th>Name</th>
        <th>Email</th>
        <th></th>
    </thead>
    <tbody>
        {{ form_widget(form.users) }}
    </tbody>
</table>

This displays everything exactly as I want, but when the form gets submitted the expected 'users' array is missing from the submitted data (and this is tested in FormEvent::PRE_SUBMIT event listener, as well as in the Controller itself).

Now, when I remove the custom widget and use standard form_widget, the data is not missing, but I have input fields in my table (rather than just text).

Some things I tried already:

  • Create a custom widget just for the name and an e-mail, rather than the whole User itself. The data was missing regardless.
  • In FormEvent::PRE_SUBMIT I got access to Form variable and I got 'users' from there. Then I appeded this to the event data (the one submitted). This way I had access to 'users' inside my Controller, but the Form data was always giving me all the users (without the users I have deleted from the table) - so this is also no good.

The code was like this:

    $form = $event->getForm();
    if (! array_key_exists('users', $invitation)) {
        $users = $form->get('users')->getData()->toArray();
        $previousUsers = [];
        foreach ($users as $user) {
            $previousUsers[] = [
              'name' => $user->getName(),
              'email' => $user->getEmail()
            ];
        }
        $invitation['users'] = $previousUsers;
    }

This is how the table looks like without creating a custom widget: Rather than showing just the values, it shows input fields.

At this point I don't know what am I missing, and I can't find an answer in the Symfony documentation, here, or even symfonycasts. Any help will be appreciated.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Solution Found

I'm posting this as a separate answer in case someone stumbles across this question and is also looking for a solution.

I tried many different approaches since I asked the question (one of them - scraping the idea of customising the form_widget and using standard form_widget which I then fully modified using jQuery - failure).

My final solution is to customise each separate field of an embedded form (i.e. 'name', and 'email') and to use them within the table - with one little nuance. The customised form_widget has to be an HTML .

Here is the code:

// Import your form theme
{% form_theme form 'path/to/your/theme.html.twig' %}

<table class="table table-bordered">
    <thead>
        <th>{{ 'Name'|trans }}</th>
        <th>{{ 'Email'|trans }}</th>
        <th></th>
    </thead>

    <tbody>
        {% for user in form.users %}
            <tr class="js-invitation-group-user-item">
                <td>{{ form_widget(user.name) }}</td>
                <td>{{ form_widget(user.email) }}</td>
                <td class="text-center">
                    <a class="js-invitation-group-user-item-remove" href="#">
                        <i class="fas fa-user-minus"></i>
                    </a>
                </td>
            </tr>
        {% endfor %}
    </tbody>
</table>

And then in the form theme file:

{% block _form_users_entry_name_widget %}
    <input type="text" id="{{ id }}" name="{{ full_name }}" value="{{ value }}" style="border: none; font-weight: 500; color: #858796; width: 100%">
{% endblock %}

{% block _form_users_entry_email_widget %}
    <input type="text" id="{{ id }}" name="{{ full_name }}" value="{{ value }}" style="border: none; font-weight: 500; color: #858796; width: 100%">
{% endblock %}

Mind you, inline styles will be moved to their own place without the duplication. Also, I'm guessing 'id' and 'name' are not really necessary here so long we have and value.

I don't understand why this has to be implemented that way, and would be nice to actually understand it.

All I wanted to do, was to write this:

{% block _form_users_entry_name %}
    {{ value }}
{% endblock %}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...