Lorsqu’on travaille avec un stockage distant comme S3, Cloudflare R2 ou un autre CDN compatible S3, la gestion des fichiers peut rapidement devenir un problème si elle n’est pas correctement contrôlée.
Un cas fréquent apparaît lors de la suppression de fichiers liés à une base de données.
Dans cet article, nous allons voir :
- le problème courant avec Laravel
- pourquoi il peut créer des incohérences
- une stratégie pour sécuriser la suppression des fichiers
Le problème : Laravel ne signale pas toujours les erreurs de suppression
Laravel propose une abstraction très pratique pour la gestion des fichiers avec Laravel via Laravel Filesystem, qui repose sur Flysystem.
Lorsqu’on utilise un stockage distant comme Amazon S3, il arrive que la suppression d’un fichier échoue silencieusement.
Par exemple :
Storage::disk('s3')->delete($path);
Langage du code : PHP (php)
Si le fichier n’est pas supprimé pour une raison quelconque (latence réseau, permissions, erreur API…), aucune exception n’est forcément levée.
Le problème apparaît alors dans ce scénario :
- La ligne est supprimée dans la base de données
- Le fichier reste sur le CDN
- La relation entre la base et le fichier est perdue
Résultat :
- fichiers orphelins sur le stockage
- incohérences de données
- coûts de stockage inutiles
- impossibilité de retrouver les fichiers
Le cas de Spatie Media Library
La librairie Spatie Laravel Media Library est largement utilisée pour gérer les fichiers associés aux modèles Eloquent.
Elle gère automatiquement :
- l’upload
- les conversions
- les relations avec la base de données
Cependant, la suppression reste dépendante du filesystem Laravel. De plus la suppression n’est exécutée uniquement sur l’évènément de suppression du modèle lié en base de donnée via un observer (deleted).
Donc les mêmes risques existent si la suppression échoue côté stockage.
Une stratégie plus sûre : vérifier avant de supprimer la base
Une approche simple consiste à :
- utiliser une transaction SQL
- récupérer le fichier
- supprimer le modèle
- vérifier que le fichier est réellement supprimé
- rollback si nécessaire
Exemple :
// 1. Démarre une transaction
DB::beginTransaction();
try {
// 2. Récupère le média associé
// Dans le cas ou le modèle contient qu'un seul media
$media = $model->media->first();
// 3. Supprime le modèle
$media->forceDelete();
// 4. Vérifie que le fichier a bien disparu
if (Storage::disk($media->disk)->exists($media->getPath())) {
throw new \Exception("File {$media->getPath()} still exists");
}
} catch (\Exception $e) {
// 5. Rollback si ce n’est pas le cas
DB::rollBack();
throw $e;
}
DB::commit();
Langage du code : PHP (php)
Cette solution garantit :
✔ cohérence entre la base et le stockage
✔ détection immédiate d’un problème CDN
✔ rollback automatique
✔ pas de fichiers orphelins
Amélioration possible : supprimer le fichier explicitement
Une approche encore plus claire consiste à supprimer le fichier explicitement avant la suppression du modèle :
Storage::disk($media->disk)->delete($media->getPath());
Langage du code : PHP (php)
Puis vérifier :
if (Storage::disk($media->disk)->exists($media->getPath())) {
throw new Exception("File deletion failed");
}
Langage du code : PHP (php)
Et seulement ensuite supprimer la ligne en base.
Cela inverse la logique :
- supprimer le fichier
- vérifier
- supprimer la base
Conclusion
Lorsque l’on utilise Amazon S3 avec Laravel et Spatie Laravel Media Library, il est important de ne pas supposer que la suppression de fichier fonctionne toujours correctement.
Sans vérification, on peut rapidement se retrouver avec :
- des fichiers orphelins
- des incohérences base / stockage
- des coûts de stockage inutiles
La solution consiste à :
- Supprimer la ligne en BDD après s’être assuré que le fichier a été supprimé sur le CDN, ou utiliser des transactions dans le cas où il n’est pas possible de supprimer la ligne après (en utilisant Spatie)
- vérifier explicitement la suppression
- centraliser la logique dans un service
Cette approche simple permet de garantir l’intégrité entre votre base de données et votre stockage distant.

