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.
Je vous propose ici une technique qui assurera que le fonctionnement du formulaire d'édition soit intuitif pour l'usager.
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">).
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.
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.
$('.crud .supprimer').click(function() {
$image = $(this).parent().prev(); // on a <img><div class="crud"><img class="supprimer"></div>
$image.toggleClass('imageasupprimer');
});
.imageasupprimer {
opacity: 0.4;
}
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.
La vue devra pouvoir fournir au contrôleur la liste des images à ajouter de même que la liste des images à supprimer.
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;)
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.
<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().
$('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"].
Lorsqu'une image doit être ajoutée ou doit ne doit plus être associée à un item, il faut travailler à deux niveaux :
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 :
L'identifiant des images à supprimer a été encodé en JSON. Pour transformer le tout en tableau, on utilisera la fonction PHP json_decode().
$imagesASupprimer = json_decode($request->jsonImagesASupprimer);
Pour chacune des images à supprimer, il faudra :
$photoProduit = PhotoProduit::findOrFail($imageASupprimer);
\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.
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 :
$photoProduit->delete();
Finalement, il ne faut pas oublier d'enregistrer les autres informations sur l'item qui vient d'être modifié.
▼Publicité