de Niklas Schöllhorn

Îndepărtându-mă de magie – sau: de ce nu mai vreau să folosesc Laravel

Este timpul pentru o schimbare a instrumentelor pe care le folosesc. Și îți voi spune de ce!

În primul rând, vreau să mă asigur că știți despre intențiile mele. Nu încerc să discut despre Laravel sau de ce alte cadre ar putea fi mai bune.

Acest articol este foarte subiectiv. Vă voi da gândurile mele și voi încerca să vă fac să vă regândiți și alegerile de cadru. Și când rămâi cu Laravel după reevaluare, este în regulă. Nu am intenția de a converti oamenii departe de Laravel în alte cadre sau limbi. Dar este important să te uiți mai atent și să te asiguri că știi ce pe care îl folosești și De ce îl folosești.

Indepartandu ma de magie sau de ce nu mai vreau
Înecat din cauza prea multor magii. Fotografie de Kristopher Roller pe Unsplash

Introducere

Am lucrat cu Laravel de aproximativ 2 ani acum. Mereu mi-a plăcut cât de ușor a fost să creez o aplicație și să încep în câteva minute. Oferă atât de multe instrumente utile din cutie. Comenzile consolei vă susțin în orice aspect în timpul codării. Acestea generează clase, schele pentru autentificare și multe altele.

Dar cu cât mergeți mai adânc și cu cât proiectele devin mai mari, cu atât dezvoltarea cu Laravel va deveni mai complicată. Sau, permiteți-mi să o reformulez: cu atât alte instrumente vor face mai bine treaba. Nici nu spun că este doar vina lui Laravel. Se datorează și parțial faptului că PHP nu este foarte bine conceput.

Acum, să începem!

ORM elocvent

Dacă ați lucrat deja cu Laravel, cu siguranță știți despre Elocvent. ORM-ul este livrat cu o instalare implicită. Acesta vine cu o mulțime de caracteristici îngrijite. Dar designul său face ca aplicația dvs. să fie inutil de complexă și împiedică IDE să vă analizeze corect codul.

Acest lucru se datorează parțial Active Record ORM model care este utilizat și parțial datorită faptului că Eloquent dorește să salveze dezvoltatorul de a fi nevoit să scrie mai mult cod. Pentru a face acest lucru, permite dezvoltatorului să introducă multe în modelul care nu aparține acolo.

Pare a fi intenții bune, dar am început să-mi placă din ce în ce mai mult.

Să aruncăm o privire la un exemplu:

Primul lucru pe care îl vedeți este că există fără proprietăți pe model. Acest lucru pare irelevant, dar pentru mine, face o mare diferență. Totul este injectat „magic” în clasă citind metadatele din tabel. Desigur, IDE-ul dvs. nu înțelege asta fără ajutor. Și nu aveți nicio șansă să vă denumiți proprietățile diferit de coloanele dvs.

Acum verificați metoda de aplicare. Pentru utilizatorii Laravel, este destul de clar ce face. Dacă apelați această metodă, aceasta vizează interogarea SQL subiacentă adăugând clauza WHERE dată.

Puteți vedea, nu este static. Asta ar însemna că această metodă operează pe un anumit obiect al clasei. Dar în acest caz, aceasta nu. Un scop este apelat la un constructor de interogări. Are nimic a face cu obiectul model în sine. Vă explic că după ce vedeți cum numiți de obicei aceste domenii:

Numiți o metodă statică popular() pe care nimeni nu l-a definit vreodată. Dar din moment ce Laravel definește a __call() și __callStatic() metoda, este tratată prin ele. Aceste metode redirecționează apelul către un constructor de interogări.

Acest lucru nu este doar ceva pe care IDE-ul dvs. nu îl înțelege. Îngreunează refactorizarea, ar putea deruta noii dezvoltatori și analiza statică devine și mai greu.

