symfony 我如何上传一个收藏类型图片?

c86crjj0  于 2023-02-23  发布在  其他
关注(0)|答案(1)|浏览(104)

首先,我想感谢你的帮助和你的时间。我是新的编码与symfony。所以如果你看到的东西要改进,你想让我知道,我会采取任何好的意见。谢谢你的理解。
我想制作一个有几个步骤的食谱,每个步骤都有一个描述和一个图像。我使用collectionType。我设法恢复步骤及其描述和图像。但当我刷新发送到数据库时,第一个图像被覆盖,因为它处于循环中。
一切都工作正常,直到这段代码我做了一个var_dump,我恢复了名称更改的图像

    • 变量转储($imageFileName2);**

但是在foreach循环中,我想设置我的映像,第一个映像被覆盖

foreach($stepsImgs as $s) {

                   $s->setStepsImage($imageFileName2);

                }
  • 在此编辑,如果这可以帮助您了解更多 *
foreach($imgs as $im) {
         $imageFileName2 = $fileUploader->upload($im);
         var_dump('<br>HERE ARE THE NAME OF THE IMAGE AFTER THE FILEUPLOAD : '.$imageFileName2.'<br>');
                              
                foreach($stepsImgs as $s) {
                    
                    $s->setStepsImage($imageFileName2);
                
                }      
            }

输出:

并且在其末尾是刷新之前的dd

我允许您检查我的代码,再次感谢您抽出时间。

这是我的配方实体:

<?php

namespace App\Entity;

use App\Repository\RecipeRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=RecipeRepository::class)
 */
class Recipe
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $recipeName;

    /**
     * @ORM\Column(type="integer")
     */
    private $duration;

    /**
     * @ORM\OneToMany(targetEntity=RecipeIngredients::class, mappedBy="recipes", cascade={"persist"}, orphanRemoval=true)
     * @ORM\JoinColumn(onDelete="CASCADE")
     */
    private $recipeIngredients;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $recipeImage;

    /**
     * @ORM\Column(type="text", nullable=true)
     */
    private $recipeDescription;

    /**
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $createdAt;

    /**
     * @ORM\ManyToOne(targetEntity=Category::class, inversedBy="recipes")
     */
    private $category;

    /**
     * @ORM\ManyToOne(targetEntity=Difficulty::class, inversedBy="recipes")
     */
    private $difficulty;

    /**
     * @ORM\OneToMany(targetEntity=Steps::class, mappedBy="recipe", cascade={"persist"}, orphanRemoval=true)
     * @ORM\JoinColumn(onDelete="CASCADE")
     */
    private $steps;

    

    public function __construct()
    {
        $this->recipeIngredients = new ArrayCollection();
        $this->createdAt = new \DateTime('now');
        $this->steps = new ArrayCollection();
       
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getRecipeName(): ?string
    {
        return $this->recipeName;
    }

    public function setRecipeName(string $recipeName): self
    {
        $this->recipeName = $recipeName;

        return $this;
    }

    public function getDuration(): ?int
    {
        return $this->duration;
    }

    public function setDuration(int $duration): self
    {
        $this->duration = $duration;

        return $this;
    }

    public function getRecipeImage(): ?string
    {
        return $this->recipeImage;
    }

    public function setRecipeImage(?string $recipeImage): self
    {
        $this->recipeImage = $recipeImage;

        return $this;
    }

    public function getRecipeDescription(): ?string
    {
        return $this->recipeDescription;
    }

    public function setRecipeDescription(?string $recipeDescription): self
    {
        $this->recipeDescription = $recipeDescription;

        return $this;
    }

    public function getCreatedAt(): ?\DateTimeInterface
    {
        return $this->createdAt;

       
    }

    public function setCreatedAt(?\DateTimeInterface $createdAt): self
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getCategory(): ?Category
    {
        return $this->category;
    }

    public function setCategory(?Category $category): self
    {
        $this->category = $category;

        return $this;
    }

    /**
     * @return Collection<int, RecipeIngredients>
     */
    public function getRecipeIngredients(): Collection
    {
        return $this->recipeIngredients;
    }

    public function addRecipeIngredient(RecipeIngredients $recipeIngredient): self
    {
        if (!$this->recipeIngredients->contains($recipeIngredient)) {
            $this->recipeIngredients[] = $recipeIngredient;
            $recipeIngredient->setRecipes($this);
        }

        return $this;
    }

    public function removeRecipeIngredient(RecipeIngredients $recipeIngredient): self
    {
        if ($this->recipeIngredients->removeElement($recipeIngredient)) {
            // set the owning side to null (unless already changed)
            if ($recipeIngredient->getRecipes() === $this) {
                $recipeIngredient->setRecipes(null);
            }
        }

        return $this;
    }

   

    public function getMinuteFromLastUpdate($id)
    {
        // $updateDate = new \DateTime($this->getCreatedAt());

        $updateDate = $this->getCreatedAt();
        $now = new \DateTime('now');

        $calculateDate = $updateDate->diff($now)->format('%H:%i');

        return $calculateDate;
    }

    public function getDifficulty(): ?Difficulty
    {
        return $this->difficulty;
    }

    public function setDifficulty(?Difficulty $difficulty): self
    {
        $this->difficulty = $difficulty;

        return $this;
    }

    /**
     * @return Collection<int, Steps>
     */
    public function getSteps(): Collection
    {
        return $this->steps;
    }

    public function addStep(Steps $step): self
    {
        if (!$this->steps->contains($step)) {
            $this->steps[] = $step;
            $step->setRecipe($this);
        }

        return $this;
    }

    public function removeStep(Steps $step): self
    {
        if ($this->steps->removeElement($step)) {
            // set the owning side to null (unless already changed)
            if ($step->getRecipe() === $this) {
                $step->setRecipe(null);
            }
        }

        return $this;
    }

   

    public function __toString()
    {
        return $this->getRecipeName();
    }
  

    
}

