Dans le précédent article, on a commencé à s'intéresser à la gestion des images. On dispose désormais de la liste des images enregistrées en forme de liste paginée avec une miniature, le lien, et quelques actions : copier le lien et supprimer l'image. On avait laissé en suspens la modification d'une image avec un bouton encore sans action. Dans le présent article, on va coder cette partie.
Pour rappel, la table des matières est ici.
Un composant pour modifier les images
On a à nouveau besoin d'un composant Volt pour gérer les images :
php artisan make:volt admin/images/edit --class
On va ajouter la route pour l'atteindre (réservée aux administrateurs) :
Route::middleware('auth')->group(function () {
...
Route::middleware(IsAdminOrRedac::class)->prefix('admin')->group(function () {
...
Route::middleware(IsAdmin::class)->group(function () {
...
Volt::route('/images/{year}/{month}/{id}/edit', 'admin.images.edit')->name('images.edit');
On ajoute le lien dans la liste des images (images.index) :
<x-popover>
<x-slot:trigger>
<x-button icon="c-wrench"
link="{{ route('images.edit', ['year' => $selectedYear, 'month' => $selectedMonth, 'id' => $loop->index + ($page - 1) * $perPage]) }}"
On peut dorénavant atteindre le nouveau composant, il ne nous reste plus qu'à le coder.
Un package pour la gestion des images
Il existe un package incontournable pour la gestion des images : Intervention Image.
L'installation pour Laravel est décrite ici. On passe évidemment par composer :
composer require intervention/image-laravel
Il suffit ensuite de publier la configuration :
php artisan vendor:publish --provider="Intervention\Image\Laravel\ServiceProvider"
Ce package est une mine d'outils pour manipuler les images ! On va l'utiliser pour notre CMS.
Le code du composant et son aspect
Le code de notre nouveau composant est trop long pour être copié dans cet article. Récupérez-le dans le fichier ZIP dont le lien de téléchargement figure tout en bas. Je ne détaillerai pas tout le code, mais je vais expliquer les principales fonctionnalités. On aura encore besoin de traductions :
"Manage an image": "Gérer une image",
"The url of this image is :": "L'URL de cette image est :",
"This image is in the post ": "Cette image est dans l'article ",
"This image is in the page ": "Cette image est dans la page ",
"Select a rescale value": "Sélectionnez une valeur de redimensionnement",
"Height": "Hauteur",
"Width": "Largeur",
"Size change": "Changement des dimensions",
"Brightness": "Luminosité",
"Brightness, contrast and gamma correction": "Correction de luminosité, contraste et gamma",
"Contrast": "Contraste",
"Restore image to its original state": "Restaurer l'image à son état d'origine",
"Color correction": "Correction de couleur",
"Valid changes": "Valider les modifications",
"Changes validated": "Modifications validées",
"Finish and keep this version": "Terminer et garder cette version",
"This image is not used": "Cette image n'est pas utilisée",
"Finish and discard this version": "Terminer et oublier cette version",
"Are you sure to delete this image?": "Etes-vous sûr de vouloir supprimer cette image ?",
"Blur and sharpen": "Flou et netteté",
"Blur": "Flou",
"Sharpen": "Netteté",
La page est constituée de deux parties, à gauche, on a :
On peut y voir l'image, son URL, l'article ou la page où elle se trouve. On a aussi un bouton pour copier son URL.
Dans la partie de droite, on a les paramètres et toutes les actions possibles :
Pour gérer les retours en arrière, on travaillera sur une copie de l'image, ce qui est aussi plus prudent.
Chargement de l'image
La méthode "mount"
Au chargement du composant, on a la méthode mount qui s'active :
public function mount($year, $month, $id): void
{
$this->year = $year;
$this->month = $month;
$this->id = $id;
$this->getImage($year, $month, $id);
$this->usage = $this->findUsage();
$this->saveImageToTemp(false);
$this->getImageInfos();
}
On réalise un certain nombre d'initialisations :
- récupération de l'année, du mois et de l'id de l'image dans l'URL
- avec ces informations, on appelle la fonction getImage pour récupérer l'image
- on cherche dans quel article ou page elle se trouve, avec la fonction findUsage
- on crée une version temporaire avec la fonction saveImageToTemp
- enfin, on récupère les dimensions de l'image avec la fonction getImageInfos
La méthode getImage
Pour charger effectivement l'image, on a la méthode getImage :
public function getImage($year, $month, $id): void
{
$imagesPath = "public/photos/{$year}/{$month}";
$allFiles = Storage::files($imagesPath);
$image = $allFiles[$id];
$this->imagePath = Storage::path($image);
$this->fileName = basename($this->imagePath);
$this->image = Storage::url('public/temp/' . $this->fileName);
$this->displayImage = Storage::url($image);
$this->refreshImageUrl();
}
- on construit un chemin vers un dossier contenant des photos ($imagesPath), en utilisant l'année et le mois fournis
- on récupère tous les fichiers dans le dossier spécifié et les stocke dans un tableau $allFiles
- on sélectionne le fichier spécifique du tableau $allFiles en utilisant l'$id fourni comme index
- on obtient le chemin complet du système de fichiers pour l'image sélectionnée et on le stocke dans la propriété imagePath
- on extrait le nom du fichier à partir du chemin complet et le stocke dans la propriété fileName
- on génère une URL pour accéder au fichier dans un dossier temporaire et on la stocke dans la propriété image
- on génère une URL pour accéder à l'image originale et la stocke dans la propriété displayImage
- enfin, on rafraîchit l'URL de l'image
Déterminer où l'image se trouve
Une information intéressante est de savoir dans quel article ou page se trouve l'image (peut-être plusieurs), et d'ailleurs peut-être dans aucune et dans ce cas, elle se retrouve orpheline et peut sans doute être supprimée. C'est la fonction findUsage qui est chargée de cette tâche :
private function findUsage(): array
{
$usage = [];
$name = $this->year . '/' . str_pad($this->month, 2, '0', STR_PAD_LEFT) . '/' . $this->fileName;
$posts = Post::select('id', 'title', 'slug')
->where('image', 'LIKE', "%{$name}%")
->orWhere('body', 'LIKE', "%{$name}%")
->get();
foreach ($posts as $post) {
$usage[] = [
'type' => 'post',
'id' => $post->id,
'title' => $post->title,
'slug' => $post->slug,
];
}
$pages = Page::where('body', 'LIKE', "%{$name}%")->get();
foreach ($pages as $page) {
$usage[] = [
'type' => 'page',
'id' => $page->id,
'title' => $page->title,
];
}
return $usage;
}
En gros, on détermine le lien de l'image puis on lance une requête pour la chercher dans les articles et les pages.
Une action sur une image
Je ne vais évidemment pas passer en revue toutes les actions possibles sur une image (je me suis un peu fait plaisir en explorant largement les possibilités du package). On va s'intéresser à une action fréquente qui consiste à modifier la taille de l'image. On ne prévoit que la réduction de la taille. Si vous regardez les paramètres :
On trouve les valeurs actuelles de l'image et quelques boutons pour changer sa taille. Quand on a fait une réduction, de nouveaux boutons apparaissent sous le tableau des paramètres :
Comme on travaille sur une image temporaire, on a la possibilité de restaurer l'image originelle, mais aussi de valider les modifications (dans ce cas l'image temporaire doit écraser l'image d'origine), et on a deux possibilités de sortie : avec ou sans sauvegarde.
Une action sur un bouton de redimensionnement active la fonction updated :
public function updated($property, $value)
{
...
$manager = new ImageManager(new Driver());
$image = $manager->read($this->tempPath);
switch ($property) {
case 'imageScale':
$image->scale(height: $this->height * $value);
$this->width = $image->width();
$this->height = $image->height();
$this->imageScale = '1';
On utilise ImageManager pour redimensionner l'image avec la méthode scale. Le package est vraiment facile à utiliser. Ensuite, on renseigne les nouvelles valeurs à afficher pour la hauteur et la largeur et on remet le curseur (imageScale) à l'unité pour une nouvelle action.
La validation du changement (qui n'est pour le moment que sur l'image temporaire) se fait par la fonction applyChanges :
public function applyChanges(): void
{
if (File::exists($this->tempPath)) {
File::copy($this->tempPath, $this->imagePath);
}
$this->changed = false;
$this->success(__('Image changes applied successfully'));
}
On écrase l'image originelle avec l'image temporaire.
À l'inverse, pour restaurer l'image originelle, on a la fonction restoreImage :
public function restoreImage($cancel): void
{
if (File::exists($this->imagePath)) {
File::copy($this->imagePath, $this->tempPath);
$this->refreshImageUrl();
$this->clipW = 0;
$this->clipH = 0;
$this->getImageInfos();
$this->success(__('Image restored'));
}
$this->changed = false;
if ($cancel) {
$this->info(__('No modification has been made'));
$this->exit();
}
}
On a la copie inverse dans ce cas.
Vous pouvez explorer tous les autres effets qui sont tous bâtis sur le même principe.
Conclusion
Dans le prochain article, nous mettrons en place un peu d'intendance : notifications, paramétrage...
Pour vous simplifier la vie, vous pouvez charger le projet dans son état à l’issue de ce chapitre.
Par bestmomo
Aucun commentaire