Search code examples
symfonyformbuilder

Symfony FormBuilder CollectionType with two fields


Target: The User can add more than one entities with one form. Every entity have two fields. In this Case: Barcode and Value.

Problem: I get an empty array when I submit the form.

The Formbuilder for a single entity

NewBarcodeForm

class NewBarcodeForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('barcode')
            ->add('value')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\Barcode'
        ]);
    }

    public function getBlockPrefix()
    {
        return 'app_bundle_new_barcode_form';
    }
}

The Formbuilder for a list of barcodes

NewBarcodesForm

class NewBarcodesForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('barcodes', CollectionType::class, array(
                    'entry_type' => NewBarcodeForm::class,
                    'allow_add' => true,
                )
            );
    }

    public function configureOptions(OptionsResolver $resolver)
    {

    }

    public function getBlockPrefix()
    {
        return 'app_bundle_new_barcodes_form';
    }
}

The Controller

    $form = $this->createForm(NewBarcodesForm::class);
    $form->handleRequest($request);

    if($form->isSubmitted() && $form->isValid()){
        $barcodes = $form->getData();
        dump($barcodes);
    return $this->render('items/newBarcode.html.twig', [
        'barcodesForm' => $form->createView()
    ]);

The HTML Form

<form name="app_bundle_new_barcodes_form" method="post">
    <input type="hidden" id="app_bundle_new_barcodes_form__token" name="app_bundle_new_barcodes_form[_token]" value="SFl1xlewaLE1JgGVX5oDc_T1PRSc0-aIUgPSaRLfLSU">
    <div>
        <table class="table table-condensed table-hover">
            <thead>
                <tr>
                    <th> Barcode </th>
                    <th> Value </th>
                </tr>
            </thead>
            <tbody id="barcodeTable">
            <tr>
                <td class="col-md-10">
                    <input type="text" id="barcodesForm_barcodes_0_barcode" name="barcodesForm[barcodes][0][barcode]" required="required" class="form-control" value="1111">
                </td>
                <td class="col-md-2">
                    <input type="number" id="barcodesForm_barcodes_0_value" name="barcodesForm[barcodes][0][value]" required="required" class="form-control" value="1">
                </td>
            </tr>
            <tr>
                <td class="col-md-10">
                    <input type="text" id="barcodesForm_barcodes_1_barcode" name="barcodesForm[barcodes][1][barcode]" required="required" class="form-control" value="2222">
                </td>
                <td class="col-md-2">
                    <input type="number" id="barcodesForm_barcodes_1_value" name="barcodesForm[barcodes][1][value]" required="required" class="form-control" value="22">
                </td>
            </tr>
            <tr>
                <td class="col-md-10">
                    <input type="text" id="barcodesForm_barcodes_2_barcode" name="barcodesForm[barcodes][2][barcode]" required="required" class="form-control" value="3333">
                </td>
                <td class="col-md-2">
                    <input type="number" id="barcodesForm_barcodes_2_value" name="barcodesForm[barcodes][2][value]" required="required" class="form-control" value="333">
                </td>
            </tr>

        </table>
    </div>
    <button type="submit">Speichern</button>
</form>

Result from dump

array:1 [▼ "barcodes" => [] ]

EDIT

The HTML above is a simplified result of the twig file, after adding 3 barcodes. The original twig file looks like this:

{% block body %}
    <div id="popDiv" class="ontop">
        <div class="panel panel-info" id="popup">
            <div class="panel-heading">
                <h3 class="panel-title">Barcode-Erfassung gestartet.</h3>
            </div>
            <div class="panel-body">
                {{ form_start(barcodesForm) }}
                {{ form_row(barcodesForm._token) }}
                <div>
                    <table class="table table-condensed table-hover">
                        <thead>
                        <tr>
                            <th> Barcode </th>
                            <th> Anzahl </th>
                        </tr>
                        </thead>
                        <tbody id="barcodeTable">
                        </tbody>
                    </table>
                </div>
                <div class="alert alert-danger" id="barcodeError" style="display: none">
                    <strong>Fehler!</strong> Der Barcode ist bereits in der Liste.
                </div>
                <button type="submit" class="btn default blue-stripe pull-right" onClick="hide('popDiv')">Speichern</button>
                <button class="btn default pull-right" style="margin-right: 10px" onClick="hide('popDiv')">Abbrechen</button>
                {{ form_end(barcodesForm, {'render_rest': false}) }}
                <!-- TODO: Abbrechen -->
            </div>
        </div>
    </div>

    <button class="btn default" onClick="pop('popDiv')">Click here to open a popup div</button>

{% endblock %}

{% block javascripts %}
    {{ parent() }}
    <script type="text/javascript" src="{{ asset('plugins/jquery.scannerdetection.js') }}"></script>
    <script type="text/javascript">
        var barcodes = [];
        i = 0;
        function scan() {
            $(document).scannerDetection({
                timeBeforeScanTest: 200, // wait for the next character for upto 200ms
                startChar: [120], // Prefix character for the cabled scanner (OPL6845R)
                endChar: [13], // be sure the scan is complete if key 13 (enter) is detected
                avgTimeByChar: 40, // it's not a barcode if a character takes longer than 40ms
                onComplete: function(barcode, qty){
                    if(barcodes.indexOf(barcode) === -1){
                        document.getElementById('barcodeError').style.display = 'none';
                        barcodes.push(barcode);
                        var tr = document.createElement('tr');
                        tr.innerHTML = '' +
                            '<td class="col-md-10"><input type="text" id="barcodesForm_barcodes_'+i+'_barcode" name="barcodesForm[barcodes]['+i+'][barcode]" required="required" class="form-control" value="'+ barcode +'"></td>' +
                            '<td class="col-md-2"><input type="number" id="barcodesForm_barcodes_'+i+'_value" name="barcodesForm[barcodes]['+i+'][value]" required="required" class="form-control" value="1"></td>'
                        ;
                        i++;
                        document.getElementById('barcodeTable').appendChild(tr);
                    }else{
                        document.getElementById('barcodeError').style.display = 'block';
                    }
                }
            });
        }

        function pop(div) {
            document.getElementById(div).style.display = 'block';
            scan();
        }

        function hide(div) {
            document.getElementById(div).style.display = 'none';
            $(document).scannerDetection(false);
        }
    </script>
{% endblock %}

Solution

  • In the form the name attribute is incorrect. Replace barcodesForm to app_bundle_new_barcodes_form.

    Current form inputs:

    <input
        type="text"
        id="barcodesForm_barcodes_0_barcode"
        name="barcodesForm[barcodes][0][barcode]"
        required="required"
        class="form-control"
        value="1111"
    >
    <input
        type="number"
        id="barcodesForm_barcodes_0_value"
        name="barcodesForm[barcodes][0][value]"
        required="required"
        class="form-control"
        value="1"
    >
    

    Should be:

    <input
        type="text"
        id="barcodesForm_barcodes_0_barcode"
        name="app_bundle_new_barcodes_form[barcodes][0][barcode]"
        required="required"
        class="form-control"
        value="1111"
    >
    <input
        type="number"
        id="barcodesForm_barcodes_0_value"
        name="app_bundle_new_barcodes_form[barcodes][0][value]"
        required="required"
        class="form-control"
        value="1"
    >