Table of Contents
Le marketing moderne exige des campagnes de messagerie qui s'adaptent rapidement aux segments d'audience, aux tests A/B et au contenu dynamique. Une implémentation monolithique rigide devient rapidement un cauchemar de maintenance au fur et à mesure que les exigences évoluent. Laravel, avec sa syntaxe expressive et son écosystème robuste, fournit une base idéale, mais la véritable clé de la flexibilité à long terme réside dans le choix du modèle de conception approprié.
Dans ce guide complet, vous apprendrez à concevoir un système de campagne électronique flexible à Laravel en utilisant le modèle Builder. Nous allons décomposer la théorie, marcher à travers une implémentation détaillée, et explorer l'intégration avec les systèmes de diffusion et de file d'attente Laravel. D'ici la fin, vous aurez une approche prête à la production pour générer toute variante de campagne électronique – des messages transactionnels en texte simple aux promotions HTML riches – avec un minimum de duplication de code.
Quel est le modèle de constructeur?
Le modèle de construction est un modèle de conception qui sépare la construction d'un objet complexe de sa représentation finale. Au lieu de créer un objet par l'intermédiaire d'un constructeur massif ou d'un ensemble de méthodes d'usine, vous délèguez le processus de construction à un directeur et à des classes de constructeurs dédiés. Le directeur orchestre les étapes, tandis que chaque constructeur sait assembler les composants pour une variante spécifique.
Ce modèle brille lorsqu'un objet nécessite de nombreuses parties optionnelles, a plusieurs étapes de configuration, ou lorsque vous devez produire différentes représentations d'objets similaires. Pour les campagnes de courriel, le --object est un message de courriel complet avec sujet, corps, pièces jointes, destinataires, en-têtes et métadonnées. Chaque type de campagne (promotionnel, transactionnel, déclencheur d'événement) peut partager une base commune mais différer dans la mise en page, l'information de l'expéditeur ou les blocs de contenu.
Comment cela diffère-t-il du modèle d'usine
Alors que le modèle Factory se concentre sur la création d'objets en un seul appel, le modèle Builder permet un processus de construction contrôlé et fragmentaire. Les usines sont idéales lorsque la logique de création est simple; les constructeurs sont meilleurs lorsque vous devez contrôler l'ordre et la sélection des pièces. Dans les systèmes de messagerie, vous devez souvent ajouter des pièces jointes sous condition, varier le modèle de corps ou définir différentes priorités de livraison. Le modèle Builder vous donne ce contrôle granulaire sans ballonner une classe d'usine.
Mettre en place votre environnement larave
Avant de plonger dans le code, assurez-vous d'avoir une application Laravel (version 9 ou ultérieure) avec la configuration par défaut du courrier. Nous supposons que vous avez les tables de base de données nécessaires pour les campagnes, les abonnés et les modèles.
composer create-project laravel/laravel email-campaign-builder
Ensuite, configurez votre pilote de messagerie dans (p. ex., pour les tests). Nous utiliserons également Laravel=»s et les classes intégrées plus tard, mais notre constructeur restera indépendant d'eux pour maintenir une séparation nette.
Composantes de base du modèle de constructeur
Nous mettrons en oeuvre quatre éléments de base :
- Produit – L'objet final de l'email (pourrait être un objet , un objet de valeur, ou une extension de Laravel=s .
- Biulder Interface – Déclare les méthodes pour chaque partie du courriel : sujet, corps, destinataires, pièces jointes, en-têtes, etc.
- Concrete Builders[ – Chaque interface implémente un type de courriel spécifique (Promotionnel, Transactionnel, Welcome series).
- Directeur – Orchestra les étapes de construction dans un ordre défini, utilisant souvent le même constructeur pour produire plusieurs courriels à partir d'un plan.
Étape 1: Définir le produit de messagerie
Nous allons créer un objet de valeur simple pour contenir toutes les données de courriel. Cela maintient notre code de constructeur propre et testable.
<?php
namespace App\Values;
class EmailMessage
{
public string $subject;
public string $body;
public string $mimeType = 'text/html'; // or text/plain
public array $recipients = [];
public array $ccRecipients = [];
public array $bccRecipients = [];
public array $attachments = [];
public array $headers = [];
public ?string $fromAddress = null;
public ?string $fromName = null;
public function toArray(): array
{
return [
'subject' => $this->subject,
'body' => $this->body,
'mimeType' => $this->mimeType,
'recipients' => $this->recipients,
'cc' => $this->ccRecipients,
'bcc' => $this->bccRecipients,
'attachments' => $this->attachments,
'headers' => $this->headers,
'from' => ['address' => $this->fromAddress, 'name' => $this->fromName],
];
}
}
Étape 2: Interface du constructeur
L'interface définit le contrat pour construire n'importe quelle variante de courriel.
<?php
namespace App\Builders\Contracts;
use App\Values\EmailMessage;
interface EmailBuilderContract
{
public function setSubject(string $subject): self;
public function setBody(string $body, string $mimeType = 'text/html'): self;
public function addRecipient(string $email, ?string $name = null): self;
public function addCc(string $email, ?string $name = null): self;
public function addBcc(string $email, ?string $name = null): self;
public function addAttachment(string $filePath, ?string $name = null): self;
public function addHeader(string $key, string $value): self;
public function setFrom(string $address, ?string $name = null): self;
public function getEmail(): EmailMessage;
public function reset(): void;
}
Étape 3: Constructeur de béton pour les courriels promotionnels
Let , implémente un constructeur qui adapte le courriel pour les promotions – adding tracking pixels, social share liens, et un pied de page de désabonnement standard.
<?php
namespace App\Builders;
use App\Builders\Contracts\EmailBuilderContract;
use App\Values\EmailMessage;
class PromotionalEmailBuilder implements EmailBuilderContract
{
private EmailMessage $email;
public function __construct()
{
$this->reset();
}
public function setSubject(string $subject): self
{
$this->email->subject = '[Promo] ' . $subject;
return $this;
}
public function setBody(string $body, string $mimeType = 'text/html'): self
{
// Wrap body with promotional header/footer
$this->email->body = $this->wrapBody($body);
$this->email->mimeType = $mimeType;
return $this;
}
private function wrapBody(string $body): string
{
return "<div style=\"background:#f5f5f5; padding:20px;\">
<div style=\"max-width:600px; margin:auto;\">
$body
<hr>
<p style=\"font-size:12px; color:#888;\">
You received this because you opted in.
<a href=\"{{unsubscribe_url}}\">Unsubscribe</a>
</p>
</div>
</div>";
}
public function addRecipient(string $email, ?string $name = null): self
{
$this->email->recipients[] = compact('email', 'name');
return $this;
}
public function addCc(string $email, ?string $name = null): self
{
$this->email->ccRecipients[] = compact('email', 'name');
return $this;
}
public function addBcc(string $email, ?string $name = null): self
{
$this->email->bccRecipients[] = compact('email', 'name');
return $this;
}
public function addAttachment(string $filePath, ?string $name = null): self
{
$this->email->attachments[] = ['path' => $filePath, 'name' => $name];
return $this;
}
public function addHeader(string $key, string $value): self
{
$this->email->headers[$key] = $value;
return $this;
}
public function setFrom(string $address, ?string $name = null): self
{
$this->email->fromAddress = $address;
$this->email->fromName = $name;
return $this;
}
public function getEmail(): EmailMessage
{
$built = clone $this->email;
$this->reset();
return $built;
}
public function reset(): void
{
$this->email = new EmailMessage();
}
}
Étape 4: Exemple de constructeur transactionnel
Un courriel transactionnel (par exemple, confirmation de commande) nécessite un emballage différent – une marque minimale, des en-têtes de priorité et aucun pied de page de désabonnement.
<?php
namespace App\Builders;
class TransactionalEmailBuilder implements EmailBuilderContract
{
private EmailMessage $email;
// ... same structure, but setSubject does not prepend prefix
// and wrapBody() uses a simple layout with order details
// getEmail() resets the builder
}
Le directeur : Orchestrating the Build
Le directeur prend une instance de constructeur et appelle ses étapes dans un ordre spécifique. C'est là que vous pouvez définir des séquences standard, comme -construire un email de campagne pour un abonné donné.
<?php
namespace App\Builders;
use App\Builders\Contracts\EmailBuilderContract;
use App\Models\User;
class CampaignDirector
{
public function __construct(private EmailBuilderContract $builder) {}
public function buildPromotionalCampaign(User $user, string $subject, string $body): EmailMessage
{
return $this->builder
->setFrom('[email protected]', 'Marketing Team')
->setSubject($subject)
->addRecipient($user->email, $user->name)
->addHeader('X-Campaign-Id', $campaignId)
->setBody($body)
->getEmail();
}
public function buildTransactionalOrderConfirmation(Order $order): EmailMessage
{
// Switch builder if needed, or use a different director method
// (In practice, you'd instantiate a TransactionalEmailBuilder)
$this->builder = new TransactionalEmailBuilder();
$body = view('emails.order-confirmation', compact('order'))->render();
return $this->builder
->setFrom('[email protected]', 'Order System')
->setSubject('Order Confirmation #' . $order->id)
->addRecipient($order->user->email, $order->user->name)
->setBody($body)
->addHeader('X-Transaction-Id', $order->transaction_id)
->addAttachment(storage_path('invoices/' . $order->invoice_file))
->getEmail();
}
}
Si vous devez ajouter une méthode , vous n'avez qu'à étendre le directeur sans toucher les constructeurs.
Intégration avec Laravel Mail
Une fois que vous avez un objet valeur, vous devez l'envoyer. Créez un Mailable personnalisé qui accepte le et le rend en utilisant le système de courrier intégré Laravel.
<?php
namespace App\Mail;
use App\Values\EmailMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CampaignMail extends Mailable
{
use Queueable, SerializesModels;
public function __construct(public EmailMessage $emailMessage) {}
public function envelope(): Envelope
{
return new Envelope(
from: $this->emailMessage->fromAddress
? new Address($this->emailMessage->fromAddress, $this->emailMessage->fromName)
: null,
subject: $this->emailMessage->subject,
cc: $this->emailMessage->ccRecipients,
bcc: $this->emailMessage->bccRecipients,
headers: $this->emailMessage->headers,
);
}
public function content(): Content
{
return new Content(
htmlString: $this->emailMessage->body,
);
}
public function attachments(): array
{
return array_map(function ($attach) {
return Attachment::fromPath($attach['path'])
->as($attach['name'] ?? null);
}, $this->emailMessage->attachments);
}
}
Maintenant, vous pouvez envoyer des courriels de n'importe quel contrôleur ou travail en utilisant le constructeur:
use App\Builders\PromotionalEmailBuilder;
use App\Builders\CampaignDirector;
use App\Mail\CampaignMail;
use Illuminate\Support\Facades\Mail;
$builder = new PromotionalEmailBuilder();
$director = new CampaignDirector($builder);
$emailMessage = $director->buildPromotionalCampaign($user, 'Summer Sale', $htmlContent);
Mail::to($user->email)->send(new CampaignMail($emailMessage));
Tirer parti de la file d'attente pour la scalabilité
Les systèmes de campagne par courriel doivent gérer des milliers de destinataires asynchrones. Le système de file d'attente Laravel est un match parfait.
<?php
namespace App\Jobs;
use App\Builders\Contracts\EmailBuilderContract;
use App\Builders\CampaignDirector;
use App\Mail\CampaignMail;
use App\Models\Subscriber;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class SendCampaignEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
private Subscriber $subscriber,
private string $subject,
private string $body,
private string $builderClass // class-string
) {}
public function handle(): void
{
$builder = app($this->builderClass);
$director = new CampaignDirector($builder);
$emailMessage = $director->buildPromotionalCampaign(
$this->subscriber->user,
$this->subject,
$this->body
);
Mail::to($this->subscriber->email)
->send(new CampaignMail($emailMessage));
}
}
Rédiger le travail pour chaque abonné dans une boucle (ou mieux, utiliser pour gérer les envois réussis/échecs).
Ajout de modèles dynamiques avec lame
Le codage dur HTML dans les constructeurs n'est pas idéal. Au lieu de cela, passez rendu vues Blade comme le corps. Votre constructeur peut accepter un nom de vue et tableau de données, puis appeler à l'intérieur . Cela maintient vos modèles séparés et faciles à modifier pour les concepteurs.
public function setBody(string $viewName, array $data = [], string $mimeType = 'text/html'): self
{
$rendered = view($viewName, $data)->render();
$this->email->body = $this->wrapBody($rendered);
$this->email->mimeType = $mimeType;
return $this;
}
Maintenant vous pouvez appeler .
Avantages dans les campagnes du monde réel
- Variété sans duplication – Différents types de campagnes partagent la même interface ; les constructeurs encapsulent les différences.
- Testabilité[ – Vous pouvez tester chaque constructeur en récupérant le et en affirmant ses propriétés.
- Essais A/B plus faciles – Constructeurs d'échange par groupe de variante. Le processus de construction du directeur demeure inchangé.
- Trails d'audit – Ajouter la logarithme dans le constructeur pour enregistrer chaque étape pour une analyse ultérieure.
- Intégration avec des services externes – Utilisez le constructeur pour assembler des charges utiles pour des services comme Mailgun, SendGrid ou SparkPost.
Meilleures pratiques et pièges communs
Gardez les constructeurs apatrides là où c'est possible
La méthode permet de réutiliser un constructeur. Si vous oubliez d'appeler , la même instance de constructeur peut s'échapper de l'état entre différentes campagnes. Dans notre exemple, appelle automatiquement – un modèle sûr.
Don Üt Over-Engineer pour les courriels simples
Si votre application n'envoie qu'un seul type de courriel, le modèle de constructeur pourrait être surqualifié. Il brille lorsque vous avez au moins trois types de courriels distincts avec des composants différents.
Utiliser l'injection de dépendance pour les constructeurs
Enregistrez vos constructeurs dans le conteneur de service afin que vous puissiez injecter facilement des dépendances (comme des services de loging ou de suivi).
Mettons en garde le nombre de bénéficiaires
Le constructeur ne devrait pas collecter des milliers de destinataires dans un seul – qui chargerait des gigaoctets en mémoire. Au lieu de cela, créer un courriel par destinataire, ou utiliser des API par lots (Mailgun=s ).
Autres améliorations
Envisager d'ajouter un – un modèle Eloquent qui stocke la classe de constructeur, le modèle et les paramètres par défaut. Ensuite, un emploi de planificateur lit des plans et utilise le directeur pour construire des courriels de file d'attente de & pour tous les abonnés actifs.
Vous pouvez également introduire un qui applique des règles globales (comme toujours ajouter un lien de désabonnement) avant de déléguer au constructeur spécifique. Cela ajoute une autre couche de séparation.
Ressources extérieures
- Builder Pattern – Guru refactoring (excellente explication visuelle)
- Laravel Mail Documentation[ (pour la référence de classe de courrier)
- Laravel Queues (pour l'envoi d'un courriel à échelle)
Conclusion
Avec le modèle de builder à Laravel, vous avez un contrôle précis sur la construction de votre email, ce qui rend votre base de codes adaptable à l'évolution des besoins marketing. En séparant le builder de la façon (directeur), vous permet aux équipes d'ajouter de nouveaux types de campagnes sans craindre de casser ceux existants. Combinez ceci avec le courrier Laravel et l'infrastructure de file d'attente, et vous avez une solution évolutive, testable et durable qui servira vos campagnes pendant des années à venir.