Laravel Astuces
6 min de lecture 279 vues

Laravel Tips Clean Code: Rendez votre code Laravel plus propre et plus sûr #1

mckenziearts

Arthur Monney

@mckenziearts

Laravel Tips Clean Code: Rendez votre code Laravel plus propre et plus sûr #1

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 split
return $this->request->session()->get($this->config->get('analytics.campaign_session_key'));
 
// Meaningless line split
return $this->request
->session()->get($this->config->get('analytics.campaign_session_key'));

✅ Correct

return $this->request->session()->get(
$this->config->get('analytics.campaign_session_key')
);
 
// Closure
new 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 order
DB::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 model
public function createInvoice(): Invoice
{
if ($this->invoice()->exists()) {
throw new OrderAlreadyHasAnInvoice('Order already has an invoice.');
}
 
return app(CreateInvoiceForOrder::class)($this);
}
 
// Action class
class 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);
 
// Model
class 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.json
function 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));
}
mckenziearts

Arthur Monney

@mckenziearts

Fullstack Designer - Laravel & React Developer. Laravel Cameroon Organizer @laravelcm | @shopperlabs

Vous aimez cet article ? Faite le savoir en partageant