Le passage de Laravel Forge à Laravel Cloud devait simplifier l’hébergement de Pinkary, une plateforme sociale open source construite avec Laravel et Livewire. Il a aussi révélé des hypothèses que l’ancienne infrastructure avait longtemps laissées passer : des UUID stockés dans des colonnes prévues pour des entiers, des requêtes tolérées par SQLite mais refusées par MySQL, et des traitements d’images conçus uniquement pour un disque local.
Cette migration a conduit l’équipe à remplacer SQLite par MySQL managé et le stockage local par un bucket compatible S3. Le changement n’a pas consisté à déplacer une application intacte d’un serveur vers un autre : il a fallu adapter le schéma, rendre les requêtes déterministes et repenser les accès aux fichiers. Pour les équipes qui préparent un déploiement similaire, Pinkary offre un cas d’école concret d’ingénierie logicielle : ce sont les détails du code, autant que l’infrastructure cloud, qui déterminent si le transfert se déroule sans mauvaise surprise.
En bref : les enseignements de la migration de Pinkary
- SQLite peut masquer des écarts de schéma : les UUID de Pinkary passaient dans des colonnes entières, jusqu’au passage à MySQL.
- MySQL a rendu les requêtes plus explicites : les fils de discussion ont été réécrits avec un classement par fenêtre et un ordre stable.
- Le stockage objet impose de revoir les habitudes locales : appels réseau, fichiers et traitements d’images ne se comportent pas comme sur un disque local.
- Des commandes Artisan dédiées ont fiabilisé le transfert des données et des fichiers, avec contrôles et vérifications avant la bascule.
- La scalabilité ne remplace pas la préparation : Laravel Cloud automatise une partie de l’exploitation, mais l’application doit être compatible avec sa base et son stockage.
Dans le paysage du développement web en 2026, Laravel continue de s’imposer comme un pilier incontournable pour concevoir des systèmes scalables performants et adaptés aux exigences actuelles. Lucas Giovanny, reconnu comme l’Artisan du Jour, incarne cette évolution à travers son…
Pourquoi Pinkary a quitté Laravel Forge pour Laravel Cloud
Lancée en 2024, Pinkary fonctionnait sur un serveur DigitalOcean administré avec Laravel Forge, une base SQLite locale et un répertoire de fichiers sur le serveur. Cette configuration était simple à gérer pour un projet de taille modeste. Le choix de Laravel Cloud répondait surtout à une volonté de regrouper l’hébergement des projets et leurs ressources managées sur une même plateforme ; Forge, lui, n’était pas à l’origine d’un problème de fonctionnement.
Le modèle d’exécution de Cloud a toutefois rendu deux changements incontournables. Les instances applicatives ne garantissent pas la persistance des fichiers locaux après un redémarrage ou un déploiement : SQLite et les téléversements ne pouvaient donc plus rester la source de vérité. Pinkary a adopté MySQL managé pour les données et un stockage objet compatible S3 pour les fichiers, dont les identifiants sont fournis à l’application par l’environnement Cloud.
Le transfert a été répété en direct avant la bascule finale, réalisée à faible trafic. Ce scénario était adapté à une petite application, mais ne constitue pas une méthode universelle sans interruption : un service qui continue d’accepter des écritures pendant la copie doit prévoir un instantané cohérent, un gel temporaire des écritures ou une synchronisation des changements restants. Le retour d’expérience technique consacré à Pinkary détaille les choix et corrections effectués.
Comment j’ai conçu un CRM intelligent propulsé par l’IA avec Laravel en une semaine
Dans un monde où la gestion efficace de la relation client devient un enjeu stratégique, la quête d’un CRM intelligent intégré à l’intelligence artificielle s’impose comme une solution incontournable. L’usage de Laravel, framework PHP réputé pour sa robustesse et sa…
SQLite vers MySQL : les écarts de schéma révélés par la migration
Des UUID dans des colonnes prévues pour des entiers
Dans Pinkary, les questions utilisaient des UUID, mais des migrations ultérieures avaient créé les champs d’auto-référence parent_id et root_id avec foreignId(), donc sous forme d’entiers non signés. SQLite, permissif sur les types, avait accepté les chaînes pendant plus d’un an. MySQL a exposé l’incompatibilité au moment de créer et d’utiliser les relations.
La correction a consisté à déclarer ces champs comme des UUID et à ajouter les contraintes de clé étrangère adaptées. Le problème ne venait pas de toute méthode Laravel de génération de clé étrangère : une déclaration telle que foreignIdFor(Question::class) peut déduire le type à partir du modèle. Ce sont ici les déclarations explicites en entiers qui ne reflétaient pas les données. La règle pratique est nette : exécuter les migrations et les requêtes représentatives sur le moteur réellement visé.
La casse des hashtags devait rester une décision métier
Pinkary distinguait les hashtags selon leur casse pour garantir leur identité, tout en permettant une recherche qui ne tenait pas compte des majuscules. Une collation insensible à la casse aurait pu fusionner des valeurs qui devaient rester différentes. La solution a conservé une comparaison binaire pour l’unicité et appliqué le traitement de casse au moment de l’autocomplétion.
Ce cas illustre une distinction essentielle : les règles d’identité et les règles de recherche ne sont pas nécessairement les mêmes. Les traduire séparément dans le schéma et la requête évite qu’un changement de moteur modifie discrètement le comportement fonctionnel.
Laravel MCP : Le Guide Complet pour Maîtriser Chaque Aspect
Laravel MCP s’impose en 2026 comme une avancée majeure pour tous les développeurs souhaitant intégrer l’intelligence artificielle à leur environnement de développement web. Ce framework PHP modernise l’architecture MVC classique en introduisant un protocole standardisé, le Model Context Protocol (MCP),…
Requêtes MySQL : rendre les fils de discussion déterministes
Pinkary ne possède pas de table de réponses séparée : les questions et leurs réponses sont représentées dans les mêmes lignes, reliées par root_id et parent_id. Les anciens flux regroupaient les lignes par fil à l’aide d’une expression équivalente à IFNULL(root_id, id), puis sélectionnaient des colonnes qui n’étaient ni agrégées ni incluses dans le regroupement. SQLite acceptait cette forme, mais MySQL, avec ONLY_FULL_GROUP_BY, la rejetait.
La réécriture classe les lignes à l’intérieur de chaque fil avec ROW_NUMBER(), puis joint la ligne gagnante à la requête principale. Le critère choisit la question la plus récemment mise à jour et utilise l’identifiant comme départage stable en cas d’égalité. Le résultat exprime clairement quelle ligne représente chaque discussion, plutôt que de compter sur un comportement implicite du moteur.
Les tests ont également révélé des hypothèses fragiles sur l’ordre des données. Plusieurs enregistrements pouvaient partager le même horodatage à la seconde ; les requêtes ont donc reçu un second critère de tri, comme id DESC, et les tests temporels ont utilisé les outils d’horloge de Laravel. Autre surprise : le modificateur after() modifiait l’ordre physique des colonnes sous MySQL, ce qui changeait l’ordre des clés dans la représentation Eloquent. Les tests ont été corrigés pour vérifier la présence des clés, pas leur position. L’ordre du stockage ne constitue pas, à lui seul, un contrat d’API.
Déployez une application Laravel MCP en production grâce à Laravel Cloud
Les développeurs Laravel disposent désormais d’une solution puissante pour déployer des applications web intégrant le Model Context Protocol (MCP) en production avec une simplicité déconcertante. Laravel Cloud révolutionne l’hébergement et le déploiement en offrant une scalabilité automatique, une sécurisation des…
Stockage S3 : supprimer les hypothèses liées au disque local
Utiliser le disque configuré plutôt qu’un nom codé en dur
Des appels à Storage::disk('public') forçaient Pinkary à utiliser le disque local, même après configuration d’un stockage distant. Ils ont été remplacés, là où aucun adaptateur précis n’était nécessaire, par l’accès au disque par défaut. Laravel peut ainsi résoudre le stockage depuis FILESYSTEM_DISK, aussi bien pour les avatars que pour les images de publications, les téléversements Livewire et les tâches de nettoyage.
La compatibilité de l’API Storage ne rend toutefois pas les opérations distantes gratuites. Pendant l’analyse du Markdown, Pinkary vérifiait l’existence de chaque image avant de générer sa balise. Sur S3, chaque exists() déclenchait une requête réseau. Le parseur construit désormais l’URL de l’objet sans contrôler sa présence à chaque rendu ; le navigateur gère l’échec éventuel de chargement. Une abstraction uniforme masque les différences d’usage, pas la latence réseau.
Encoder les images avant de les écrire dans le bucket
Le traitement des avatars a été adapté pour utiliser Intervention Image avec le pilote Imagick. Au lieu de modifier un chemin local sur place, le traitement produit un flux PNG, puis envoie les octets au stockage configuré. Cette approche évite de dépendre d’un chemin de fichier disponible sur la machine applicative.
Les images de publications ont posé un autre problème : un GIF animé d’environ 5 Mo pouvait atteindre près de 35 Mo après décodage, redimensionnement et réécriture de toutes ses images. Pinkary a choisi de conserver les GIF sans optimisation et de traiter les autres formats en encodant le résultat dans un flux avant l’appel à Storage::put(). Modifier une image en mémoire ne suffit pas à la persister dans S3 : l’écriture du flux vers le disque distant est l’étape décisive.
Transférer les données et les fichiers avec des commandes Artisan contrôlées
La première tentative de conversion à partir d’un dump SQL a mal traité certaines valeurs proches d’UUID et des données de cache sérialisées. L’équipe a abandonné cette stratégie au profit de commandes Artisan ponctuelles, capables de se connecter aux deux bases et de copier les données selon leur forme réelle.
La commande de base de données vérifiait les connexions, comparait les colonnes, refusait d’importer dans une cible déjà peuplée et copiait les lignes par lots stables dans une transaction côté destination. Elle réinitialisait aussi les compteurs auto-incrémentés et affichait les nombres de lignes source et cible. Ces contrôles rendent le processus observable, mais une égalité des comptes ne prouve pas à elle seule l’intégrité de toutes les relations.
La copie des fichiers suivait la même logique de prudence : vérifier l’accès aux deux disques, limiter le transfert aux avatars et images concernés, envoyer les objets en flux pour éviter de charger les fichiers entiers en mémoire, puis signaler les écarts de taille. Les fichiers déjà présents étaient ignorés, sauf demande explicite de remplacement. Ces garde-fous sont plus utiles qu’un transfert rapide dont le résultat reste impossible à vérifier.
- Avant la copie, vérifier l’accès à la source et à la destination, ainsi que la structure du schéma cible.
- Pendant le transfert, procéder par lots, conserver un ordre stable et éviter de charger les fichiers volumineux intégralement en mémoire.
- Avant la bascule, comparer les volumes, valider les relations et garantir une source cohérente, par instantané ou par gel temporaire des écritures.
- Après le déploiement, surveiller les erreurs de base de données, les accès au stockage objet et les tâches d’arrière-plan.
Le cache contenait des clés temporaires de déduplication pour les vues, conservées pendant 120 minutes ; les compteurs durables, eux, étaient enregistrés dans les tables des utilisateurs et des questions. Cette distinction évite de confondre état transitoire et donnée métier. Une file d’attente ou un cache peut protéger une opération, mais ne doit pas devenir l’unique copie d’une donnée durable.
Ce que Laravel Cloud automatise — et ce qui reste à la charge du code
Laravel Cloud réduit la gestion directe des serveurs et rassemble l’application avec des ressources managées. Cette automatisation peut faciliter le déploiement et accompagner la scalabilité, mais elle ne corrige pas les colonnes mal typées, les tris indéterministes ou les appels réseau inutiles. Pinkary n’a pas fait l’objet d’un benchmark avant-après contrôlé : l’équipe a trouvé l’application plus rapide après le transfert, mais le changement simultané de base et de stockage empêche d’attribuer cette impression à une seule cause.
Pour comparer les modèles d’hébergement et leurs compromis de contrôle, le panorama de Forge, Vapor et Cloud apporte un éclairage utile. Les équipes qui préparent leur application peuvent aussi consulter cette liste de vérifications avant un déploiement sur Laravel Cloud, notamment pour repérer les dépendances au stockage local et au moteur de base de données.
La migration de Pinkary a surtout montré que le passage vers une plateforme managée est un exercice de compatibilité autant qu’un changement d’hébergement. Tester tôt sur MySQL, définir explicitement les règles de tri et traiter le stockage objet comme un service réseau transforment les surprises de dernière minute en corrections maîtrisables. Pour approfondir les questions d’architecture et de préparation, le retour de Laravel sur la migration de Pinkary complète ce cas pratique.