Laravel 7 : mes trois outils préférés

March 11, 2020
Rédigé par

Laravel 7 : mes trois outils préférés

Laravel 7 est disponible ! Et comme pour chaque nouvelle sortie digne de ce nom, il est blindé de nouvelles caractéristiques intéressantes. Il est l’heure de jeter un œil à 3 de mes fonctionnalités préférées de cette version, et si on a de la chance, il y aura peut-être un bonus à la fin.

Pour pouvoir suivre ce tutoriel, vous aurez besoin de connaissances en PHP, ainsi qu’un peu de savoir-faire avec Laravel. Vous pouvez retrouver le code de cet article sur GitHub.

Cast personnalisé avec Eloquent

Eloquent est un mapping objet-relationnel. C’est un outil utile pour gérer la data entre notre base de données et notre code.

On pourrait déjà faire des conversions de type sensibles en utilisant Mutators de Eloquent. Par exemple : ajoutez n’importe quel champ pour la propriété $dates de notre modèle et il sera automatiquement converti en un date objet Carbon pour nous.

Custom Casts va encore un peu plus loin en nous permettant de découpler la représentation de la data dans l'application et dans la base de données. Souvent, nous aurons besoin de stocker notre data d'une certaine façon et de la récupérer dans l'application d'une autre. Custom Casts nous permet de convertir automatiquement la data au format base de donnée ou application au moment de la lecture et de l'écriture. Imaginons que nous un ayons une table de transactions décrit par cette migration :

database/migrations/create_transactions_table.php

<?php
 
Schema::create('transactions', function (Blueprint $table) {
    $table->uuid('uuid');
    $table->integer('amount_primary');
    $table->string('amount_currency');
    $table->integer('amount_secondary');
    $table->char('amount_separator');
    $table->char('amount_symbol');
    $table->date('date');
    $table->id();
    $table->timestamps();
});

Quelques infos sur le coût de la transaction sont stockées dans différents champs : les valeurs premières et secondaires, la devise du code,  séparateur et le symbole. Ce format convient à la base de données mais ça serait encore mieux si on interagissait avec le montant en devise dans notre code en utilisant un ValueObject Amount.

app/ValueObject/Amount.php

<?php
 
namespace App\ValueObject;
 
class Amount
{
    public int $primary;
    public int $secondary;
    public string $separator;
    public string $symbol;
    public string $currency;
 
    public function __toString(): string
    {
        return $this->symbol . $this->primary . $this->separator . $this->secondary;
    }
}

On peut voir comment notre get créé un nouveau ValueObject Amount et le remplit de data de la base donnée, pendant que set convertit les données de ce ValueObject en paire de clé/valeur prête pour la base de données.

Pour dire à Eloquent d’utiliser ce cast, on ajoute un nouveau tableau de propriété $casts à notre modèle Transaction, où la clé est le nom de la propriété et la valeur est le cast à utiliser.

app/Transaction.php

<?php
 
namespace App;
 
use App\Cast\AmountCast;
use Illuminate\Database\Eloquent\Model;
 
class Transaction extends Model
{
    protected $casts = [
        'amount' => AmountCast::class
    ];
}

On peut tester si ça fonctionne en créant une route simple :

routes/web.php

<?php
 
Route::get('/casts', function () {
    $transaction = Transaction::where('id', 1)->first();
    return (string)$transaction->amount;
});

Si on exécute /casts dans notre application, on verra que la méthode __toString de l’objet Amount s’exécute, ce qui prouve que notre ValueObject est créé par Eloquent.

Je préfère toujours utiliser des ValueObjects plutôt que des tableaux dans mes applications. Les objets sont beaucoup moins source d'erreur puisque nous avons la complétion de l'éditeur pour nous aider. En plus, il peut être rendu immuable, ce qui nous assure que l’état ne sera pas changé là où il ne devrait pas l’être. C’est un super ajout pour nous aider à avoir des applications mieux construites.

Blade components

Blade components nous permet de rendre des petites zones isolées de notre front-end avec des composants réutilisables. Ils ressemblent beaucoup aux composants d’un framework front-end comme React ou Vue mais utilisent seulement les moteurs de modèles Blade et sont entièrement côté serveur. J’apprécie ces composants pour créer du code réutilisable qui résout des problèmes récurrents.

Avec les applications Web, c’est commun d’avoir besoin de d’afficher les erreurs résultant des interactions utilisateur. Blade components est un excellent exemple de composants que nous aurons besoin de réutiliser sur notre front-end. Nous pouvions déjà le faire avant avec subviews mais la nouvelle architecture composant simplifie le processus et est plus alignée avec l'architecture actuelle des outils front-end

Nous pouvons créer un nouveau composant en utilisant la CLI d’Artisan depuis la racine de notre projet :

php artisan make:component Error

