Iterer sur un objet envoyé en POST (Ajax) dans un controller ?

Par Jean Sérien, il y a 1 an


Bonjour à tous,

J'ai mis en place une drop-zone indépendante pour des images, celle-ci n'est pas un contrôle de formulaire...

La partie javascript marche parfaitement, j'ai bien ma liste d'images draguées qui est envoyée au controller Symfony.

Je dois traiter ensuite chaque image en passant par un service "PictureService" qui était à l'époque destiné à traiter des fichiers "UploadedFile".

Le problème est que je ne sais pas comment adapter chaque fichier image pourqu'il soit traité par le service, dans le code ci-dessous, je crée un nouvel objet Uploadedfile pour chaque image mais je n'arrive pas à récupérer les propriétés de chaque objet dans le tableau...

Se pourrait t'il que mon objet JSON ne soit pas bon ? Voici ce que m'affiche le getContent() :

{#748 +"files": {#745 +"0": {#744} +"1": {#746} +"2": {#747} } +"galerieId": "81" }

Comme vous le voyez, les propriétés de chaque image sont inaccessibles, par contre je peux accéder à la propriété "galerieId" sans problème...

Je pense qu'il y a une erreur dans l'envoi du fichier "files" dans la requête Ajax (faut-il vraiment utiliser stringify pour un objet ?).

D'autre-part, faut-il créer un nouvel objet "Uploadedfile" pour chaque image comme je tente de le faire ou bien exite il une autre méthode qui permettrait le traitement dans le service cité ? (J'ai essayé en changeant Uploadedfile pour File, ça ne marche pas non plus)

Merci d'avance pour votre expertise

drop-area.js:

const handleDrop = (e) => { const dataTransfer = e.dataTransfer; const files = dataTransfer.files; const listeImages = [...files]; console.log(listeImages); //Affichage des détails des images en tableau en cours d'importation const tableauDetails = document.querySelector(".tableau-details-images"); tableauDetails.innerHTML = ""; tableauDetails.insertAdjacentHTML( "afterbegin", `<span>${listeImages.length} images sélectionnées : </span>` ); let i = 1; listeImages.forEach((img) => { tableauDetails.insertAdjacentHTML( "beforeend", `<div class="details-image">${i}. ${img.name} ${readableFileSize( img.size )} ${img.type}</div>` ); i++; }); //envoi de la liste d'images vers le controller Galeries de symfony const envoiListeImages = async () => { const galerie = document.querySelector(".container-galerie"); let galerieId = galerie.dataset.id; try { const response = await fetch(`/admin/galeries/importer-images`, { method: "POST", headers: { "X-Requested-with": "XMLHttpRequest", "Content-Type": "application/json;charset=UTF-8", }, body: JSON.stringify({ files, galerieId }), }); const data = await response.json(); console.log(data); } catch (error) { console.error("Errors : ", error); } }; envoiListeImages(); };

importer-imagesController.php:

#[Route('importer-images', 'importer_images', methods: ['POST'])] public function importerImages(Request $request, EntityManagerInterface $em, PictureService $pictureService): Response { if ($request->isXmlHttpRequest()) { $postData = json_decode($request->getContent()); // dd($postData); $galerie = $postData->galerieId; // dd($galerie); $listeImages = $postData->files; // dd($listeImages); foreach ($listeImages as $image) { $image = new UploadedFile("", $image, $image->name, $image->type); $folder = 'images'; $fichier = $pictureService->add($image, $folder, 300, 300); $img = new Images(); $img->setName($fichier); $galerie->addImage($img); } $em->persist($galerie); $em->flush(); $this->addFlash('success', 'La nouvelle galerie d\'images a été actualisée dans la base.'); return new JsonResponse('Réponse en json'); } return new JsonResponse(['error' => 'Cet appel doit être effectué via AJAX.'], Response::HTTP_BAD_REQUEST); }

8 réponses

popotte, il y a 1 an

Hello :)

Alors dans ton cas il ne faut pas utiliser de json pour les files, mais un tableau de json:

{#748 +"files": [ +"0" => {#744} +"1" => {#746} +"2" => {#747} ] +"galerieId": "81" }

Un json ce n'est pas fait pour etre parcouru mais pour récupérer un élément spécifique à partir d'une clé, alors si il y a 1, 2, 3... c'est un tableau ^^

Jean Sérien, il y a 1 an

Merci Popotte pour ta réponse

J'avais pensé à convertir mon tableau grâce au spread operator dans le js, j'ai donc maintenant un tableau Json exactement comme celui que tu m'as envoyé mais pour pouvoir l'exploiter en tant qu'objet dans le controller je suppose qu'il faut le désérialiser ?

J'ai essayé de le faire en injectant le "serializerInterface" dans les paramètres de ma fonction mais il ne reconnaît pas la commande "serialize" ni "deserialize" malgré que les Use soient importés correctement... Que fais-je mal ?

popotte, il y a 1 an

De rien ;)

