Home > Software design >  Get the file object in the form's controller, from a javascript array collection prototype in S
Get the file object in the form's controller, from a javascript array collection prototype in S

Time:10-12

Here is what i'm trying to do :

I have a classic Folder entity which contains Sku entities in a OneToMany relation as an ArrayCollection. So far so good.

I want the FolderType to dynamically create as many Skus as I want in it. For that, I followed the Symfony fast track method to generate prototypes in javascript. Works great.

Now, in this folder form, "skus" are an array collection of entities. In the SkuType, I have files to uploads. Here are the forms (Folder and Sku) :

 $builder
        ->add('norlogReference', TextType::class, [
            'label' => 'Référence du dossier'
        ])
        ->add('skus', CollectionType::class, [
            'entry_type' => SkuType::class,
            'entry_options' => ['label' => false],
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => 'skus'
        ]);

As for the SkuType, here are my upload fields :

->add('picture_1', FileType::class, [
            'label'         => 'Image 1 du produit',
            'mapped'        => false,
            'required'      => false,
            'attr'          => ['accept' => 'image/*']
        ])
        ->add('picture_2', FileType::class, [
            'label'         => 'Image 2 du produit',
            'mapped'        => false,
            'required'      => false,
            'attr'          => ['accept' => 'image/*']
        ])

Here is the controller recieving the form :

/**
 * @Route("/new", name="folder_new")
 */
public function new(Request $request): Response
{
    $entityManager = $this->getDoctrine()->getManager();

    $norlogFolder = new NorlogFolder();

    $form = $this->createForm(NorlogFolderType::class, $norlogFolder);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {

        foreach ($norlogFolder->getSkus() as $sku) {
I want to get both file objects here, pass them in handleUploadedFile(), and use $sku->setPicture1() and setPicture2 with the $newfileName returned by the private method.
            $sku->setFolder($norlogFolder);
            $sku->setSKU($norlogFolder->getNorlogReference() . '-' . $sku->getSKU());
        }

        $entityManager->persist($norlogFolder);
        $entityManager->flush();

        return $this->redirectToRoute('folder_edit', ['id' => $norlogFolder->getId()]);
    }

    return $this->render('folder/new.html.twig', [
        'form' => $form->createView(),
    ]);
}

and the little private method I want to use in order to handle the file :

private function handleUploadedFile(UploadedFile $uploadedFile): string
{
    $destination = $this->getParameter('kernel.project_dir').'/public/uploads/sku_medias';
    $originalFilename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
    $newFilename = $originalFilename . '-' . uniqid() . '.' . $uploadedFile->guessExtension();
    $uploadedFile->move(
        $destination,
        $newFilename
    );
    return $newFilename;
}

And the folder form view :

    {{ form_start(form, {
    attr: {
        class: 'sku-form'
    }
}) }}

<div class="form-group">
    {{ form_row(form.norlogReference,  {
        attr: {
            placeholder: 'ex: XOTP-25',
            class: 'sku-form-input'
        }
    }) }}
</div>

<div class="form-group sku-row skus" data-prototype="{{ form_widget(form.skus.vars.prototype)|e('html_attr') }}">
    {% if form.skus|length > 0 %}
        {% for sku in form.skus %}
            <div class="row sku-bloc bg-light">
                <div class="field-title">
                    <p class="display-3 text-center">Produit #{{ loop.index }}</p>
                    <a href="{{ path('sku_delete', {id: sku.vars.data.id}) }}"
                       class="btn bg-danger btn-sku-remove"><i class="fas fa-trash-alt"></i></a>
                </div>
                <span class="btn btn-secondary form-extend-btn">Voir</span>
                <div class="col-12 sku-fields-bloc">
                    <div class="form-control">
                        {{ form_row(sku.SKU, {
                            attr: {
                                placeholder: 'ex: XVF-25663',
                                class: 'sku-form-input'
                            }
                        }) }}
                    </div>

                    <div class="container">
                        <div class="row">
                            {{ form_label(sku.marque) }}
                            {{ form_widget(sku.marque, {
                                attr: {
                                    class: 'sku-form-input'
                                }
                            }) }}

                            {{ form_label(sku.taille) }}
                            {{ form_widget(sku.taille, {
                                attr: {
                                    class: 'sku-form-input'
                                }
                            }) }}

                            {{ form_label(sku.designation) }}
                            {{ form_widget(sku.designation, {
                                attr: {
                                    class: 'sku-form-input'
                                }
                            }) }}

                            {{ form_label(sku.couleur) }}
                            {{ form_widget(sku.couleur, {
                                attr: {
                                    class: 'sku-form-input'
                                }
                            }) }}

                            {{ form_label(sku.etat) }}
                            {{ form_widget(sku.etat, {
                                attr: {
                                    class: 'sku-form-input'
                                }
                            }) }}

                            {{ form_label(sku.composition) }}
                            {{ form_widget(sku.composition, {
                                attr: {
                                    class: 'sku-form-input'
                                }
                            }) }}
                        </div>
                    </div>

                    <div class="form-control mt-3">
                        {{ form_row(sku.picture_1, {
                            attr: {
                                placeholder: 'Image 1',
                                class: 'sku-form-input'
                            }
                        }) }}
                    </div>
                    <div class="form-control mt-3">
                        {{ form_row(sku.picture_2, {
                            attr: {
                                placeholder: 'Image 2',
                                class: 'sku-form-input'
                            }
                        }) }}
                    </div>
                </div>
            </div>
        {% endfor %}
    {% else %}
        <div class="row">
            <div class="col-12 mt-4">
                <p class="p-3 bg-info">Pas encore de {{ form_label(form.skus) }} ajoutés</p>
                {{ form_widget(form.skus) }}
            </div>
        </div>
    {% endif %}