Blade components a besoin d’étendre la classe abstraite Component et devrait implémenter la méthode render.  Regardons un peu la classe de notre composant error.

app/View/Components/Error.php

 <?php

namespace App\View\Components;

use Illuminate\View\Component;
use Illuminate\View\View;

class Error extends Component
{

    public string $message;
 
    public function __construct(string $message)
    {
        $this->message = $message;
    }
 
    public function type(string $type = null): string
    {
        return $type ?? 'warning';
    }
 
    public function render(): View
    {
        return view('component.error');
    }
}

Il se passe beaucoup de choses ici, ça mérite de s’y attarder. N’importe quel argument constructeur est géré  de l’un des deux endroits. Dans notre exemple, nous attendons un paramètre string, alimenté par les props du template qui appelle ce composant; mais nous verrons cela plus tard. On peut aussi inclure les noms des classes comme arguments qui seront automatiquement remplis par le container.

On utilise la fonction globale view pour rendre notre template de sous-composant. N’importe quelles propriétés publiques ou méthodes de la classe composant sont automatiquement rendues disponibles dans tous les modèles de vue que vous appellerez.

resources/views/component/error.blade.php

<div class="alert alert-{{ $type() }}" {{ $attributes }} role="alert">
    {{ $message }}
</div>

The $attributes property is automatically passed down from the parent template, while the $type is a method of our component class that Blade has automatically made available. The $messages property came in as a prop from our calling HTML as a property:

resources/views/components.blade.php

La propriété $attributes est automatiquement transmise du template parent, tandis que le $type est une méthode de notre composant classe que Blade a automatiquement rendue disponible.  La propriété $messages est récupéré en tant que prop venant de notre appel HTML en tant que propriété :

resources/views/components.blade.php

<x-error message="There's been a big problem" class="ignored" />

La propriété message est passée dans le composant class comme un argument constructeur qui est paramétré comme une propriété publique disponible dans le rendu du composant. Pfiou.

Tout ce qui nous aide à retirer la duplication de notre code et nous permet d’éditer des choses à un seul endroit est considéré comme une victoire pour moi. J’utiliserai ça dans mon code pour effectuer les changements à un seul endroit.

Nouveau Wrapper de Client HTTP

Guzzle est un client HTTP formidable et peut faire n’importe quelle requête HTTP dont vous aurez besoin. Malheureusement, l’API qui le rend si puissant ne facilite pas la capacité à se souvenir comment faire la plus simple des requêtes. Laravel 7's HTTP client est un wrapper de commodité qui nous aide à faire les requêtes HTTP les plus communes de façon prévisible et simple à retenir.

<?php
 
Route::get('/http-client', function(){
    $response = Http::get('http://www.recipepuppy.com/api/?i=garlic,bread');
    return $response['results'][array_rand($response['results'])];
});

Faire une simple requête Get tient en une ligne, mais lorsqu’il s’agit de faire des choses plus complexes, ça devient encore plus facile. J’ai perdu le compte du nombre de fois où j’ai dû regarder le tableau des valeurs possibles pour le champ options dans le but d’envoyer les données d’un form post. En utilisant le nouveau Wrapper de Laravel 7, on peut directement appeler la méthode asForm puis ajouter les champs du formulaire comme seconds paramètres.

Le wrapper nous offre aussi des manières plus faciles pour trouver ce qu’il se passe avec les réponses de nos requêtes. A l’inverse de Guzzle, le Client L        aravel ne nous lève pas d' exceptions quand il rencontre des codes HTTP non-200. A la place, il rend le statut disponible à travers des méthodes d’aide comme successful ou ok. Si jamais on préfère, on peut réactiver le comportement de gestion des exceptions par défaut de Guzzle.

Le Client HTTP fournit des méthodes de mocking très utiles, afin que nous n’ayons pas besoin de faire des requêtes réelles lors de nos tests :

<?php
 
public function testHttpRequestToApiReturns200()
{
    Http::fake([
        'www.recipepuppy.com/*' => Http::response(['recipe'], 302),
    ]);
    self::assertEquals(302, Http::get('http://www.recipepuppy.com/api/?i=garlic,bread')->status());
}

BONUS : Nouvel Exécuteur de Tests
Laravel 7 possède aussi un nouvel exécuteur de test que l'on peut lancer avec la commande :

php artisan test

On peut apprécier les magnifiques nouveaux visuels et en plus le runner s’arrête par défaut à la première erreur.

Faites la mise à jour !

Il y a plein d’autres nouvelles features intéressantes dans Laravel 7, et ce n’est pas si dur de faire la mise à jour…! Alors j’espère que cet avant-gout vous aura donné envie de télécharger la nouvelle version. Dites-moi quels sont vos nouveaux outils préférés de Laravel 7 sur Twitter ou contactez-moi par mail si vous avez des questions !

  • Email: ghockin@twilio.com
  • Twitter: @GeeH