Mmmh plus simple d'utiliser les fonctionnalité de PHP basiques: json_encode et json_decode

Jean Sérien, il y a 1 an

Bonjour Popotte

Que je fasse n'importe quoi le tableau d'objet ne retourne que des chiffres précédés d'un "#", j'ai bien les index dans le tableau mais pas les keys/values, inexploitable !
Par contre j'ai bien la variable "galerieId" de disponible en tant qu'objet, à n'y rien comprendre...
On dirait que le tableau de valeursn'est pas convertit malgré le couple classique stringify en js et json_decode en php...

La console du navigateur donne ceci grâce au spread (tout est normal) :

liste images : Array(4) [ File, File, File, File ] ​ 0: File { name: "Look2-111bbbb-2vb.jpg", lastModified: 1718454649514, size: 3782549, … } ​​ lastModified: 1718454649514 ​​ name: "Look2-111bbbb-2vb.jpg" ​​ size: 3782549 ​​ type: "image/jpeg" ​​ webkitRelativePath: "" ​​ <prototype>: FilePrototype { name: Getter, lastModified: Getter, webkitRelativePath: Getter, … } ​ 1: File { name: "Look2-150b-2.jpg", lastModified: 1718454649546, size: 3964591, … } ​ 2: File { name: "Look2-265.jpg", lastModified: 1718454649925, size: 12624466, … } ​ 3: File { name: "Look2-292vvv-3.jpg", lastModified: 1718454649942, size: 556375, … } ​ length: 4 ​ <prototype>: Array [] drop-images.js:28:11

Par contre une fois utilisé json_decode en false, null ou true dans le controller ($postData = json_decode($request->getContent(),false);

J'ai toujours ceci :

{#747 +"listeImages": array:4 [ 0 => {#743} 1 => {#744} 2 => {#745} 3 => {#746} ] +"galerieId": "201" }

A noter que les valeurs entre "{}" ne sont pas accessible par click (expansion)

Ca serait pas le fait que les ligne de données soient préfixées par "FILE" ?

Ca doit etre bête mais je m'arrache les cheveux ...

popotte, il y a 1 an

C'est normal, en gros tu accèdes à chaque élément depuis un forEach ou un map, enfin une boucle

foreach ($listeImages as $image) { // et ici tu a accès a chaque File dump($image->name); } dd($listeImages);
Jean Sérien, il y a 1 an

Bonjour Popotte

Toujours pareil : impossible de lire les propriétés du tableau !

Si je fais :

foreach ($listeImages as $image) { dd($image->name); }

Il me renvoie : "Warning: Undefined property: stdClass::$name", on en revient toujours au même, le tableau ne contient pas les données escomptées, seulement des "#" suivis de numéros, sans possibiliter d'accéder aux propriétés...

Voir ici :

{#746 +"listeImages": array:3 [ 0 => {#743} 1 => {#744} 2 => {#745} ] +"galerieId": "201" }

En principe il devrait y avoir les keys/values dans les crochets non ?

J'ai tranformé en formData : aucune différence
J'ai utilisé tous les json_decode(true,false,null) : aucune différence

J'ai déjà fait des milliers d'envois Ajax de ce genre vers des controllers Symfony, c'est la première fois que ça foire, à chaque fois j'utilise les couples JSON.stringify ou formData/Json_decode().

Là le tableau n'est pas correct, j'ai forcément oublié un détail...
J'ai fait un e.preventDefault partout où nécessaire aussi...
Je pencherai plutôt sur une erreur dans le js ...

Je sèche...

Voici le code actualisé :

//envoi de la liste d'images vers le controller Galeries de symfony const envoiListeImages = async (e) => { e.preventDefault(); const galerie = document.querySelector(".container-galerie"); let galerieId = galerie.dataset.id; const formData = new formData(); formData.append("listeImages", JSON.stringify(listeImages)); formData.append("galerieId", JSON.stringify(galerieId)); try { const response = await fetch(`/admin/galeries/importer-images`, { method: "POST", headers: { "X-Requested-with": "XMLHttpRequest", "Content-Type": "application/json; charset=UTF-8", }, body: formData, }); const data = await response.json(); console.log("postData : ", data); } catch (error) { console.error("Errors : ", error); } }; envoiListeImages(); };
Jean Sérien, il y a 1 an

Bon , j'ai trouvé la solution, il fallait itérer sur le tableau listeImages dans le form data :

//Création du formData const formData = new FormData(); //Traitement du tableau d'images listeImages.forEach((file) => { formData.append("listeImages[]", file); });

Merci pour ton aide

grafitfit, il y a 1 an

Merci pour ta réponse ,vous mavez boucaup aider