Laravel verder pushen - beste tips en goede praktijken voor Laravel 5.7

Dit bericht heeft een audioversie dankzij de Blogcast-app van Miguel Piedrafita.

Laravel is al bij veel PHP-ontwikkelaars bekend voor het schrijven van schone, werkende en foutopsporende code. Het biedt ook ondersteuning voor veel functies, die soms niet in de documenten worden vermeld, of ze waren, maar ze zijn om verschillende redenen verwijderd.

Ik werk al twee jaar met Laravel in productie-gebruik en heb geleerd van het schrijven van slechte code naar betere code en ik heb het voordeel van Laravel gebruikt sinds de eerste keer dat ik ermee begon te schrijven. Ik ga je de mysterieuze trucs laten zien die je kunnen helpen bij het schrijven van code met Laravel.

Gebruik lokale scopes wanneer u dingen moet ondervragen

Laravel heeft een leuke manier om met Query Builder query's te schrijven voor uw database-stuurprogramma. Iets zoals dit:

$ orders = Bestellen :: waar ('status', 'afgeleverd') -> waar ('betaald', waar) -> get ();

Dit is best leuk. Dit zorgde ervoor dat ik de SQL opgaf en me concentreerde op codering die voor mij toegankelijker is. Maar dit stukje code kan beter worden geschreven als we lokale scopes gebruiken.

Met lokale scopes kunnen we onze eigen Query Builder-methoden maken die we kunnen koppelen als we proberen gegevens op te halen. In plaats van -> where () statements kunnen we bijvoorbeeld -> deliver () en -> betaald () op een schonere manier gebruiken.

Allereerst moeten we in ons bestelmodel enkele methoden toevoegen:

klasse Order breidt Model uit
{
   ...
   public function scopeDelivered ($ query) {
      retourneer $ query-> where ('status', 'afgeleverd');
   }
   public function scopePaid ($ query) {
      retourneer $ query-> where ('betaald', true);
   }
}

Wanneer u lokale bereiken aangeeft, moet u de exacte naamgeving van het bereik [Iets] gebruiken. Op deze manier weet Laravel dat dit een scope is en zal het hiervan gebruikmaken in uw Query Builder. Zorg ervoor dat u het eerste argument opneemt dat automatisch door Laravel wordt geïnjecteerd en dat de instantie voor het maken van zoekopdrachten is.

$ orders = Bestellen :: geleverd () -> betaald () -> get ();

Voor meer dynamisch ophalen kunt u dynamische lokale scopes gebruiken. Met elke scope kunt u parameters opgeven.

klasse Order breidt Model uit
{
   ...
   public function scopeStatus ($ query, string $ status) {
      retourneer $ query-> where ('status', $ status);
   }
}
$ orders = Order :: status ('geleverd') -> betaald () -> get ();

Verderop in dit artikel leert u waarom u snake_case moet gebruiken voor databasevelden, maar hier is de eerste reden: Laravel gebruikt standaard waar [Something] om de eerdere scope te vervangen. Dus in plaats van de vorige kunt u het volgende doen:

Bestellen :: whereStatus ( 'geleverd') -> betaald () -> get ();

Laravel zal zoeken naar de snake_case-versie van Something van waar [Something]. Als u een status in uw DB hebt, gebruikt u het vorige voorbeeld. Als u shipping_status heeft, kunt u het volgende gebruiken:

Bestellen :: whereShippingStatus ( 'geleverd') -> betaald () -> get ();

Het is jouw keuze!

Gebruik aanvraagbestanden indien nodig

Laravel biedt u een welsprekende manier om uw formulieren te valideren. Of het nu een POST-verzoek of een GET-verzoek is, het zal niet nalaten het te valideren als u het nodig hebt.

U kunt op deze manier valideren in uw controller:

openbare functieopslag (verzoek $ aanvragen)
{
    $ validatedData = $ request-> validate ([
        'title' => 'verplicht | uniek: berichten | max: 255',
        'body' => 'verplicht',
    ]);

    // Het blogbericht is geldig ...
}

Maar wanneer je te veel code in je controller-methoden hebt, kan het behoorlijk smerig zijn. U wilt zoveel mogelijk code in uw controller verminderen. Tenminste, dit is het eerste wat ik denk als ik echt veel logica moet schrijven.

