Formation PUB030 : Laravel, 2019 Le formulaire de modification

29.5 Gérer les images dans un formulaire de modification


Lorsqu'on modifie un item auquel une ou plusieurs images peuvent être associées, il faut prendre un soin particulier pour bien gérer les images, autant dans le formulaire que dans la méthode d'action qui met à jour des données.

Dans le formulaire

Je vous propose ici une technique qui assurera que le fonctionnement du formulaire d'édition soit intuitif pour l'usager.

Affichage initial

L'affichage initial sera différent selon qu'il peut y avoir une seule image ou que plus d'une image peuvent être associées à l'item.

Dans tous les cas, l'attribut name de la balise <input type="file"> devra correspondre au nom utilisé dans le fichier de validation. On utilisera généralement le nom du champ qui contiendra le nom de l'image mais ce n'est pas obligatoire (ex : <input type="file" name="photo">).

Si une seule image peut être associée à l'item

Il s'agit du cas le plus simple. On sera dans cette situation, par exemple, si on permet aux usagers d'associer un avatar à leur profil ou si un produit ne peut avoir qu'une seule image.

  • Lors de l'affichage initial du formulaire de modification, l'image est affichée. Il n'est généralement pas utile d'afficher d'autres informations sur cette image. Le visuel suffit.
  • Un lien ou un bouton permet de supprimer l'image.
  • La balise HTML <input type="file"> n'est présente que si aucune image n'était associée à l'item.

Édition d'un item qui a une image

Édition d'un item qui n'a encore aucune image

Si plusieurs images peuvent être associées à l'item

  • Lors de l'affichage initial du formulaire, toutes les images sont affichées.
  • Un lien ou un bouton permet de supprimer les images individuellement.
  • La balise HTML <input type="file"> est là en tout temps pour permettre d'ajouter une image. Dès qu'une image est choisie avec cette balise, une nouvelle balise <input type="file> est ajoutée à l'aide de jQuery.
  • On ajoutera [] à l'attribut name de la balise afin qu'elle puisse contenir plusieurs images (ex : <input type="file" name="photo[]">).

Édition d'un item qui a plusieurs images

Clic sur le bouton de suppression de l'image

J'ai choisi de diminuer l'opacité d'une image pour indiquer visuellement que l'image sera supprimée lors de la soumission du formulaire. Pour y arriver, j'ai ajouté un peu de jQuery et de CSS.

Un second clic sur le bouton remettra l'image dans son état original.

 

jQuery

$('.crud .supprimer').click(function() {

    $image = $(this).parent().prev();   // on a <img><div class="crud"><img class="supprimer"></div>

    $image.toggleClass('imageasupprimer');

});

CSS

.imageasupprimer {

    opacity: 0.4;

}

Image à supprimer

Dans le cas où une seule image peut être associée à l'item, un clic sur le bouton de suppression de l'image sera accompagné par l'ajout d'une balise <input type="file> à l'aide de jQuery.

Transférer au contrôleur les images à ajouter ou à supprimer

La vue devra pouvoir fournir au contrôleur la liste des images à ajouter de même que la liste des images à supprimer.

Images à ajouter

Pour les images à ajouter, il n'y a pas vraiment de problème. C'est la balise <input type="file"> qui transmettra la liste des fichiers à ajouter. Dans le contrôleur, le paramètre $request pourra retrouver la ou les images dans son attribut file (ex : $file = $request->file('photo'); ou $file = $request['photo']; ou plus simplement $file = $request->photo;)

Images à supprimer

Ici, il faut utiliser une astuce pour conserver une liste des fichiers à supprimer.

Lors de la soumission du formulaire, on créera un champ caché qui contient la liste des images ayant la classe "imageasupprimer". On aura pris le soin, lors de la génération des balises <img>, de stocker l'identifiant de l'image dans l'attribut data-id.

HTML

<img data-id="{{ $photo->id }}" src="{{ URL::asset('medias/commun/produits/' . $produit->slug . '/' . $photo->photo) }}" ...>

On pourra donc récupérer la liste des identifiants à l'aide de jQuery dans un tableau, que l'on convertira au format JSON à l'aide de la fonction jQuery JSON.stringify().

jQuery

$('form').submit(function() {

    var imagesASupprimer = [];

 

    // retrouve l'identifiant de chaque image à supprimer

    $('.imageasupprimer').each(function() {

        imagesASupprimer.push($(this).data('id'));   // data('id') est l'équivalent de attr('data-id')

    });

 

    // ajoute le champ caché dans le formulaire juste avant la soumission

    $('<input>').attr({

        type: 'hidden',

        name: 'jsonImagesASupprimer',

        id: 'jsonImagesASupprimer',

        value: JSON.stringify(imagesASupprimer)

    }).appendTo('form');

});

Il sera donc possible d'accéder à la liste des fichiers à supprimer à l'aide de $request->jsonImagesASupprimer, qui contiendra une valeur sous la forme ["8","9","11"].

Dans le contrôleur

Lorsqu'une image doit être ajoutée ou doit ne doit plus être associée à un item, il faut travailler à deux niveaux :

  • l'ajouter ou la supprimer du serveur
  • et la l'ajouter ou la supprimer de la base de données.

Opérations sur les fichiers sur le serveur

Images à ajouter

Avant de débuter le traitement, il faut s'assurer qu'il y a effectivement des images à ajouter. En effet, $request['photos'] n'existera simplement pas si aucune nouvelle image n'est à ajouter.

Pour chacune des images à ajouter, le traitement sera le même que lors de l'ajout initial des images :

  • On commencera par renommer le fichier afin qu'il n'y ait aucun espace, caractère accentué ou caractère spécial dans le nom puis on lui aura ajouté une chaîne unique pour assurer qu'aucun autre fichier ne porte le même nom.
  • On procédera ensuite au téléversement de l'image.

Images à supprimer

L'identifiant des images à supprimer a été encodé en JSON. Pour transformer le tout en tableau, on utilisera la fonction PHP json_decode().

Contrôleur Laravel (PHP)

$imagesASupprimer = json_decode($request->jsonImagesASupprimer);

Pour chacune des images à supprimer, il faudra :

  • Retrouver l'enregistrement correspondant à l'identifiant reçu de la vue.
    Contrôleur Laravel (PHP)

    $photoProduit = PhotoProduit::findOrFail($imageASupprimer);

  • Supprimer physiquement le fichier à l'aide de \File::Delete().
    Contrôleur Laravel (PHP)

    \File::Delete(public_path() . "/medias/commun/produits/$produit->slug/$photoProduit->photo");

N'oubliez pas de placer le tout dans un try...catch afin de bien réagir dans le cas où l'enregistrement ne sera pas retrouvé ou qu'un problème empêcherait la suppression du fichier.

Opérations dans la base de données

Si une seule image peut être associée à l'item, son nom sera stocké directement dans l'enregistrement de l'item. il suffira alors d'enregistrer le nouveau nom du fichier ou de le remettre à blanc si l'image a été supprimée.

Dans le cas où plusieurs images peuvent être associées à l'item, on travaillera avec une seconde table contenant une clé étrangère vers l'item. On ajoutera un enregistrement dans cette table pour chacune des images à ajouter. On supprimera chacun des enregistrements correspondant aux images à supprimer.

Ex :

Contrôleur Laravel (PHP)

$photoProduit->delete();

Finalement, il ne faut pas oublier d'enregistrer les autres informations sur l'item qui vient d'être modifié.

▼Publicité

Veuillez noter que le contenu de cette fiche vous est partagé à titre gracieux, au meilleur de mes connaissances et sans aucune garantie.
Merci de partager !
Soumettre