Allontanarsi dalla magia - o: perché non voglio più usare Laravel

È tempo di cambiare gli strumenti che utilizzo. E ti dirò perché!

Prima di tutto, voglio assicurarmi che tu sappia delle mie intenzioni. Non sto cercando di lamentarmi di Laravel o del motivo per cui altri framework potrebbero essere migliori.

Questo articolo è molto soggettivo. Ti darò i miei pensieri e cercherò di farti ripensare anche alle tue scelte di framework. E quando rimani con Laravel dopo la rivalutazione, va bene. Non ho intenzione di convertire le persone da Laravel ad altri framework o linguaggi. Ma è importante guardare più da vicino e assicurarti di sapere cosa stai usando e perché lo stai usando.

Intro

Lavoro con Laravel da circa 2 anni ormai. Mi è sempre piaciuto quanto fosse facile avviare un'applicazione e iniziare a utilizzarla in pochi minuti. Fornisce così tanti strumenti utili fuori dagli schemi. I comandi della console ti supportano in ogni aspetto durante la codifica. Generano classi, scaffolding per l'autenticazione e molto altro.

Ma più vai in profondità e più grandi diventano i progetti, più complicato sarà lo sviluppo con Laravel. Oppure lasciatemi riformulare: gli altri strumenti migliori faranno il lavoro. Non sto nemmeno dicendo che sia solo colpa di Laravel. In parte è anche dovuto al fatto che PHP non è progettato molto bene.

Adesso cominciamo!

Eloquente ORM

Se hai già lavorato con Laravel, sicuramente conosci Eloquent. È l'ORM che viene fornito con un'installazione predefinita. Viene fornito con molte caratteristiche interessanti. Ma il suo design rende la tua applicazione inutilmente complessa e impedisce all'IDE di analizzare correttamente il tuo codice.

Ciò è in parte dovuto al pattern ORM di Active Record utilizzato e in parte al fatto che Eloquent vuole evitare che lo sviluppatore debba scrivere altro codice. Per fare ciò, consente allo sviluppatore di inserire molto nel modello che non appartiene a lì.

Sembra una buona intenzione, ma ho iniziato a non piacermi sempre di più.

Diamo un'occhiata a un esempio:

La prima cosa che vedi è che non ci sono proprietà sul modello. Questo sembra irrilevante, ma per me fa una bella differenza. Tutto viene iniettato “magicamente” nella classe leggendo i metadati della tabella. Ovviamente, il tuo IDE non lo capisce senza aiuto. E non hai la possibilità di nominare le tue proprietà in modo diverso dalle tue colonne.

Ora controlla il metodo dell'ambito. Per gli utenti di Laravel, è abbastanza chiaro cosa fa. Se si chiama questo metodo, l'ambito della query SQL sottostante viene aggiunto alla clausola WHERE specificata.

Puoi vedere, non è statico. Ciò significherebbe che questo metodo opera su un oggetto specifico della classe. Ma in questo caso non è così . Un ambito viene chiamato in un generatore di query. Non ha nulla a che fare con l'oggetto modello stesso. Lo spiegherò dopo aver visto come di solito chiami quegli ambiti:

Stai chiamando un metodo statico popular()che nessuno ha mai definito. Ma poiché Laravel definisce un metodo __call()and __callStatic(), viene gestito attraverso di loro. Questi metodi inoltrano la chiamata a un generatore di query.

Questo non è solo qualcosa che il tuo IDE non capisce. Rende più difficile il refactoring, potrebbe confondere i nuovi sviluppatori e anche l'analisi statica diventa più difficile.

Inoltre, quando inserisci tali metodi sul tuo modello, stai violando la S di SOLID. Nel caso in cui tu non abbia familiarità con questo, SOLID è un acronimo che sta per:

  • S Responsabilità ingle Principio
  • O penna / principio di chiusura
  • Principio di sottomissione di L iskov
  • I nterface segregazione Principio
  • D ependency Inversion Principle

Quando utilizzi Eloquent, i tuoi modelli hanno molteplici responsabilità. Contiene i dati dal tuo database, che è ciò che di solito fanno i modelli, ma ha anche una logica di filtraggio, forse l'ordinamento e anche di più al suo interno. Non lo vuoi.

Aiutanti globali

Laravel viene fornito con alcune funzioni di supporto globali. Sembrano molto utili e sì, sono utili.

Devi solo sapere che sacrifichi la tua indipendenza per quella praticità e il tuo spazio dei nomi globale viene inquinato. Raramente porta a conflitti, ma è preferibile evitare tutto ciò.

Diamo un'occhiata ad alcuni esempi. Ecco un elenco di tre metodi di supporto che abbiamo ma di cui non abbiamo bisogno poiché esistono alternative migliori:

  • app_path()- perché? Se hai bisogno del percorso dell'app, chiedi all'oggetto app. Lo ottieni per tipo di suggerimento.
  • app()- eh? Non abbiamo bisogno di questo metodo. Possiamo iniettare l'istanza dell'app!
  • collect()- Questo crea una nuova istanza della classe Collection. Possiamo solo creare un oggetto da soli.

Un altro esempio concreto:

Stiamo usando l' request()helper globale di Laravel per recuperare i dati POST e inserirli nel nostro modello come attributi.

Invece di usare l'helper globale, potremmo digitare hint a Requestobject come parametro nel metodo del controller. Il dispatcher di Laravel sa come fornirci l'oggetto necessario. Chiamerà il nostro metodo con esso e non dobbiamo chiamare un helper.