这里我的RecipeController和方法,当我想要创建配方:

/**
     * @Route("admin/create/recipe/{id}", name="create_recipe")
     */
    public function createRecipe(Request $request, FileUploader $fileUploader, EntityManagerInterface $em, Category $category=null, Steps $steps=null): Response
    {
        $recipe = new Recipe;

        $form = $this->createForm(RecipeFormType::class, $recipe);
        $form->handleRequest($request);
        
        $imgs = [];
        $stepsImgs =[];
        
        if ($form->isSubmitted() && $form->isValid()) {

            $recipe->setCategory($category);

            $imageFile = $form->get('recipeImage')->getData();

            $imageFilesData = $request->files;

            foreach($imageFilesData as $imageFiles) {  
                 
                foreach($imageFiles as $value) {
         
                    foreach($value as $images) {
                       
                        foreach($images as $img){
                          
                            $imgs[] = $img; 
 
                        }
                    }
                }
            }

            foreach($recipe->getSteps() as $stepImg) {
                
                $stepsImgs[] = $stepImg;
            }

            foreach($imgs as $im) {
                
                $imageFileName2 = $fileUploader->upload($im);
                              
                foreach($stepsImgs as $s) {

                   $s->setStepsImage($imageFileName2);

                }      
            }
   

            if ($imageFile) {
                $imageFileName = $fileUploader->upload($imageFile);
            
                $recipe->setRecipeImage($imageFileName);
            }

            // dd($recipe);
            $em->persist($recipe);
           
            $em->flush();

            return $this->redirectToRoute('dashboard', []);
          
        }

        $formView = $form->createView();

        return $this->render('admin/recipe/createRecipe.html.twig', [
            'formView' => $formView,
            'recipe' => $recipe,
            'recipeId' => $recipe->getId(),
        ]); 

    }

此处为我配方表单类型:

<?php

namespace App\Form;

use App\Entity\Difficulty;
use App\Entity\Recipe;
use App\Form\CategoriesFormType;
use App\Form\RecipeIngredientsFormType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\File;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class RecipeFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('recipeName', TextType::class, [
                'label' => 'Name of the recipe',
                'attr' => [
                    'placeholder' => 'Type the name of the recipe',
                ],
                
            ])
            
            ->add('recipeImage', FileType::class, [

                'label' => 'Image of the recipe',

                "attr" => ['class' => "form-control"],

                'mapped' => false,

                'required' => false,

                'constraints' => [

                    new File([

                        'maxSize' => '10254k',

                        'mimeTypes' => [

                            'image/jpeg',

                            'image/png',

                            'image/webp',
                            
                            'image/jpg',

                        ],

                        'mimeTypesMessage' => 'Please upload une image valide',

                    ]),

                ],

            ])

            ->add('recipeDescription', TextareaType::class, [
                'label' => 'Description of the recipe',
                'attr' => [
                    'placeholder' => 'Description of the recipe',
                ],
                
            ])
            ->add('duration', NumberType::class, [
                'label' => 'Duration of the recipe',
                'attr' => [
                    'placeholder' => 'The duration of the recipe in minutes',
                ],
            ])

            ->add('difficulty', EntityType::class, [
                'label' => 'Difficulty',
                'placeholder' => 'Difficulty',
                'class' => Difficulty::class,
                'choice_label' => function(Difficulty $difficulty){
                    return strtoupper($difficulty->getDifficultyName());
                }
            ])

            ->add('recipeIngredients', CollectionType::class, [
                //le collection type need a element 'form , entity, ...
                'entry_type' => RecipeIngredientsFormType::class,
                'prototype' => true,
                'delete_empty' => true,
                // we allow adds on persist with the cascade_persist;
                //its gonna be a html and we can manipulate it with js
                'allow_add' => true,
                'allow_delete' => true,

                'by_reference' => false,
                'attr' => [
                    'class' => 'collection-recipeIngredients',
                ]

            ])

            ->add('steps', CollectionType::class, [
                //le collection type need a element 'form , entity, ...
                'entry_type' => StepsFormType::class,
                'prototype' => true,
                'delete_empty' => true,
                // we allow adds on persist with the cascade_persist;
                //its gonna be a html and we can manipulate it with js
                'allow_add' => true,
                'allow_delete' => true,

                'by_reference' => false,
                'attr' => [
                    'class' => 'collection-steps',
                ]

            ])
            
        ;
    }

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