Laravel biedt je een * leuke * manier om aanvragen te valideren door aanvraagklassen te maken en ze te gebruiken in plaats van de ouderwetse aanvraagklasse. U hoeft alleen uw verzoek aan te maken:

php ambachtelijke make: verzoek StoreBlogPost

In de app / Http / Verzoeken / map vindt u uw aanvraagbestand:

class StoreBlogPostRequest breidt FormRequest uit
{
   openbare functie autoriseren ()
   {
      retourneer $ this-> user () -> can ('create.posts');
   }
   openbare functie regels ()
   {
       terug [
         'title' => 'verplicht | uniek: berichten | max: 255',
         'body' => 'verplicht',
       ];
   }
}

Nu moet u in plaats van uw Illuminate \ Http \ Request in uw methode vervangen door de nieuw gemaakte klasse:

gebruik App \ Http \ Requests \ StoreBlogPostRequest;
openbare functie store (StoreBlogPostRequest $ request)
{
    // Het blogbericht is geldig ...
}

De methode autorize () moet een boolean zijn. Als het onwaar is, geeft het een 403, dus zorg ervoor dat je het ophaalt in de methode render / (uitzonderingen / Handler.php):

publieke functie renderen ($ request, Exception $ exception)
{
   if ($ exception instanceof \ Illuminate \ Auth \ Access \ AuthorizationException) {
      //
   }
   retour ouder :: render ($ request, $ exception);
}

De ontbrekende methode hier, in de verzoekklasse, is de functie messages (), dat is een array met de berichten die worden geretourneerd als de validatie mislukt:

class StoreBlogPostRequest breidt FormRequest uit
{
   openbare functie autoriseren ()
   {
      retourneer $ this-> user () -> can ('create.posts');
   }
   openbare functie regels ()
   {
       terug [
         'title' => 'verplicht | uniek: berichten | max: 255',
         'body' => 'verplicht',
       ];
   }
   openbare functieberichten ()
   {
      terug [
        'title.required' => 'De titel is verplicht.',
        'title.unique' => 'De titel van het bericht bestaat al.',
        ...
      ];
   }
}

Om ze in je controller te vangen, kun je de error-variabele in je blade-bestanden gebruiken:

@if ($ errors-> any ())
   @foreach ($ errors-> all () as $ error)
      {{$ error}}
   @endforeach
@stop als

Als u het validatiebericht van een specifiek veld wilt ontvangen, kunt u het als volgt doen (het retourneert een vals-booleaanse entiteit als de validatie voor dat veld is geslaagd):


@if ($ errors-> has ('title'))
   
@stop als

Magische scopes

Wanneer je dingen bouwt, kun je de magische scopes gebruiken die al zijn ingebed

  • Haal de resultaten op door create_at, aflopend:
Gebruikers :: laatste () -> get ();
  • Haal de resultaten op via een willekeurig veld, aflopend:
Gebruikers :: laatste ( 'last_login_at') -> te krijgen ();
  • Resultaten ophalen in willekeurige volgorde:
Gebruikers :: inRandomOrder () -> te krijgen ();
  • Voer een querymethode alleen uit als er iets waar is:
// Laten we aannemen dat de gebruiker op de nieuwspagina is en deze eerst op nieuwste wil sorteren
// mydomain.com/news?sort=new
Gebruiker :: wanneer ($ request-> query ('sort'), functie ($ query, $ sort) {
   if ($ sort == 'new') {
      retourneer $ query-> last ();
   }
   
   retourneer $ query;
}) -> te krijgen ();

In plaats van wanneer () kunt u tenzij gebruiken, dat is het tegenovergestelde van wanneer ().

Gebruik relaties om grote (of slecht geschreven) vragen te voorkomen

Heb je ooit een heleboel joins in een query gebruikt om meer informatie te krijgen? Het is vrij moeilijk om die SQL-opdrachten te schrijven, zelfs met Query Builder, maar modellen doen dat al met Relaties. Misschien ben je er in eerste instantie niet bekend mee, vanwege de grote hoeveelheid informatie die de documentatie biedt, maar dit zal je helpen beter te begrijpen hoe de dingen werken en hoe je je applicatie soepeler kunt laten verlopen.

Bekijk hier de documentatie van Relaties.

Gebruik Jobs voor tijdrovende taken