E possiamo fare un ulteriore passo avanti per disaccoppiare ancora di più. Laravel è conforme a PSR-7. Quindi, invece di digitare hinting sull'oggetto Request, puoi anche digitare hint ServerRequestInterface. Ciò ti consente di sostituire l'intero framework con qualsiasi cosa che sia compatibile con PSR-7. Tutto in questo metodo continuerà a funzionare. Questo fallirebbe se stai ancora usando i metodi di supporto. Il nuovo framework non verrà fornito con il metodo helper e quindi dovresti riscrivere quella parte del tuo codice.

Raramente cambi l'intero framework, ma ci sono persone che lo fanno. E anche se si potrebbe mai cambiare, è ancora importante per l'interoperabilità. Essere in grado di iniettare dipendenze e avere un flusso di dati conciso invece di risolvere e richiedere dipendenze e dati al rovescio è la strada da percorrere. Rende i test, il refactoring e quasi tutto più facili quando ci si prende confidenza.

Sono stato felice quando ho letto che con Laravel 5.8 gli helper di stringa e array vengono rimossi dal core e inseriti in un pacchetto separato. Questo è un buon primo passo. Ma la documentazione dovrebbe iniziare a scoraggiare l'uso di tutte le funzioni di supporto.

Facciate

Anche qui entrano in gioco gli argomenti dell'ultima parte. Le facciate sembrano essere un ottimo strumento per accedere rapidamente ad alcuni metodi che non sono realmente statici. Ma ti legano ancora una volta alla struttura. Li usi per risolvere manualmente le dipendenze invece di istruire l'ambiente a fornirle.

Lo stesso vale per la complessità passando tutto attraverso i metodi magici.

Dato che stavamo parlando del supporto IDE, so che alcuni di voi potrebbero indirizzarmi al pacchetto helper IDE di barryvdh. Non ne hai bisogno. Conosco già questo pacchetto. Ma perché è anche necessario? Perché alcune decisioni di progettazione in Laravel non sono buone. Ci sono framework in cui non ne hai bisogno. Prendi Symfony per esempio. Non sono necessari file di supporto IDE, perché è ben progettato e implementato.

Invece delle facciate, potremmo usare di nuovo l'inserimento delle dipendenze come nell'esempio precedente. Avremmo un oggetto reale e potremmo chiamare metodi reali su di esso. Molto meglio.

Ti faccio ancora una volta un esempio:

Potremmo facilmente ripulirlo. Diciamo a Laravel di iniettare un ResponseFactorye passarci la richiesta corrente:

Ora abbiamo eliminato con successo l'uso delle facciate dal nostro controller. Il codice sembra ancora pulito e compatto, se non addirittura migliore di prima. E poiché i nostri controller estendono sempre la Controllerclasse generale , potremmo fare un ulteriore passo avanti spostando la factory di risposta in quella classe genitore. Ne abbiamo comunque bisogno in tutte le altre classi di controller.

Ho sentito che alcune persone forniscono "troppi parametri del costruttore" come argomento contro l'iniezione di tutto. Ma non sono d'accordo con quello. Nasconde solo le dipendenze e quindi la complessità in primo luogo. Se non ti piace avere da 10 a 20 argomenti nel tuo costruttore, hai ragione.

La soluzione però non è magica. Avere bisogno di tante dipendenze in una singola classe significa che questa classe molto probabilmente ha troppe responsabilità. Invece di nascondere quella complessità, effettua il refactoring di quella classe. Suddividilo in nuovi e migliora l'architettura dell'applicazione.

Curiosità: c'è un vero e proprio motivo di design chiamato "pattern di facciata", introdotto nel libro di Gang of Four. Ma ha un significato completamente diverso. Le facciate di Laravel sono essenzialmente localizzatori di servizi statici . La denominazione semplicemente non lo trasmette. La stessa denominazione per cose diverse rende anche più difficili le discussioni sull'architettura nei progetti, perché l'altra parte potrebbe aspettarsi qualcosa di completamente diverso dietro quel nome.

Conclusione

Veniamo alla fine. Potrei scrivere presto un seguito sulle tecnologie che preferisco usare al giorno d'oggi. Ma per il momento, lasciatemi riassumere ciò che abbiamo imparato:

L'approccio di Laravel per rendere tutto il più semplice possibile è buono. Ma è difficile andare d'accordo quando le tue app diventano più avanzate. Preferisco un fantastico supporto IDE, una digitazione più forte, oggetti reali e una buona progettazione. Potrei anche tornare a Laravel quando voglio scrivere un'app più piccola.

Molti dei miei punti non sono solo colpa di Laravel. Potrei scambiare le parti che non mi piacciono, ad esempio l'ORM. Invece, cambierò semplicemente il toolkit, dove i valori predefiniti si adattano meglio alle mie esigenze. Non vedo alcun motivo per utilizzare un framework in cui devo dedicare più tempo a evitare le trappole che crea per una cattiva ingegneria piuttosto che per sviluppare la mia app. Altri framework e strumenti sono dotati di impostazioni predefinite meglio progettate e meno magia.

Quindi per ora saluterò Laravel.

Grazie per il tuo tempo. Apprezzerei una bella discussione nei commenti e sono ovviamente aperto alle vostre domande e suggerimenti.

PS: un ringraziamento speciale a Marco Pivetta per la correzione di bozze e ulteriori input!

Modifica il 1 ° marzo 2019:

Poiché il mio articolo è stato pubblicato su Reddit, ho creato un account Reddit per rispondere ad alcuni commenti. Il mio account non è quello da cui è stato pubblicato l'articolo, ma questo: //reddit.com/u/nschoellhorn

Modifica 13 marzo 2019:

Se leggi fin qui, puoi anche controllare il mio profilo Twitter. Grazie per il tuo continuo interesse per questo argomento! Sono sempre aperto a discussioni produttive, quindi non esitare a contattarci, qui o su Twitter.