• Assurez la sécurité des applications de la bonne manière! Détectez, protégez, surveillez, accélérez et plus encore…
  • Laravel, c'est beaucoup de choses. Mais rapide n'en fait pas partie. Apprenons quelques astuces du métier pour le rendre plus rapide!

    Aucun développeur PHP n'est épargné par Laravel ces jours-ci. Il s'agit soit d'un développeur junior ou de niveau intermédiaire qui aime le développement rapide offert par Laravel, soit d'un développeur senior qui est obligé d'apprendre Laravel en raison des pressions du marché.

    Quoi qu'il en soit, il est indéniable que Laravel a revitalisé l'écosystème PHP (j'aurais certainement quitté le monde PHP si Laravel n'était pas là).

    Un extrait d'éloge de soi (quelque peu justifié) de Laravel

    Cependant, puisque Laravel se plie en quatre pour vous faciliter la tâche, cela signifie qu'en dessous, il fait des tonnes et des tonnes de travail pour vous assurer d'avoir une vie confortable en tant que développeur. Toutes les fonctionnalités «magiques» de Laravel qui semblent fonctionner ont des couches sur des couches de code qui doivent être fouettées chaque fois qu'une fonctionnalité s'exécute. Même une simple exception trace la profondeur du terrier du lapin (remarquez où l'erreur commence, jusqu'au noyau principal):

    Pour ce qui semble être une erreur de compilation dans l'une des vues, il y a 18 appels de fonction à tracer. J'en ai personnellement rencontré 40, et il pourrait facilement y en avoir plus si vous utilisez d'autres bibliothèques et plugins.

    Le fait que, par défaut, ces couches sur couches de code ralentissent Laravel.

    Quelle est la lenteur de Laravel?

    Honnêtement, il est tout simplement impossible de répondre à cette question pour plusieurs raisons.

    Prénom, il n'y a pas de norme acceptée, objective et raisonnable pour mesurer la vitesse des applications Web. Plus rapide ou plus lent par rapport à quoi? Sous quelles conditions?

    DEUXIEMENT, une application Web dépend de tellement de choses (base de données, système de fichiers, réseau, cache, etc.) qu'il est tout simplement ridicule de parler de vitesse. Une application Web très rapide avec une base de données très lente est une application Web très lente. 🙂

    Mais cette incertitude est précisément la raison pour laquelle les benchmarks sont populaires. Même s'ils ne veulent rien dire (voir précise et précise), ils fournissent certains cadre de référence et nous aider à ne pas devenir fou. Par conséquent, avec plusieurs pincées de sel prêtes, ayons une idée fausse et approximative de la vitesse parmi frameworks PHP.

    Passer par ce GitHub plutôt respectable la source, voici comment les frameworks PHP s'alignent par rapport à:

    Vous ne remarquerez peut-être même pas Laravel ici (même si vous louchez très fort) à moins que vous ne jetiez votre cas jusqu'au bout de la queue. Oui, chers amis, Laravel vient en dernier! Maintenant, d'accord, la plupart de ces «frameworks» ne sont pas très pratiques ou même utiles, mais cela nous dit à quel point Laravel est lent par rapport à d'autres plus populaires.

    Normalement, cette «lenteur» ne figure pas dans les applications car nos applications Web quotidiennes atteignent rarement des chiffres élevés. Mais une fois qu'ils le font (par exemple, plus de 200 à 500 concurrences), les serveurs commencent à s'étouffer et à mourir. C'est le moment où même le fait de lancer plus de matériel sur le problème ne le résout pas, et les factures d'infrastructure grimpent si vite que vos idéaux élevés de cloud computing s'effondrent.

    Mais bon, remontez le moral! Cet article ne traite pas de ce qui ne peut pas être fait, mais de ce qui peut être fait. 🙂

    La bonne nouvelle est que vous pouvez faire beaucoup pour rendre votre application Laravel plus rapide. Plusieurs fois vite. Oui, sans blague. Vous pouvez rendre la même base de code balistique et économiser plusieurs centaines de dollars sur les factures d'infrastructure / d'hébergement chaque mois. Comment? Allons-y.

    Quatre types d'optimisations

    À mon avis, l'optimisation peut se faire à quatre niveaux distincts (en ce qui concerne les applications PHP, c'est-à-dire):

    • Niveau de langue: Cela signifie que vous utilisez une version plus rapide du langage et évitez les fonctionnalités / styles de codage spécifiques dans le langage qui ralentit votre code.
    • Au niveau du cadre: Ce sont les choses que nous allons couvrir dans cet article.
    • Niveau infrastructure: Réglage de votre gestionnaire de processus PHP, serveur Web, base de données, etc.
    • Niveau matériel: Passer à un matériel meilleur, plus rapide et plus puissant fournisseur d'hébergement.

    Tous ces types d'optimisations ont leur place (par exemple, Optimisation PHP-fpm est assez critique et puissant). Mais l'objet de cet article sera les optimisations purement de type 2: celles liées au framework.

    Soit dit en passant, il n'y a aucune justification derrière la numérotation et ce n'est pas une norme acceptée. Je viens de les inventer. S'il vous plaît, ne me citez jamais et dites: «Nous avons besoin d'une optimisation de type 3 sur notre serveur», sinon votre chef d'équipe vous tuera, me trouvera et me tuera aussi. 😀

    Et maintenant, enfin, nous arrivons à la terre promise.

    Soyez conscient des requêtes de base de données n + 1

    Le problème de requête n + 1 est courant lorsque les ORM sont utilisés. Laravel a son puissant ORM appelé Eloquent, qui est si beau, si pratique que nous oublions souvent de regarder ce qui se passe.

    Prenons un scénario très courant: afficher la liste de toutes les commandes passées par une liste de clients donnée. Ceci est assez courant dans les systèmes de commerce électronique et dans toutes les interfaces de reporting en général où nous devons afficher toutes les entités liées à certaines entités.

    Dans Laravel, nous pourrions imaginer une fonction de contrôleur qui fait le travail comme ceci:

    class OrdersController extends Controller 
    {
        // ... 
    
        public function getAllByCustomers(Request $request, array $ids) {
            $customers = Customer::findMany($ids);        
            $orders = collect(); // new collection
            
            foreach ($customers as $customer) {
                $orders = $orders->merge($customer->orders);
            }
            
            return view('admin.reports.orders', ['orders' => $orders]);
        }
    }

    Doux! Et plus important encore, élégant, beau. 🤩🤩

    Malheureusement, c'est un catastrophique façon d'écrire du code dans Laravel.

    Voici pourquoi.

    Lorsque nous demandons à l'ORM de rechercher les clients donnés, une requête SQL comme celle-ci est générée:

    SELECT * FROM customers WHERE id IN (22, 45, 34, . . .);

    Ce qui est exactement comme prévu. Par conséquent, toutes les lignes renvoyées sont stockées dans la collection $customers à l'intérieur de la fonction de contrôleur.

    Maintenant, nous bouclons sur chaque client un par un et obtenons leurs commandes. Cela exécute la requête suivante. . .

    SELECT * FROM orders WHERE customer_id = 22;

    . . . autant de fois qu'il y a de clients.

    En d'autres termes, si nous devons obtenir les données de commande pour 1000 clients, le nombre total de requêtes de base de données exécutées sera 1 (pour récupérer toutes les données des clients) + 1000 (pour récupérer les données de commande pour chaque client) = 1001. Ce est d'où vient le nom n + 1.

    Pouvons-nous faire mieux? Certainement! En utilisant ce que l'on appelle le chargement hâtif, nous pouvons forcer l'ORM à effectuer un JOIN et renvoyer toutes les données nécessaires dans une seule requête! Comme ça:

    $orders = Customer::findMany($ids)->with('orders')->get();

    La structure de données résultante est imbriquée, bien sûr, mais les données de commande peuvent être facilement extraites. La requête unique qui en résulte, dans ce cas, est quelque chose comme ceci:

    SELECT * FROM customers INNER JOIN orders ON customers.id = orders.customer_id WHERE customers.id IN (22, 45, . . .);

    Une seule requête vaut bien sûr mieux que mille requêtes supplémentaires. Imaginez ce qui se passerait s'il y avait 10,000 XNUMX clients à traiter! Ou Dieu nous en préserve si nous voulions également afficher les éléments contenus dans chaque commande! N'oubliez pas que le nom de la technique est un chargement impatient, et c'est presque toujours une bonne idée.

    Cachez la configuration!

    L'une des raisons de la flexibilité de Laravel est la multitude de fichiers de configuration qui font partie du framework. Vous voulez changer comment / où les images sont stockées?

    Eh bien, changez simplement le config/filesystems.php fichier (au moins au moment de l'écriture). Vous souhaitez travailler avec plusieurs pilotes de file d'attente? N'hésitez pas à les décrire en config/queue.php. J'ai juste compté et trouvé qu'il y avait 13 fichiers de configuration pour différents aspects du framework, vous assurant que vous ne serez pas déçu, peu importe ce que vous voulez changer.

    Compte tenu de la nature de PHP, chaque fois qu'une nouvelle requête Web arrive, Laravel se réveille, démarre tout et analyse tous de ces fichiers de configuration pour savoir comment faire les choses différemment cette fois. Sauf que c'est stupide si rien n'a changé ces derniers jours! Reconstruire la configuration à chaque demande est un gaspillage qui peut (en fait, doit être) évité, et la solution est une simple commande que Laravel propose:

    php artisan config:cache

    Cela permet de combiner tous les fichiers de configuration disponibles en un seul et le cache est quelque part pour une récupération rapide. La prochaine fois qu'il y aura une requête Web, Laravel lira simplement ce fichier unique et se lancera.

    Cela dit, la mise en cache de configuration est une opération extrêmement délicate qui peut vous exploser au visage. Le plus gros problème est qu'une fois que vous avez émis cette commande, le env() les appels de fonction de partout sauf les fichiers de configuration retourneront null!

    Cela a du sens quand on y pense. Si vous utilisez la mise en cache de configuration, vous dites au cadre: «Vous savez quoi, je pense que j'ai bien configuré les choses et je suis sûr à 100% que je ne veux pas qu'ils changent.» En d'autres termes, vous vous attendez à ce que l'environnement reste statique, ce qui .env les fichiers sont pour.

    Cela dit, voici quelques règles de mise en cache de configuration, vêtues de fer, sacrées et incassables:

    1. Faites-le uniquement sur un système de production.
    2. Ne le faites que si vous êtes vraiment, vraiment sûr de vouloir geler la configuration.
    3. En cas de problème, annulez le réglage avec php artisan cache:clear
    4. Priez pour que les dommages causés à l'entreprise ne soient pas importants!

    Réduisez les services chargés automatiquement

    Pour être utile, Laravel propose une tonne de services à son réveil. Ceux-ci sont disponibles dans le config/app.php dans le cadre du 'providers' clé de tableau. Jetons un coup d'œil à ce que j'ai dans mon cas:

    /*
        |--------------------------------------------------------------------------
        | Autoloaded Service Providers
        |--------------------------------------------------------------------------
        |
        | The service providers listed here will be automatically loaded on the
        | request to your application. Feel free to add your own services to
        | this array to grant expanded functionality to your applications.
        |
        */
    
        'providers' => [
    
            /*
             * Laravel Framework Service Providers...
             */
            Illuminate\Auth\AuthServiceProvider::class,
            Illuminate\Broadcasting\BroadcastServiceProvider::class,
            Illuminate\Bus\BusServiceProvider::class,
            Illuminate\Cache\CacheServiceProvider::class,
            Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
            Illuminate\Cookie\CookieServiceProvider::class,
            Illuminate\Database\DatabaseServiceProvider::class,
            Illuminate\Encryption\EncryptionServiceProvider::class,
            Illuminate\Filesystem\FilesystemServiceProvider::class,
            Illuminate\Foundation\Providers\FoundationServiceProvider::class,
            Illuminate\Hashing\HashServiceProvider::class,
            Illuminate\Mail\MailServiceProvider::class,
            Illuminate\Notifications\NotificationServiceProvider::class,
            Illuminate\Pagination\PaginationServiceProvider::class,
            Illuminate\Pipeline\PipelineServiceProvider::class,
            Illuminate\Queue\QueueServiceProvider::class,
            Illuminate\Redis\RedisServiceProvider::class,
            Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
            Illuminate\Session\SessionServiceProvider::class,
            Illuminate\Translation\TranslationServiceProvider::class,
            Illuminate\Validation\ValidationServiceProvider::class,
            Illuminate\View\ViewServiceProvider::class,
    
            /*
             * Package Service Providers...
             */
    
            /*
             * Application Service Providers...
             */
            App\Providers\AppServiceProvider::class,
            App\Providers\AuthServiceProvider::class,
            // App\Providers\BroadcastServiceProvider::class,
            App\Providers\EventServiceProvider::class,
            App\Providers\RouteServiceProvider::class,
    
        ],

    Encore une fois, j'ai compté, et il y a 27 services répertoriés! Maintenant, vous pouvez avoir besoin de tous, mais c'est peu probable.

    Par exemple, je suis en train de créer une API REST pour le moment, ce qui signifie que je n'ai pas besoin du fournisseur de services de session, du fournisseur de services d'affichage, etc. Et puisque je fais quelques choses à ma façon et ne suit pas les valeurs par défaut du framework , Je peux également désactiver le fournisseur de services d'authentification, le fournisseur de services de pagination, le fournisseur de services de traduction, etc. Dans l'ensemble, près de la moitié d'entre eux sont inutiles pour mon cas d'utilisation.

    Examinez longuement votre demande. At-il besoin de tous ces fournisseurs de services? Mais pour l'amour de Dieu, ne commentez pas aveuglément ces services et passez à la production! Exécutez tous les tests, vérifiez les choses manuellement sur les machines de développement et de préparation, et soyez très paranoïaque avant d'appuyer sur la gâchette. 🙂

    Soyez sage avec les piles de middleware

    Lorsque vous avez besoin d'un traitement personnalisé de la requête Web entrante, la création d'un nouveau middleware est la réponse. Maintenant, c'est tentant d'ouvrir app/Http/Kernel.php et collez le middleware dans le web or api empiler; de cette façon, il devient disponible dans toute l'application et s'il ne fait pas quelque chose d'intrusif (comme la journalisation ou la notification, par exemple).

    Cependant, à mesure que l'application se développe, cette collection d'intergiciels mondiaux peut devenir un fardeau silencieux pour l'application si tous (ou la majorité) d'entre eux sont présents dans chaque demande, même s'il n'y a aucune raison commerciale à cela.

    En d'autres termes, faites attention à l'endroit où vous ajoutez / appliquez un nouveau middleware. Il peut être plus pratique d'ajouter quelque chose de manière globale, mais la pénalité de performance est très élevée à long terme. Je connais la douleur que vous auriez à subir si vous deviez appliquer de manière sélective un middleware à chaque fois qu'il y a un nouveau changement, mais c'est une douleur que je prendrais volontiers et recommanderais!

    Évitez l'ORM (parfois)

    Bien qu'Eloquent rend de nombreux aspects de l'interaction avec la base de données agréables, cela se fait au détriment de la vitesse. En tant que mappeur, l'ORM doit non seulement récupérer les enregistrements de la base de données, mais également instancier les objets du modèle et les hydrater (les remplir) avec des données de colonne.

    Donc, si vous faites un simple $users = User::all() et il y a, par exemple, 10,000 10,000 utilisateurs, le framework va extraire 10,000 XNUMX lignes de la base de données et en faire XNUMX XNUMX en interne new User() et remplissez leurs propriétés avec les données pertinentes. Il s'agit d'une énorme quantité de travail effectuée dans les coulisses, et si la base de données est l'endroit où votre application devient un goulot d'étranglement, contourner l'ORM est parfois une bonne idée.

    Cela est particulièrement vrai pour les requêtes SQL complexes, où vous devrez sauter beaucoup de cerceaux et écrire des fermetures lors de fermetures et finir avec une requête efficace. Dans de tels cas, faire un DB::raw() et il est préférable d'écrire la requête à la main.

    En passant précise étude des performances, même pour de simples inserts Eloquent est beaucoup plus lent à mesure que le nombre d'enregistrements augmente:

    Utilisez la mise en cache autant que possible

    L'un des secrets les mieux gardés de l'optimisation des applications Web est la mise en cache.

    Pour les non-initiés, la mise en cache signifie précalculer et stocker des résultats coûteux (coûteux en termes d'utilisation du processeur et de la mémoire), et simplement les renvoyer lorsque la même requête est répétée.

    Par exemple, dans un magasin de commerce électronique, il pourrait rencontrer celui des 2 millions de produits, la plupart du temps les gens sont intéressés par ceux qui sont fraîchement approvisionnés, dans une certaine fourchette de prix et pour une tranche d'âge particulière. Interroger la base de données pour ces informations est un gaspillage - puisque la requête ne change pas souvent, il est préférable de stocker ces résultats dans un endroit auquel nous pouvons accéder rapidement.

    Laravel a un support intégré pour plusieurs types de la mise en cache. En plus d'utiliser un pilote de mise en cache et de créer le système de mise en cache à partir de zéro, vous souhaiterez peut-être utiliser certains packages Laravel qui facilitent mise en cache du modèle, mise en cache des requêtes, Etc.

    Mais notez qu'au-delà d'un certain cas d'utilisation simplifié, les packages de mise en cache préconstruits peuvent causer plus de problèmes qu'ils n'en résolvent.

    Préférez la mise en cache en mémoire

    Lorsque vous mettez quelque chose en cache dans Laravel, vous avez plusieurs options pour stocker le calcul résultant qui doit être mis en cache. Ces options sont également appelées pilotes de cache. Ainsi, s'il est possible et parfaitement raisonnable d'utiliser le système de fichiers pour stocker les résultats du cache, ce n'est pas vraiment ce que la mise en cache est censée être.

    Idéalement, vous souhaitez utiliser un cache en mémoire (vivant entièrement dans la RAM) comme Redis, Memcached, MongoDB, etc., de sorte que sous des charges plus élevées, la mise en cache sert une utilisation vitale plutôt que de devenir un goulot d'étranglement en soi.

    Maintenant, vous pourriez penser qu'avoir un disque SSD est presque la même chose que d'utiliser une clé RAM, mais ce n'est même pas proche. Même informel repères montrent que la RAM surpasse le SSD de 10 à 20 fois en termes de vitesse.

    Mon système préféré en matière de mise en cache est Redis. Ses ridiculement rapide (100,000 XNUMX opérations de lecture par seconde sont courantes), et pour les systèmes de cache très volumineux, peuvent évoluer en grappe facilement.

    Cachez les itinéraires

    Tout comme la configuration de l'application, les routes ne changent pas beaucoup avec le temps et sont un candidat idéal pour la mise en cache. Cela est particulièrement vrai si vous ne pouvez pas supporter de gros fichiers comme moi et que vous finissez par diviser votre web.php et api.php sur plusieurs fichiers. Une seule commande Laravel emballe toutes les routes disponibles et les garde à portée de main pour un accès futur:

    php artisan route:cache

    Et lorsque vous finissez par ajouter ou modifier des itinéraires, faites simplement:

    php artisan route:clear

    Optimisation d'image et CDN

    Les images sont le cœur et l'âme de la plupart des applications Web. Par coïncidence, ils sont également les plus gros consommateurs de bande passante et l'une des principales raisons de la lenteur des applications / sites Web. Si vous stockez simplement les images téléchargées naïvement sur le serveur et que vous les renvoyez dans des réponses HTTP, vous laissez passer une énorme opportunité d'optimisation.

    Ma première recommandation est de ne pas stocker d'images localement - il y a le problème de la perte de données à traiter, et selon la région géographique dans laquelle se trouve votre client, le transfert de données peut être extrêmement lent.

    Au lieu de cela, optez pour une solution comme Cloudinary qui redimensionne et optimise automatiquement les images à la volée.

    Si ce n'est pas possible, utilisez quelque chose comme Cloudflare pour mettre en cache et diffuser des images pendant qu'elles sont stockées sur votre serveur.

    Et si même cela n'est pas possible, peaufiner un peu votre logiciel de serveur Web pour compresser les actifs et diriger le navigateur du visiteur vers la mise en cache des choses fait toute la différence. Voici à quoi ressemblerait un extrait de configuration de Nginx:

    server {
    
       # file truncated
        
        # gzip compression settings
        gzip on;
        gzip_comp_level 5;
        gzip_min_length 256;
        gzip_proxied any;
        gzip_vary on;
    
       # browser cache control
       location ~* \.(ico|css|js|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {
             expires 1d;
             access_log off;
             add_header Pragma public;
             add_header Cache-Control "public, max-age=86400";
        }
    }

    Je suis conscient que l'optimisation d'image n'a rien à voir avec Laravel, mais c'est une astuce si simple et puissante (et si souvent négligée) qui ne pouvait pas m'en empêcher.

    Optimisation de l'autochargeur

    Le chargement automatique est une fonctionnalité intéressante et pas si ancienne de PHP qui a sans doute sauvé la langue de la catastrophe. Cela dit, le processus de recherche et de chargement de la classe appropriée en déchiffrant une chaîne d'espace de noms donnée prend du temps et peut être évité dans les déploiements de production où des performances élevées sont souhaitables. Encore une fois, Laravel a une solution à commande unique à cela:

    composer install --optimize-autoloader --no-dev

    Faites-vous des amis avec des files d'attente

    Queues sont la façon dont vous traitez les choses quand il y en a beaucoup, et que chacune d'elles prend quelques millisecondes. Un bon exemple est l'envoi d'e-mails - un cas d'utilisation répandu dans les applications Web consiste à envoyer quelques e-mails de notification lorsqu'un utilisateur effectue certaines actions.

    Par exemple, dans un produit nouvellement lancé, vous voudrez peut-être que la direction de l'entreprise (environ 6 à 7 adresses e-mail) soit notifiée chaque fois que quelqu'un passe une commande supérieure à une certaine valeur. En supposant que votre passerelle de messagerie puisse répondre à votre demande SMTP en 500 ms, nous parlons d'une bonne attente de 3 à 4 secondes pour l'utilisateur avant que la confirmation de commande ne démarre. Un très mauvais morceau d'UX, je suis sûr que vous se mettre d'accord.

    Le remède consiste à stocker les travaux au fur et à mesure qu'ils arrivent, à dire à l'utilisateur que tout s'est bien passé et à les traiter (quelques secondes) plus tard. En cas d'erreur, les travaux en file d'attente peuvent être réessayés plusieurs fois avant d'être déclarés comme ayant échoué.

    Crédits: Microsoft.com

    Bien qu'un système de mise en file d'attente complique un peu la configuration (et ajoute une surcharge de surveillance), il est indispensable dans une application Web moderne.

    Optimisation des actifs (Laravel Mix)

    Pour tous les actifs frontaux de votre application Laravel, veuillez vous assurer qu'il existe un pipeline qui compile et réduit tous les fichiers d'actifs. Ceux qui sont à l'aise avec un système de bundler comme Webpack, Gulp, Parcel, etc., n'ont pas besoin de s'inquiéter, mais si vous ne le faites pas déjà, Mélange Laravel est une recommandation solide.

    Mix est un wrapper léger (et délicieux, en toute honnêteté!) Autour de Webpack qui prend en charge tous vos fichiers CSS, SASS, JS, etc. pour la production. Un typique .mix.js Le fichier peut être aussi petit que cela et fonctionne toujours à merveille:

    const mix = require('laravel-mix');
    
    mix.js('resources/js/app.js', 'public/js')
        .sass('resources/sass/app.scss', 'public/css');

    Cela prend automatiquement en charge les importations, la minification, l'optimisation et l'ensemble du shebang lorsque vous êtes prêt pour la production et l'exécution npm run production. Mix prend en charge non seulement les fichiers JS et CSS traditionnels, mais également les composants Vue et React que vous pourriez avoir dans le flux de travail de votre application.

    Plus d'infos ici!

    Conclusion

    L'optimisation des performances est plus un art que de la science - savoir comment et combien faire est important que ce qu'il faut faire. Cela dit, il n'y a pas de limite à ce que vous pouvez optimiser dans une application Laravel.

    Mais quoi que vous fassiez, j'aimerais vous laisser avec quelques conseils de départ - l'optimisation doit être effectuée lorsqu'il y a une raison solide, et non parce que cela sonne bien ou parce que vous êtes paranoïaque à propos des performances de l'application pour plus de 100,000 utilisateurs alors qu'en réalité il n'y en a que 10.

    Si vous ne savez pas si vous devez optimiser votre application ou non, vous n'avez pas besoin de jeter le pied dans le nid de frelons proverbiale. Une application de travail qui semble ennuyeuse mais qui fait exactement ce qu'elle doit est dix fois plus souhaitable qu'une application qui a été optimisée en une supermachine hybride mutante mais qui tombe à plat de temps en temps.

    Et, pour que newbiew devienne un maître Laravel, consultez ceci Cours en ligne.

    Que vos applications fonctionnent beaucoup, beaucoup plus rapidement! 🙂