Laravel Jobs is een krachtig hulpmiddel om taken op de achtergrond uit te voeren.

  • Wil je een e-mail sturen? Jobs.
  • Wilt u een bericht uitzenden? Jobs.
  • Wilt u afbeeldingen verwerken? Jobs.

Jobs helpen u bij het opgeven van laadtijd voor uw gebruikers bij dergelijke tijdrovende taken. Ze kunnen in benoemde wachtrijen worden geplaatst, ze kunnen prioriteit krijgen, en raad eens - Laravel implementeerde wachtrijen bijna overal waar mogelijk was: ofwel wat PHP op de achtergrond verwerken of berichten verzenden of evenementen uitzenden, wachtrijen zijn er!

U kunt de documentatie van Wachtrijen hier raadplegen.

Ik ben dol op het gebruik van Laravel Horizon voor wachtrijen, omdat het eenvoudig is in te stellen, het kan worden gedemonstreerd met Supervisor en via het configuratiebestand kan ik Horizon vertellen hoeveel processen ik voor elke wachtrij wil.

Houd u aan databasestandaarden & Accessors

Laravel leert je vanaf het begin dat je variabelen en methoden $ camelCase camelCase () moeten zijn, terwijl je databasevelden snake_case moeten zijn. Waarom? Omdat dit ons helpt betere accessors te bouwen.

Accessors zijn aangepaste velden die we rechtstreeks vanuit ons model kunnen bouwen. Als onze database voornaam, achternaam en leeftijd bevat, kunnen we een aangepast veld met de naam toevoegen dat de voornaam en achternaam samenvoegt. Maak je geen zorgen, dit zal op geen enkele manier in de DB worden geschreven. Het is gewoon een aangepast kenmerk dat dit specifieke model heeft. Alle accessors, zoals scopes, hebben een aangepaste naamgevingssyntaxis: getSomethingAttribute:

klasse Gebruiker breidt Model uit
{
   ...
   openbare functie getNameAttribute (): string
   {
       geef $ this-> first_name terug. ' ' $ This-> last_name.;
   }
}

Wanneer u $ user-> name gebruikt, wordt de aaneenschakeling geretourneerd.

Standaard wordt het kenmerk name niet weergegeven als we dd ($ user), maar we kunnen dit algemeen beschikbaar maken met de variabele $ appends:

klasse Gebruiker breidt Model uit
{
   beveiligde $ appends = [
      'naam',
   ];
   ...
   openbare functie getNameAttribute (): string
   {
       geef $ this-> first_name terug. ' ' $ This-> last_name.;
   }
}

Nu zullen we elke keer dat we dd ($ user) zien dat de variabele er is (maar nog steeds, deze is niet aanwezig in de database)

Wees echter voorzichtig: als u al een naamveld hebt, zijn de dingen een beetje anders: de naam binnen $ voegt is niet langer nodig en de attribuutfunctie wacht op één parameter, de reeds opgeslagen variabele (we zullen niet langer gebruik $ this).

Voor hetzelfde voorbeeld willen we misschien de namen ucfirst () gebruiken:

klasse Gebruiker breidt Model uit
{
   beveiligde $ appends = [
      //
   ];
   ...
   openbare functie getFirstNameAttribute ($ firstName): string
   {
       retourneer ucfirst ($ firstName);
   }
   openbare functie getLastNameAttribute ($ lastName): string
   {
      retourneer ucfirst ($ lastName);
   }
}

Wanneer we nu $ user-> first_name gebruiken, retourneert deze een hoofdletter-eerste string.

Vanwege deze functie is het goed om snake_case te gebruiken voor uw databasevelden.

Bewaar modelgerelateerde statische gegevens niet in configs

Wat ik graag doe, is modelgerelateerde statische gegevens in het model opslaan. Ik zal het je laten zien.

In plaats van dit:

BettingOdds.php

klasse BettingOdds breidt Model uit
{
   ...
}

config / bettingOdds.php

terug [
   'sports' => [
      'voetbal' => 'sport: 1',
      'tennis' => 'sport: 2',
      'basketball' => 'sport: 3',
      ...
   ],
];

En toegang krijgen tot hen met:

config (bettingOdds.sports.soccer);

Ik doe dit het liefst:

BettingOdds.php

klasse BettingOdds breidt Model uit
{
   beschermde statische $ sports = [
      'voetbal' => 'sport: 1',
      'tennis' => 'sport: 2',
      'basketball' => 'sport: 3',
      ...
   ];
}