</div>

<div class="row">
    <div class="col-4">
        <button type="button" class="btn btn-primary btn-nav w-100 mt-2 mb-2 add_item_link"
                data-collection-holder-class="skus">
            Ajouter Sku
        </button>
    </div>
    <div class="col-4">
        <button type="submit"
                class="btn btn-success btn-nav w-100 mt-2 mb-2">{{ button_label|default('Enregistrer') }}</button>
    </div>
    <div class="col-4">
        <a href="{{ path('folder_list') }}"
           class="btn btn-primary btn-nav w-100 mt-2 mb-2">Retour liste</a>
    </div>
</div>


{{ form_end(form) }}

So the question is : how to get these picture_1 and 2 objects (and not path which I already have by remapping them to true), by Sku, in the controller side, in order to handle the files as usual ?

I tried :

  • $request->files->get() (result = null, and I have the enctype right in the form)
  • $sku->getPicture_1() (result = temporary linux path, but is there a simple way to retrieve the file from that, and on every OS ?)
  • Trying to access the $form from inside the sku loop (result = nada)
  • $request->get('picture_1') and that ->getData()
  • Various weird tries I forgot which gave me nothing

I might miss something obvious here, but I can't think properly on this project anymore. Please don't mind the necessary refactoring for now =) Ty !

CodePudding user response:

Your picuter_1 and picture_2 are mapped: false. That means, you have to use ->getData() (or maybe ->getNormData()) on each SkuType or your NorlogFolderType

instead of looping through SKUs entities collection, loop through sku formtypes and get the data directly from 'picture_1' and 'picture_2' field

if ($form->isSubmitted() && $form->isValid())
{
    if ($form->has('skus'))
    {
        foreach ($form->get('skus') as $skuForm)
        {
            // always check if field named 'picture_1' is there
            if ($skuForm->has('picture_1'))
            {
                /** @var \Symfony\Component\HttpFoundation\File\UploadedFile $firstPic */
                $firstPic = $skuForm->get('picture_1')->getData();
                //todo: maybe check if null
                $picOne = $this->handleUploadedFile($firstPic);
            }
            // do the same with picture_2 (and others if any)
        }
    }
}

CodePudding user response:

With 'mapped' => false, you need to handle the collection of uploads like this:

if ($form->isSubmitted() && $form->isValid()) {

        foreach ($form->get('skus') as $formChild)
        {
            // Get the unmapped picture fields
            $uploadedPicture1 = $formChild->get('picture_1')->getData();
            $uploadedPicture2 = $formChild->get('picture_2')->getData();

            // Get the sku
            $sku = $formChild->getData();

            // Upload the pictures
            $picture1Filename = $this->handleUploadedFile($uploadedPicture1);
            $picture2Filename = $this->handleUploadedFile($uploadedPicture2);

            // Set the new filenames onto the sku
            $sku->setPicture1($picture1Filename);
            $sku->setPicture2($picture2Filename);
            
            // Your original code
            $sku->setFolder($norlogFolder);
            $sku->setSKU($norlogFolder->getNorlogReference() . '-' . $sku->getSKU());
        }
        
    }

    $entityManager->persist($norlogFolder);
    $entityManager->flush();

    return $this->redirectToRoute('folder_edit', ['id' => $norlogFolder->getId()]);
}

Edit: do check if a picture was actually uploaded, as in the other answer. :-)

  • Related