amsom-habitat / mailer-sender
Utilities pour les envoies de mails depuis MailerInterface
Requires
- php: >=8.1
- ext-curl: *
- ext-fileinfo: *
- symfony/mailer: ^6.1 || ^7.0 || ^8.0
- symfony/twig-bridge: ^6.1 || ^7.0 || ^8.0
README
Connecteur PHP pour envoyer des mails via l'API mailing d'AMSOM Habitat. Le connecteur ne fabrique pas les emails : il transmet une description à l'API, qui se charge du rendu (templates MJML/EJS), de l'envoi SMTP, des tentatives de renvoi, du log et de la redirection prod/test.
Comment ça marche
Ton code PHP ──send([...])──▶ mailer-sender ──POST /v2/send (JSON + clé API)──▶ API mailing ──▶ SMTP
- Tu décris l'email avec un tableau associatif ; le connecteur le poste en JSON sur
POST <API_MAIL_URL>/v2/send, avec la clé API dans l'en-têteX-API-Key. - L'API valide, rend le template, envoie, journalise (consultable sur son interface
/emails) et renvoie un identifiant de suivi. - La logique prod/test (rediriger les mails vers une boite de dev hors prod) est gérée par l'API, plus par le connecteur.
Prérequis
- PHP ≥ 8.1
- Une API mailing en v4+ joignable (elle expose la route
/v2/send). - Une clé API valide (fournie par l'équipe qui opère l'API mailing).
Installation
composer require amsom-habitat/mailer-sender
Le service est autowiré par Symfony — injecte simplement AmsomUtilities\MailerSender dans ton contrôleur/service.
Configuration
Deux variables d'environnement suffisent :
API_MAIL_URL=http://api_mailing # base de l'API mailing (sans /v2)
API_MAIL_KEY=ma_cle_api # clé API — doit correspondre à une entrée
# de API_KEYS côté API mailing (ex. "metier:ma_cle_api")
Alternative : passer l'URL et la clé au constructeur (prioritaires sur l'env), utile pour les tests :
$mailer = new MailerSender($symfonyMailer, apiUrl: 'http://api_mailing', apiKey: 'ma_cle_api');
Utilisation — send()
Une seule méthode, un tableau. Ajouter une option = ajouter une clé (aucun changement de signature).
use AmsomUtilities\MailerSender;
// $mailer est injecté par Symfony (autowiring)
$res = $mailer->send([
// 'template' omis → 'default' (le template HTML tout-configurable)
'to' => 'locataire@example.com',
'subject' => 'Information',
'body' => ['message' => 'Bonjour, votre demande a été prise en compte.'],
]);
if ($res['success']) {
$idSuivi = $res['id']; // hash visible sur l'interface /emails de l'API
} else {
// $res['httpStatus'], $res['error']
}
Champs disponibles
💡 Découverte : le tableau
array{...}dans le docblock desend()→ PhpStorm autocomplète les clés (y comprisoptionsetdelivery). Liste à l'exécution :dump(\AmsomUtilities\MailerSender::champsDisponibles()); // clé => description, à date
| Clé | Type | Description |
|---|---|---|
template | string | Défaut default (template tout-configurable : message, message+bouton, texte brut, config libre). Sinon un template spécifique (enquete, edl, prestataire…) |
to | string | string[] | Requis. Destinataire : un email, ou un tableau d'emails |
subject | string | Requis. Objet du mail |
body | array | Données injectées dans le template |
text | string | Contenu texte brut (uniquement pour template: 'texte_brut') |
from | string | Expéditeur réel. Si fourni, l'envoi passe par le relay interne |
replyTo | string | Adresse de réponse (en-tête Reply-To), distincte de l'expéditeur |
cc / cci | string[] | Copies / copies cachées |
destinataire | string | Nom affiché du destinataire (utilisé par certains en-têtes de template) |
attachments | array[] | Pièces jointes (voir plus bas) |
options | array | Config de template libre (voir plus bas) |
delivery | array | Acheminement (voir plus bas) |
options — configuration de template
Toutes optionnelles. Interprétées par le template ; ajouter une nouvelle option ne casse rien.
| Clé | Type | Description |
|---|---|---|
logo | string | logoEspaceClient | logoSyneo | logoNexio |
bonjour | bool | Affiche « Bonjour … » |
signature | array | Bloc signature (nom, prenom, fonction, tel, mobile) ; remplace le footer réseaux sociaux |
auteur | string | Auteur en bas de page |
remerciement | string | Message de remerciement |
showRemerciement | bool | Affiche le remerciement |
showFooter | bool | Affiche le footer (mode custom) |
footerMiddlePart | string | Contenu médian du footer |
headerTemplateName / bodyTemplateName / footerTemplateName | string | Templates à utiliser (mode custom) |
delivery — acheminement
| Clé | Type | Défaut | Description |
|---|---|---|---|
mode | 'sync' | 'async' | sync | sync = attend l'envoi réel, success reflète le résultat. async = réponse immédiate (202), envoi + retries en tâche de fond côté API |
retries | int | 7 | Nombre de tentatives de renvoi |
delayMs | int | 1000 | Délai initial entre tentatives (× 3 à chaque échec) |
timeToKeep | int | 30 | Jours de conservation du log (-1 = illimité) |
testMode | string | "" | En prod, redirige vers la boite de dev et préfixe le sujet |
Par défaut l'envoi est synchrone :
send()attend le résultat etsuccessest fiable. Passedelivery.mode = 'async'seulement si tu veux du « fire-and-forget » (utile pour des envois en masse), en sachant quesuccessne reflétera alors que l'acceptation, pas la réussite d'envoi.
Pièces jointes
'attachments' => [
// contenu déjà en base64
['filename' => 'facture.pdf', 'content' => $base64],
// OU un fichier / une URL — le connecteur le convertit en base64 (l'API ne fait aucun fetch)
['filename' => 'photo.jpg', 'content' => '/tmp/photo.jpg', 'type' => 'path'],
['filename' => 'image', 'content' => 'https://…', 'type' => 'url'],
]
Valeur de retour
send() renvoie un tableau :
| Clé | Type | Description |
|---|---|---|
success | bool | true si HTTP 200 (sync) ou 202 (async) |
httpStatus | int | Code HTTP renvoyé par l'API |
id | string | Hash de suivi (identifie le mail dans /emails ou /emails/failed) |
status | string | sent / queued / rejected / failed |
error | string | Présent en cas d'échec |
Lève \RuntimeException si la config manque (API_MAIL_URL / API_MAIL_KEY) ou si l'appel réseau échoue (aucune réponse). Une réponse HTTP d'erreur (4xx/5xx), elle, revient dans success = false sans exception.
Exemples
Message avec bouton, expéditeur, réponse et pièce jointe (template default) :
$mailer->send([
// template omis → 'default'
'to' => 'locataire@example.com',
'subject' => 'Votre dossier',
'from' => 'service-client@amsom-habitat.fr',
'replyTo' => 'contact@amsom-habitat.fr',
'body' => [
'message' => 'Veuillez trouver le document ci-joint.',
'btnUrl' => 'https://espace.amsom-habitat.fr',
'btnText' => 'Accéder à mon espace',
],
'options' => ['logo' => 'logoSyneo', 'signature' => ['nom' => 'Martin', 'prenom' => 'Claire']],
'attachments' => [['filename' => 'dossier.pdf', 'content' => '/tmp/dossier.pdf', 'type' => 'path']],
]);
Multi-destinataires + copie :
$mailer->send([
'to' => ['a@ista.com', 'b@ista.com'],
'cc' => ['superviseur@amsom-habitat.fr'],
'subject' => "Demande d'intervention",
'body' => ['message' => '…'],
'options' => ['logo' => 'logoSyneo'],
]);
Asynchrone (fire-and-forget) :
$mailer->send([
'to' => 'locataire@example.com',
'subject' => 'Newsletter',
'body' => ['message' => '…'],
'delivery' => ['mode' => 'async'], // → 202, envoi en tâche de fond
]);
Templates disponibles
default(défaut) — template HTML tout-configurable : message, message + bouton (btnUrl/btnText), et config viaoptions. À utiliser pour tout mail générique.texte_brut— texte brut (champtext, sans HTML).- templates spécifiques (
enquete,prestataire,edl,invit_hesta…) — définis et documentés côté API mailing (voir sonREADME/V2.md), avec leurs champsbody.
Anciennes méthodes (dépréciées)
sendMail(), apiSendMail() et apiSendMailCustom() restent disponibles à l'identique pour compatibilité (elles tapent les anciennes routes non authentifiées), mais ne sont plus recommandées : préférez send().
Dépannage
| Symptôme | Cause probable |
|---|---|
RuntimeException: API_MAIL_URL et API_MAIL_KEY doivent être configurés | Variables d'env manquantes |
RuntimeException: Appel à l'API mailing échoué : … | API injoignable (réseau/DNS/URL) |
success = false, httpStatus = 401 | Clé API invalide / absente de API_KEYS côté API |
success = false, httpStatus = 404 | L'API cible n'est pas en v4 (route /v2/send absente) |
success = false, httpStatus = 422 | Payload invalide (email mal formé, champ requis manquant, template inconnu) |