En open ze met behulp van:

BettingOdds :: $ sport [ 'voetbal'];

Waarom? Omdat het gemakkelijker is om te worden gebruikt bij verdere bewerkingen:

klasse BettingOdds breidt Model uit
{
   beschermde statische $ sports = [
      'voetbal' => 'sport: 1',
      'tennis' => 'sport: 2',
      'basketball' => 'sport: 3',
      ...
   ];
   public function scopeSport ($ query, string $ sport)
   {
      if (! isset (self :: $ sports [$ sport])) {
         retourneer $ query;
      }
      
      retourneer $ query-> where ('sport_id', self :: $ sports [$ sport]);
   }
}

Nu kunnen we genieten van scopes:

BettingOdds :: sport ( 'soccer') -> te krijgen ();

Gebruik collecties in plaats van raw-array-verwerking

Vroeger waren we gewend om op een rauwe manier met arrays te werken:

$ fruits = ['appel', 'peer', 'banaan', 'aardbei'];
foreach ($ fruits as $ fruit) {
   echo 'Ik heb'. $ Fruit;
}

Nu kunnen we geavanceerde methoden gebruiken die ons helpen de gegevens in arrays te verwerken. We kunnen gegevens binnen een array filteren, transformeren, itereren en wijzigen:

$ fruits = collect ($ fruits);
$ fruits = $ fruits-> reject (functie ($ fruit) {
   retourneer $ fruit === 'appel';
}) -> toArray ();
['peer', 'banaan', 'aardbei']

Raadpleeg de uitgebreide documentatie over collecties voor meer informatie.

Wanneer u met Querybouwers werkt, retourneert de methode -> get () een instantie Collection. Maar pas op dat u Collection niet verwart met een Query-builder:

  • In de Query Builder hebben we geen gegevens opgehaald. We hebben veel query-gerelateerde methoden: orderBy (), where (), etc.
  • Nadat we op -> get () hebben gedrukt, worden de gegevens opgehaald, is het geheugen verbruikt en wordt een Collection-instantie geretourneerd. Sommige Query Builder-methoden zijn niet beschikbaar of zijn dat wel, maar de naam is anders. Controleer de beschikbare methoden voor meer informatie.

Als u gegevens op het niveau van Query Builder kunt filteren, doet u dat! Vertrouw niet op filteren als het gaat om de Collection-instantie - u gebruikt op sommige plaatsen te veel geheugen en dat wilt u niet. Beperk uw resultaten en gebruik indexen op DB-niveau.

Gebruik pakketten en vind het wiel niet opnieuw uit

Hier zijn enkele pakketten die ik gebruik:

  • Laravel-mesrichtlijnen
  • Laravel CORS (bescherm uw routes tegen andere oorsprong)
  • Laravel Tag Helper (beter gebruik van HTML-tags in Blade)
  • Laravel Sluggable (handig als het gaat om het genereren van naaktslakken)
  • Laravel-responder (eenvoudiger een JSON-API bouwen)
  • Beeldinterventie (afbeeldingen in stijl verwerken)
  • Horizon (bewaken van wachtrijen met minimale configuratie)
  • Socialite (minimale configuratie om in te loggen met sociale media)
  • Paspoort (OAuth-implementatie voor routes)
  • Spatie's ActivityLog (activiteit volgen voor modellen)
  • Spatie's Backup (back-upbestanden en databases)
  • Spatie's Blade-X (definieer uw eigen HTML-tags; werkt goed met Laravel Tag Helper)
  • Spatie's mediabibliotheek (eenvoudige manier om bestanden aan modellen toe te voegen)
  • Spatie’s Response Cache (reacties van de cache-controller)
  • Spatie’s Collectiemacro's (meer macro's over collecties)

Hier zijn enkele pakketten die ik heb geschreven:

  • Bevriend (leuk, volgen en blokkeren zoals in sociale media)
  • Schema (maak tijdschema's en vergelijk deze met uren en dagen)
  • Beoordeling (tariefmodellen)
  • Guardian (machtigingensysteem, de gemakkelijke manier)

Te moeilijk om te begrijpen? Bereik me!

Als je meer vragen hebt over Laravel, als je hulp nodig hebt met informatie over DevOps of gewoon een bedankje wilt zeggen !, kun je me vinden op Twitter @rennokki!