În plus, atunci când puneți astfel de metode pe modelul dvs., încălcați S de SOLID. În cazul în care nu sunteți familiarizați cu asta, SOLID este un acronim care înseamnă:

  • Sprincipiul responsabilității
  • Ostilou / Principiu închis
  • Liskov Principiul înlocuirii
  • Eunterface Principiul de segregare
  • Dprincipiul inversiunii ependenței

Când utilizați Elocvent, modelele dvs. au multiple responsabilități. Păstrează datele din baza de date, ceea ce fac de obicei modelele, dar are și logică de filtrare, poate de sortare și chiar mai mult în ea. Nu vrei asta.

Ajutoare globale

Laravel vine cu câteva funcții globale de asistență. Pare foarte la îndemână și da, ei sunteți la indemana.

Trebuie doar să știți că vă sacrificați independența pentru această manevrabilitate și spațiul dvs. de nume global se poluează. Rareori duce la conflicte, dar evitarea acestui lucru ar trebui preferată.

Să ne uităm la câteva exemple. Iată o listă cu trei metode de ajutor pe care le avem, dar pe care nu le avem nevoie, deoarece există alternative mai bune:

  • app_path() – De ce? Dacă aveți nevoie de calea aplicației, întrebați obiectul aplicației. O obțineți prin sugestie de tip.
  • app() – nu? Nu avem nevoie de această metodă. Putem injecta instanța aplicației!
  • collect() – Aceasta creează o nouă instanță a clasei Colecție. Putem doar să creăm un obiect de la noi înșine.

Un alt exemplu concret:

Folosim globalul Laravel request() ajutor pentru a prelua datele POST și a le pune în modelul nostru ca atribute.

În loc să folosim ajutorul global, am putea tasta hint a Request obiect ca parametru în metoda controlerului. Dispeceratul din Laravel știe cum să ne ofere obiectul necesar. Ne va apela metoda cu ea și nu trebuie să apelăm un ajutor.

Și putem face acest lucru cu un pas mai departe pentru a decupla și mai mult. Laravel este PSR-7 conform. Deci, în loc să tastați sugerarea obiectului Cerere, puteți să tastați și hint ServerRequestInterface. Acest lucru vă permite să înlocuiți întregul cadru cu orice este compatibil cu PSR-7. Totul din această metodă va continua să funcționeze. Acest lucru ar eșua dacă utilizați în continuare metodele de ajutor. Noul cadru nu ar veni cu metoda de ajutor și, prin urmare, va trebui să rescrieți acea parte a codului.

Rareori schimbi întregul cadru, dar sunt oameni care o fac. Și chiar dacă tu s-ar putea să nu se schimbe niciodată, este încă important pentru interoperabilitate. Abilitatea de a injecta dependențe și de a avea un flux de date concis în loc să rezolvați și să solicitați dependențe și date din interior este calea de urmat. Face mai ușor testarea, refactorizarea și aproape totul atunci când o înțelegi.

Am fost fericit când am citit asta cu Laravel 5.8 ajutoarele pentru șir și matrice sunt eliminate din nucleu și introduse într-un pachet separat. Acesta este un prim pas bun. Dar documentația ar trebui să înceapă să descurajeze utilizarea toate funcții de ajutor.

Fațade

Argumentele din ultima parte intră în joc și aici. Fatadele par a fi un instrument frumos pentru a accesa rapid unele metode care nu sunt chiar statice. Dar te leagă încă o dată în cadru. Le folosiți pentru a rezolva manual dependențele în loc să instruiți mediul să le furnizeze.

Același lucru este valabil și pentru complexitate, trecând totul prin metodele magice.

Din moment ce vorbeam despre suport IDE, știu că unii dintre voi m-ar putea direcționa către Pachet asistent IDE din barryvdh. Nu trebuie. Cunosc deja acest pachet. Dar de ce este chiar nevoie? Deoarece unele decizii de proiectare în Laravel nu sunt chiar bune. Există cadre în care nu aveți nevoie de asta. Luați Symfony de exemplu. Nu este nevoie de fișiere de asistență IDE, deoarece este bine conceput și implementat.

