Laravel Tips Clean Code: Rendez votre code Laravel plus propre et plus sûr #1
Arthur Monney
@mckenziearts
Dans cet article (qui est le premier d'une longue série), je vais énumérer quelques tactiques que vous pouvez utiliser pour écrire du code plus propre. Au fur et à mesure que vous vous y habituerez, vous développerez un sens pour ce qui est un bon et un mauvais code.
Je vais également recommander quelques conseils généraux sur Laravel entre ces tactiques. Allons y 💪🏾 !
Lookup tables (Imbrication de conditions)
Au lieu d'écrire les conditions else if
répétitives, utilisez un tableau pour rechercher la valeur souhaitée en fonction de la clé que vous avez.
Le code sera plus propre et plus lisible et vous verrez des exceptions compréhensibles si quelque chose ne va pas. Non cas limites de demi-passages.
❌ Mauvais
if ($order->product->option->type === 'pdf') { $type = 'book';} else if ($order->product->option->type === 'epub') { $type = 'book';} else if ($order->product->option->type === 'license') { $type = 'license';} else if ($order->product->option->type === 'artwork') { $type = 'creative';} else if ($order->product->option->type === 'song') { $type = 'creative';} else if ($order->product->option->type === 'physical') { $type = 'physical';} if ($type === 'book') { $downloadable = true;} else if ($type === 'license') { $downloadable = true;} else if ($type === 'creative') { $downloadable = true;} else if ($type === 'physical') { $downloadable = false;}
✅ Correct
$type = [ 'pdf' => 'book', 'epub' => 'book', 'license' => 'license', 'artwork' => 'creative', 'song' => 'creative', 'physical' => 'physical',][$order->product->option->type]; $downloadable = [ 'book' => true, 'license' => true, 'creative' => true, 'physical' => false,][$type];
Early return (retour anticipé)
Essayez d'éviter les imbrications inutiles en renvoyant une valeur rapidement. Trop d'imbrications et d'instructions else
ont tendance à rendre le code plus difficile à lire.
❌ Mauvais
if ($notificationSent) { $notify = false;} else { if ($isActive) { if ($total > 100) { $notify = true; } else { $notify = false; } } else { if ($canceled) { $notify = true; } else { $notify = false; } }} return $notify;
✅ Correct
if ($notificationSent) { return false;} if ($isActive && $total > 100) {return true;} if (! $isActive && $canceled) { return true;} return false;
Diviser les lignes correctement
Ne coupez pas les lignes au hasard, mais ne les rendez pas trop longues non plus.
Ouvrir un tableau avec [
et indenter les valeurs a tendance à bien fonctionner. Il en va de même pour les valeurs longues des paramètres de fonction.
D'autres bons endroits pour couper les lignes sont les appels en chaîne et les fermetures.
❌ Mauvais
// No line splitreturn $this->request->session()->get($this->config->get('analytics.campaign_session_key')); // Meaningless line splitreturn $this->request ->session()->get($this->config->get('analytics.campaign_session_key'));
✅ Correct
return $this->request->session()->get( $this->config->get('analytics.campaign_session_key')); // Closurenew EventCollection($this->events->map(function (Event $event) { return new Entries\Event($event->code, $event->pivot->data);})) // Array$this->validate($request, [ 'code' => 'string|required', 'name' => 'string|required',]);
Ne pas créér de variables inutiles
Ne créez pas de variables lorsque vous pouvez simplement passer la valeur directement.
❌ Mauvais
public function create(){ $data = [ 'resource' => 'campaign', 'generatedCode' => Str::random(8), ]; return $this->inertia('Resource/Create', $data);}
✅ Correct
public function create(){ return $this->inertia('Resource/Create', [ 'resource' => 'campaign', 'generatedCode' => Str::random(8), ]);}
Créez des variables lorsqu'elles améliorent la lisibilité
Le contraire de l'astuce précédente. Parfois, la valeur provient d'un appel complexe et, à ce titre, la création d'une variable améliore la lisibilité et supprime la nécessité d'un commentaire. N'oubliez pas que le contexte est important et que votre objectif final est la lisibilité.
❌ Mauvais
Visit::create([ 'url' => $visit->url, 'referer' => $visit->referer, 'user_id' => $visit->userId, 'ip' => $visit->ip, 'timestamp' => $visit->timestamp,])->conversion_goals()->attach($conversionData);
✅ Correct
$visit = Visit::create([ 'url' => $visit->url, 'referer' => $visit->referer, 'user_id' => $visit->userId, 'ip' => $visit->ip, 'timestamp' => $visit->timestamp,]); $visit->conversion_goals()->attach($conversionData);
Créer des méthodes pour vos modèles pour la logique métier
Vos contrôleurs doivent être simples. Ils doivent dire des choses comme "créer une facture pour une commande". Ils ne doivent pas se préoccuper des détails de la structure de votre base de données.
Laissez cela au modèle.
❌ Mauvais
// Create invoice for orderDB::transaction(function () use ($order) { $invoice = $order->invoice()->create(); $order->pushStatus(new AwaitingShipping); return $invoice;});
✅ Correct
$order->createInvoice();
Créer des classes d'action
Développons l'exemple précédent. Parfois, la création d'une classe pour une seule action permet de faire le ménage.
Les modèles doivent encapsuler la logique métier qui leur est liée, mais ils ne doivent pas être trop volumineux.
❌ Mauvais
public function createInvoice(): Invoice{ if ($this->invoice()->exists()) { throw new OrderAlreadyHasAnInvoice('Order already has an invoice.'); } return DB::transaction(function () use ($order) { $invoice = $order->invoice()->create(); $order->pushStatus(new AwaitingShipping); return $invoice; });}
✅ Correct
// Order modelpublic function createInvoice(): Invoice{ if ($this->invoice()->exists()) { throw new OrderAlreadyHasAnInvoice('Order already has an invoice.'); } return app(CreateInvoiceForOrder::class)($this);} // Action classclass CreateInvoiceForOrder{ public function __invoke(Order $order): Invoice { return DB::transaction(function () use ($order) { $invoice = $order->invoice()->create(); $order->pushStatus(new AwaitingShipping); return $invoice; }); }}
Utiliser des FormRequest
C'est un endroit idéal pour cacher une logique de validation complexe.
Mais attention, c'est exactement ce qu'il faut faire : cacher des choses. Lorsque la logique de validation est simple, il n'y a rien de mal à la faire dans le contrôleur. La déplacer vers une demande de formulaire la rend moins explicite.
/*** Get the validation rules that apply to the request.** @return array*/public function rules(){ return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ];}
Utiliser les événements
Envisagez de décharger certaines logiques des contrôleurs vers les événements. Par exemple, lors de la création de modèles. L'avantage est que la création de ces modèles fonctionnera de la même manière partout (contrôleurs, tâches, ...) et le contrôleur a un souci de moins pour les détails du schéma de la base de données.
❌ Mauvais
// Only works in this place & concerns it with// details that the model should care about.if (! isset($data['name'])) { $data['name'] = $data['code'];} $conversion = Conversion::create($data);
✅ Correct
$conversion = Conversion::create($data); // Modelclass ConversionGoal extends Model{ public static function booted() { static::creating(function (self $model) { $model->name ??= $model->code; }); }}
Extraire des méthodes
Si une méthode est trop longue ou complexe et qu'il est difficile de comprendre ce qui se passe exactement, divisez la logique en plusieurs méthodes.
public function handle(Request $request, Closure $next){ // We extracted 3 long methods into separate methods. $this->trackVisitor(); $this->trackCampaign(); $this->trackTrafficSource($request); $response = $next($request); $this->analytics->log($request); return $response;}
Créer des fonctions d'aide (helper functions)
Si vous répétez souvent un code, demandez-vous si l'extraire vers une fonction d'aide ne rendrait pas le code plus propre.
// app/helpers.php, autoloaded in composer.jsonfunction money(int $amount, string $currency = null): Money{ return new Money($amount, $currency ?? config('shop.base_currency'));} function html2text($html = ''): string{ return str_replace(' ', ' ', strip_tags($html));}
Arthur Monney
@mckenziearts
Fullstack Designer - Laravel & React Developer. Laravel Cameroon Organizer @laravelcm | @shopperlabs