下面是我的步骤表单类型:

<?php

namespace App\Form;

use App\Entity\Steps;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\File;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;

class StepsFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('stepDescription', TextareaType::class, [
                'label' => 'Description of the step',
                'attr' => [
                    'placeholder' => 'Description of the step',
                ],
            ])

            ->add('stepsImage' , FileType::class, [

                'label' => 'Image of the step',

                "attr" => ['class' => "form-control"],

                'mapped' => true,

                'required' => false,

                'constraints' => [

                    new File([

                        'maxSize' => '10254k',

                        'mimeTypes' => [

                            'image/jpeg',

                            'image/png',

                            'image/webp',
                            
                            'image/jpg',

                        ],

                        'mimeTypesMessage' => 'Please upload a valid image ',

                    ]),

                ],  

            ])
         
        ;
    }

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

这里是我用于collectionType的JQuery代码;

$(document).ready(function() { // Une fois que le document (base.html.twig) HTML/CSS a bien été complètement chargé...
    // add-collection-widget.js : fonction permettant d'ajouter un nouveau bloc "programme" au sein d'une question (pour agrandir la collection)
    $('.add-another-collection-widget').click(function (e) {
        let list = $($(this).attr('data-list-selector'))
       
        // Récupération du nombre actuel d'élément "programme" dans la collection (à défaut, utilisation de la longueur de la collection)
        let counter = list.data('widget-counter') || list.children().length
       
        // Récupération de l'identifiant de la question concernée, en cours de création/modification
        let recipe = list.data('recipe')
        console.log(recipe)
        // Extraction du prototype complet du champ (que l'on va adapter ci-dessous)
        let newWidget = list.attr('data-prototype')
        console.log(newWidget)
        // Remplacement des séquences génériques "__name__" utilisées dans les parties "id" et "name" du prototype
        // par un numéro unique au sein de la collection de "programmes" : ce numéro sera la valeur du compteur
        // courant (équivalent à l'index du prochain champ, en cours d'ajout).
        // Au final, l'attribut ressemblera à "question[programmes][n°]"
        newWidget = newWidget.replace(/__name__/g, counter)
        // Ajout également des attributs personnalisés "class" et "value", qui n'apparaissent pas dans le prototype original 
        newWidget = newWidget.replace(/><input type="hidden"/, ' class="borders"><input type="hidden" value="'+recipe+'"')
        // Incrément du compteur d'éléments et mise à jour de l'attribut correspondant
        counter++
        list.data('widget-counter', counter)
        // Création d'un nouvel élément (avec son bouton de suppression), et ajout à la fin de la liste des éléments existants
        var newElem = $(list.attr('data-widget-tags')).html(newWidget)
        addDeleteLink($(newElem).find('div.borders'))
        newElem.appendTo(list)
    })
    // anonymize-collection-widget.js : fonction permettant de supprimer un bloc "programme" existant au sein d'une question
    $('.remove-collection-widget').find('div.borders').each(function() {
        addDeleteLink($(this))
    })
    // fonction permettant l'ajout d'un bouton "Supprimer ce answer" dans un bloc "programme", et d'enregistrer l'évenement "click" associé
    function addDeleteLink($recipeIngredientsForm) {
        var $removeFormButton = $('<div class="block"><button type="button" class="button">Supprimer</button></div>');
        $recipeIngredientsForm.append($removeFormButton)
    
        $removeFormButton.on('click', function(e) {
            $recipeIngredientsForm.remove()
        })
    }
vxf3dgd4

vxf3dgd41#

你能做一个控制器的方案吗?我认为你在这方面有一个问题:

foreach($images as $img){
  
    $imgs[] = $img; 

  }
}

任何时候当你的操作进入这个函数,第一个索引都是0..有可能有这样的代码:

foreach($images as $key in $value) { $array[$key] = ... }

或者使用递增索引,在开头声明。($imgs[$index] = $img;$指数++ ;)

相关问题