În loc de fațade, am putea folosi din nou injecția de dependență așa cum am făcut în exemplul anterior. Am avea un obiect real și am putea apela metode reale pe el. Mult mai bine.

Vă voi da încă o dată un exemplu:

Am putea curăța cu ușurință acest lucru. Să-i spunem lui Laravel să injecteze o ResponseFactory și transmiteți-ne solicitarea actuală:

Acum am eliminat cu succes utilizarea fațadelor din controlerul nostru. Codul încă arată curat și compact, dacă nu chiar mai bun decât înainte. Și întrucât controlorii noștri extind întotdeauna generalul Controller am putea face totul cu un pas mai departe mutând fabrica de răspuns la acea clasă părinte. Avem nevoie de el în toate celelalte clase de controlere oricum.

Am auzit că unii oameni oferă „prea mulți parametri de constructor” ca argument împotriva injectării totul. Dar nu sunt de acord cu asta. Ascunde doar dependențele și, prin urmare, complexitatea. Dacă nu vă place să aveți între 10 și 20 de argumente în constructorul dvs., aveți dreptate.

Soluția nu este totuși magică. Având nevoie de atâtea dependențe într-o singură clasă înseamnă că această clasă are cel mai probabil prea multe responsabilități. În loc să ascundeți această complexitate, refactorați acea clasă. Împărțiți-l în altele noi și îmbunătățiți-vă arhitectura aplicației.

Fapt amuzant: există un model de design real numit „model de fațadă”, introdus în cartea Gang of Four. Dar are un sens complet diferit. Fațadele lui Laravel sunt în esență localizatoare de servicii statice. Numirea nu transmite asta. Aceleași denumiri pentru diferite lucruri îngreunează, de asemenea, discuțiile despre arhitectură în proiecte, deoarece cealaltă parte s-ar putea aștepta la ceva complet diferit în spatele acestui nume.

Concluzie

Să ajungem la sfârșit. Aș putea scrie în curând o urmărire despre ce tehnologii prefer să folosesc în zilele noastre. Dar, pentru moment, permiteți-mi să rezum ce am învățat:

Abordarea lui Laravel de a face totul cât mai ușor posibil este bună. Dar este greu să te înțelegi atunci când aplicațiile tale devin mai avansate. Prefer asistența IDE minunată, tastarea mai puternică, obiectele reale și ingineria bună. S-ar putea chiar să mă întorc la Laravel când vreau să scriu o aplicație mai mică.

Multe dintre punctele mele nu sunt doar vina lui Laravel. Aș putea schimba piesele care nu-mi plac, de exemplu ORM. Dar, în schimb, voi schimba doar setul de instrumente, unde valorile implicite se potrivesc mai bine nevoilor mele. Nu văd nici un rost să folosesc un cadru în care trebuie să petrec mai mult timp evitând capcanele pe care le stabilește pentru o inginerie proastă decât în ​​dezvoltarea aplicației mele. Alte cadre și instrumente vin cu valori implicite mai bine concepute și mai puțină magie.

Deci, deocamdată, îmi voi lua rămas bun de la Laravel.

Multumesc pentru timpul acordat. Aș aprecia o discuție frumoasă în comentarii și, desigur, sunt deschis pentru întrebări și sugestii.

PS: Mulțumiri speciale lui Marco Pivetta pentru citirea probelor și contribuții suplimentare!

Editați 1 martie 2019:
De când articolul meu a fost postat pe Reddit, am creat un cont Reddit pentru a răspunde la câteva comentarii. Contul meu nu este cel din care a fost postat articolul, ci acesta: https://reddit.com/u/nschoellhorn

Editați pe 13 martie 2019:
Dacă citești până aici, poți să-l vezi și pe Profilul Twitter. Vă mulțumim pentru interesul dvs. continuu pentru acest subiect! Sunt întotdeauna deschis discuțiilor productive, așa că vă rugăm să nu ezitați să contactați, fie aici, fie pe Twitter.