Framework PHP minimaliste à saveur ivoirienne. Zéro dépendance externe · PHP 8.1+ · Architecture propre
- Installation
- Architecture
- Configuration
- Routing
- Request
- Response
- Middleware
- Base de données — database
- Offline-First
- Mode Low Bandwidth
- SMS — sms
- Logging
- CLI — console
- Helpers globaux
- Générateurs de code
- Licence
git clone https://github.com/brikocode/framework mon-projet
cd mon-projet
composer install
cp .env.example .envDémarrer le serveur de développement :
php briko feu
# → http://localhost:8000brikocode/
│
├── foundation/ Noyau applicatif
│ ├── App.php Bootstrap : charge .env, boot Logger, instancie Kernel
│ ├── Container.php Conteneur d'injection de dépendances
│ ├── Env.php Parseur .env natif
│ ├── Logger.php Système de logs structurés (+ LogChannel)
│ └── helpers.php Fonctions globales : env(), db(), sms(), logger(), base_path()
│
├── http/ Couche HTTP
│ ├── Kernel.php Reçoit la requête, dispatche, envoie la réponse
│ ├── Request.php Abstraction de la requête HTTP
│ ├── Response.php Envoi de réponse JSON/HTML avec gzip optionnel
│ └── Middleware/
│ ├── MiddlewareInterface.php
│ ├── Pipeline.php Exécute la chaîne de middlewares
│ ├── Guard.php Exemple de middleware d'authentification
│ ├── OfflineFirst.php Cache GET + file d'attente writes hors ligne
│ ├── LowBandwidth.php Gzip + sélection de champs + strip nulls
│ └── HttpLogger.php Log automatique de chaque requête HTTP
│
├── routing/ Routeur (itinéraire = chemin)
│ └── Router.php Routes dynamiques, tous verbes HTTP, middleware par route
│
├── database/ Base de données
│ ├── Connection.php Connexion PDO singleton (MySQL, PostgreSQL, SQLite)
│ ├── QueryBuilder.php Query Builder fluide
│ ├── DB.php Facade statique
│ ├── OfflineQueue.php File d'attente JSON pour requêtes hors ligne
│ └── ResponseCache.php Cache fichier des réponses GET
│
├── sms/ SMS
│ ├── SMS.php Facade statique (envoi + OTP)
│ ├── SmsMessage.php Constructeur de message fluide
│ ├── SmsResult.php Objet résultat d'un envoi
│ ├── OtpManager.php Génération/vérification OTP sans base de données
│ └── Drivers/
│ ├── SmsDriverInterface.php
│ ├── AbstractDriver.php Client HTTP partagé (cURL / fgc fallback)
│ ├── AfricasTalkingDriver.php Africa's Talking (recommandé Afrique)
│ ├── TwilioDriver.php Twilio (international)
│ ├── HttpDriver.php HTTP générique (Orange CI, MTN, Moov…)
│ └── LogDriver.php Développement — SMS dans les logs
│
├── console/ CLI
│ └── Console.php Toutes les commandes briko
│
├── app/ Ton application
│ ├── routes.php Définition des routes
│ ├── controllers/ Tes controllers
│ └── models/ Tes models
│
├── storage/
│ ├── logs/ Fichiers de log journaliers
│ ├── cache/ Cache des réponses GET
│ ├── queue/offline.json File d'attente hors ligne
│ └── otp/ Codes OTP temporaires
│
├── public/
│ └── index.php Point d'entrée HTTP
├── briko Point d'entrée CLI
├── composer.json
└── .env.example
Copie .env.example en .env et remplis les valeurs :
# Application
APP_NAME=Brikocode
APP_ENV=local # local | production
APP_DEBUG=true
APP_URL=http://localhost:8000
APP_PORT=8000
# Logs
LOG_LEVEL=DEBUG # DEBUG | INFO | WARNING | ERROR | CRITICAL
# En production : LOG_LEVEL=WARNING
# Base de données
DB_DRIVER=mysql # mysql | pgsql | sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=brikocode
DB_USER=root
DB_PASS=
# SQLite
# DB_DRIVER=sqlite
# DB_PATH=database/db.sqlite
# SMS — driver actif
SMS_DRIVER=log # log | africastalking | twilio | http
SMS_FROM=Brikocode
OTP_MESSAGE="Votre code de vérification : "
# Africa's Talking (africastalking.com)
AT_USERNAME=sandbox
AT_API_KEY=
AT_SANDBOX=true # false en production
# Twilio
TWILIO_SID=
TWILIO_TOKEN=
TWILIO_FROM=
# HTTP générique (Orange CI, MTN, Moov, etc.)
SMS_HTTP_URL=
SMS_HTTP_FIELD_TO=to
SMS_HTTP_FIELD_MSG=message
SMS_HTTP_FIELD_FROM=from
SMS_HTTP_AUTH_FIELD=apikey
SMS_HTTP_AUTH_VALUE=
SMS_HTTP_SUCCESS_CODE=200Les routes sont définies dans app/routes.php.
$router->get('/users', [UserController::class, 'index']);
$router->post('/users', [UserController::class, 'store']);
$router->put('/users/{id}', [UserController::class, 'update']);
$router->patch('/users/{id}', [UserController::class, 'patch']);
$router->delete('/users/{id}', [UserController::class, 'destroy']);$router->get('/users/{id}', [UserController::class, 'show']);
$router->get('/posts/{slug}/comments/{commentId}', [CommentController::class, 'show']);$router->get('/ping', fn () => ['pong' => true]);
$router->get('/salut', fn () => 'Gbaka est en route 🚐');use Briko\Http\Middleware\OfflineFirst;
use Briko\Http\Middleware\LowBandwidth;
use Briko\Http\Middleware\HttpLogger;
$router->get('/users', [UserController::class, 'index'], [HttpLogger::class, OfflineFirst::class]);
$router->get('/api/light', [ApiController::class, 'data'], [LowBandwidth::class]);
// Plusieurs middlewares s'exécutent dans l'ordre du tableau
$router->post('/users', [UserController::class, 'store'], [HttpLogger::class, OfflineFirst::class]);use Briko\Http\Request;
public function show(Request $request): array
{
// Paramètre de route : /users/{id}
$id = $request->param('id');
// Valeur GET, POST, JSON body ou param — ordre unifié
$name = $request->input('name', 'defaut');
$search = $request->input('q');
// Accès direct
$request->query; // $_GET
$request->post; // $_POST
$request->params; // paramètres de route
$request->files; // $_FILES
// Corps JSON brut
$data = $request->body(); // array
// Tout fusionné (query + post + body + params)
$all = $request->all();
// Utilitaires
$request->isJson(); // Content-Type: application/json ?
$request->isLowBandwidth(); // ?lb=1 ou ?compact=1 ou X-Low-Bandwidth: 1
$request->wantsFields(); // ?fields=id,name → ['id','name'] ou null
$request->acceptsGzip(); // Accept-Encoding: gzip + extension zlib ?
return ['id' => $id];
}use Briko\Http\Response;
// JSON automatique si array/object (défaut)
return ['status' => 'ok', 'data' => $users];
// Méthodes statiques
Response::json(['key' => 'value'], 201);
Response::html('<h1>Bonjour</h1>', 200);
Response::notFound('Utilisateur introuvable');
Response::error('Erreur serveur', 500);Les réponses JSON incluent automatiquement JSON_UNESCAPED_UNICODE — les caractères spéciaux africains s'affichent correctement.
| Middleware | Rôle |
|---|---|
Guard |
Exemple de blocage par header |
OfflineFirst |
Cache GET + queue writes si DB/service down |
LowBandwidth |
Gzip + sélection de champs + strip nulls |
HttpLogger |
Log automatique requête/réponse + détection slow |
<?php
namespace App\middleware;
use Briko\Http\Middleware\MiddlewareInterface;
use Briko\Http\Request;
class AuthMiddleware implements MiddlewareInterface
{
public function handle(Request $request, callable $next): mixed
{
$token = $request->input('token') ?? ($_SERVER['HTTP_AUTHORIZATION'] ?? '');
if (!$token || !$this->tokenValide($token)) {
return ['error' => 'Non autorisé', 'code' => 401];
}
return $next($request);
}
private function tokenValide(string $token): bool
{
// Ta logique de validation
return $token === env('API_TOKEN');
}
}// Utilisation
$router->get('/admin', [AdminController::class, 'index'], [AuthMiddleware::class]);La connexion est gérée automatiquement via .env. Aucun code d'initialisation requis.
use Briko\Database\DB;// Tous les enregistrements
$users = DB::table('users')->get();
// Par ID
$user = DB::table('users')->find(1);
$user = DB::table('users')->findOrFail(1); // lève RuntimeException si absent
// Premier résultat
$user = DB::table('users')->where('email', 'aya@abidjan.ci')->first();
// Conditions
DB::table('users')->where('active', 1)->get();
DB::table('users')->where('age', 18, '>=')->get();
DB::table('users')->where('ville', 'Abidjan')->orWhere('ville', 'Bouaké')->get();
// Sélection de colonnes
DB::table('users')->select('id', 'nom', 'email')->get();
// Tri
DB::table('users')->orderBy('nom')->get();
DB::table('users')->latest('created_at')->get(); // DESC
DB::table('users')->oldest('created_at')->get(); // ASC
// Limite / offset
DB::table('users')->limit(10)->offset(20)->get();
// Pagination
$page = DB::table('users')
->where('active', 1)
->orderBy('nom')
->paginate(15, 1);
// → ['data' => [...], 'total' => 120, 'per_page' => 15, 'current_page' => 1, 'last_page' => 8]
// Valeurs d'une seule colonne
$emails = DB::table('users')->pluck('email'); // ['a@ci', 'b@ci', ...]
// Comptage
$total = DB::table('users')->where('active', 1)->count();
$existe = DB::table('users')->where('email', 'x@ci')->exists(); // bool// Insertion
DB::table('users')->insert(['nom' => 'Kouadio', 'email' => 'k@abidjan.ci']);
// Insertion avec retour de l'ID
$id = DB::table('users')->insertGetId(['nom' => 'Aya', 'email' => 'aya@ci']);
// Mise à jour
DB::table('users')->where('id', 1)->update(['nom' => 'Amani Koné']);
// Suppression
DB::table('users')->where('id', 1)->delete();DB::transaction(function () {
DB::table('comptes')->where('id', 1)->update(['solde' => 500]);
DB::table('comptes')->where('id', 2)->update(['solde' => 1500]);
// Rollback automatique si une exception est levée
});$results = DB::raw('SELECT * FROM users WHERE created_at > ?', ['2024-01-01']);
$affected = DB::exec('UPDATE users SET active = 0 WHERE last_login < ?', ['2023-01-01']);DB_DRIVER=mysql # MySQL / MariaDB
DB_DRIVER=pgsql # PostgreSQL
DB_DRIVER=sqlite # SQLite (DB_PATH=database/db.sqlite)Conçu pour les zones à connectivité intermittente. Aucune dépendance externe.
GET + service UP → réponse normale + mise en cache automatique
GET + service DOWN → sert depuis le cache fichier (_offline: true)
POST + service DOWN → enfile la requête dans storage/queue/offline.json
php briko sync → rejoue les requêtes en file via le routeur interne
use Briko\Http\Middleware\OfflineFirst;
$router->get('/produits', [ProduitController::class, 'index'], [OfflineFirst::class]);
$router->post('/commandes', [CommandeController::class, 'store'], [OfflineFirst::class]);
$router->put('/commandes/{id}', [CommandeController::class, 'update'],[OfflineFirst::class]);Lecture (GET) :
{
"data": [...],
"_offline": true,
"_cached_at": "2026-04-30 10:15:22",
"_message": "Données en cache — connexion indisponible"
}Écriture (POST/PUT/DELETE) :
{
"_offline": true,
"queued": true,
"sync_id": "briko_abc123",
"message": "Requête enfilée — sera synchronisée à la reconnexion (php briko sync)",
"pending": 3
}php briko sync # Rejoue toutes les requêtes en attente
php briko sync:status # Voir la file d'attente + stats du cache
php briko sync:flush # Vider la file (attention : données perdues)Le TTL par défaut est de 300 secondes (5 minutes). Configurable :
// Cache pendant 10 minutes
$router->get('/produits', [ProduitController::class, 'index'], [
new OfflineFirst(ttl: 600)
]);use Briko\Database\ResponseCache;
use Briko\Database\OfflineQueue;
// Cache
ResponseCache::set('/produits', $data, 600);
ResponseCache::get('/produits');
ResponseCache::has('/produits');
ResponseCache::forget('/produits');
ResponseCache::flush();
ResponseCache::stats(); // ['entries' => 5, 'size_kb' => 1.2]
// File d'attente
OfflineQueue::push('POST', '/commandes', ['produit_id' => 42]);
OfflineQueue::pending();
OfflineQueue::count();
OfflineQueue::flush();Réduit la taille des réponses pour les connexions lentes.
use Briko\Http\Middleware\LowBandwidth;
$router->get('/users', [UserController::class, 'index'], [LowBandwidth::class]);
// Combiné avec Offline-First
$router->get('/users', [UserController::class, 'index'], [OfflineFirst::class, LowBandwidth::class]);| Mécanisme | Déclencheur | Effet |
|---|---|---|
| Gzip | Header Accept-Encoding: gzip |
Corps compressé (−60 à −80 %) |
| Sélection de champs | ?fields=id,nom,email |
Retourne seulement les colonnes demandées |
| Strip des nulls | ?lb=1 · ?compact=1 · X-Low-Bandwidth: 1 |
Supprime les valeurs null et vides |
# Ne retourne que id et nom dans chaque objet
GET /users?fields=id,nom
# Supprime les champs null + compresse
GET /users?compact=1
# Les deux combinés
GET /users?fields=id,nom,email&compact=1
# Via header (pratique pour les clients mobiles)
GET /users
X-Low-Bandwidth: 1
Accept-Encoding: gzipX-Bandwidth-Mode: low
X-Payload-Original: 4800B
X-Payload-Compact: 1240B
X-Payload-Saved: 3560B
Content-Encoding: gzip
X-Compressed-Size: 312B
Le tam-tam était le système de communication longue distance en Afrique avant les réseaux. Aujourd'hui c'est ton SMS.
| Driver | Cas d'usage | Inscription |
|---|---|---|
log |
Développement (SMS dans les logs, rien envoyé) | — |
africastalking |
Recommandé Afrique — CI, GH, NG, KE, TZ… | africastalking.com |
twilio |
International | twilio.com |
http |
Orange CI, MTN CI, Moov, tout provider HTTP | Ton opérateur local |
use Briko\Sms\SMS;
// Un destinataire
SMS::to('+2250700000000')->send('Votre commande #1042 est confirmée.');
// Expéditeur personnalisé
SMS::to('+2250700000000')->from('MonBoutik')->send('Nouvelle promo !');
// Multi-destinataires
SMS::to(['+2250700000000', '+2250800000000'])->send('Fermeture exceptionnelle demain.');
// Helper global
sms('+2250700000000')->send('Bienvenue !');Sans base de données — codes stockés dans storage/otp/.
// Générer et envoyer un OTP (6 chiffres, valide 5 minutes)
$code = SMS::otp('+2250700000000');
// → Envoie "[Brikocode] Votre code : 482916"
// → Retourne '482916' pour vérification côté serveur
// OTP personnalisé : 4 chiffres, valide 10 minutes
$code = SMS::otp('+2250700000000', length: 4, ttlMinutes: 10);
// Préfixe personnalisé
$code = SMS::otp('+2250700000000', prefix: 'Code MonApp : ');
// Vérifier — supprime le code si correct
$ok = SMS::verifyOtp('+2250700000000', '482916'); // true / false
// Bloqué automatiquement après 3 tentatives échouées
// Utilitaires
SMS::otpPending('+2250700000000'); // bool — un OTP est-il en attente ?
SMS::cancelOtp('+2250700000000'); // Annuler un OTP$result = SMS::to('+2250700000000')->send('Bonjour');
$result->isOk(); // bool
$result->success; // bool
$result->messageId; // string — ID retourné par le provider
$result->info; // string — statut ou message d'erreur
$result->raw; // array — réponse brute du provider
$result->toArray(); // array ['success', 'message_id', 'info']SMS_DRIVER=africastalking
AT_USERNAME=monapp # Ton username AT
AT_API_KEY=abcdef123 # Ta clé API
AT_SANDBOX=false # true en développement
SMS_FROM=BrikoAppSMS_DRIVER=twilio
TWILIO_SID=ACxxxxxxxx
TWILIO_TOKEN=xxxxxxxx
TWILIO_FROM=+1234567890SMS_DRIVER=http
SMS_HTTP_URL=https://api.orange.ci/sms/send
SMS_HTTP_AUTH_FIELD=apikey
SMS_HTTP_AUTH_VALUE=ta_cle
SMS_HTTP_FIELD_TO=numero
SMS_HTTP_FIELD_MSG=contenu
SMS_HTTP_FIELD_FROM=expediteur
SMS_HTTP_SUCCESS_CODE=200Logs JSON structurés, rotation journalière, zéro dépendance.
DEBUG → INFO → WARNING → ERROR → CRITICAL
Le niveau minimum est configurable via LOG_LEVEL dans .env.
En production, utilise LOG_LEVEL=WARNING pour ignorer DEBUG et INFO.
| Canal | Contenu |
|---|---|
app |
Logs applicatifs généraux |
http |
Requêtes HTTP (via HttpLogger middleware) |
db |
Requêtes base de données |
sms |
Envois SMS et OTP |
security |
Authentification, accès refusés |
* (libre) |
N'importe quel canal personnalisé |
use Briko\Foundation\Logger;
// Canal app (défaut)
Logger::debug('Valeur inspectée', ['var' => $value]);
Logger::info('Utilisateur connecté', ['user_id' => 42]);
Logger::warning('Tentative de connexion échouée', ['ip' => '185.x.x.x', 'attempts' => 3]);
Logger::error('Paiement refusé', ['montant' => 5000, 'operateur' => 'MTN']);
Logger::critical('Intrusion détectée', ['ip' => '185.x.x.x', 'uri' => '/admin']);
// Canal nommé
Logger::channel('db')->info('Requête lente', ['sql' => 'SELECT...', 'duration_ms' => 820]);
Logger::channel('security')->warning('Token expiré', ['user_id' => 5]);
Logger::channel('sms')->info('OTP envoyé', ['phone' => '+225...']);
// Helper global
logger('Paiement reçu', ['montant' => 5000], 'info');{
"ts": "2026-04-30 13:39:56",
"request_id": "1092537e0d",
"level": "WARNING",
"channel": "http",
"message": "GET /api/data",
"elapsed_ms": 623.0,
"memory_kb": 503,
"context": {
"duration_ms": 623,
"ip": "197.157.1.8",
"slow": true
}
}ts— horodatagerequest_id— ID unique par requête (corrèle tous les logs d'une même requête)elapsed_ms— temps écoulé depuis le bootmemory_kb— mémoire PHP utilisée
storage/logs/
├── 2026-04-30.log # Tous les canaux fusionnés
├── 2026-04-30-app.log # Canal app
├── 2026-04-30-http.log # Canal http
├── 2026-04-30-sms.log # Canal sms
└── ...
Log automatique de chaque requête. Détecte les requêtes lentes (seuil configurable).
use Briko\Http\Middleware\HttpLogger;
// Sur une route
$router->get('/users', [UserController::class, 'index'], [HttpLogger::class]);
// Seuil personnalisé : WARNING si > 1000ms
$router->get('/users', [UserController::class, 'index'], [new HttpLogger(1000)]);Masque automatiquement les champs sensibles dans les payloads :
password, password_confirmation, token, secret, card_number
# Serveur de développement
php briko feu
# Générateurs
php briko fabrique:controller UserController
php briko fabrique:model User
# Offline sync
php briko sync # Rejoue les requêtes en attente
php briko sync:status # État de la file + stats cache
php briko sync:flush # Vider la file d'attente
# Logs
php briko logs # 50 dernières lignes (tous canaux)
php briko logs http 100 # 100 dernières lignes du canal http
php briko logs:tail # Suivre en temps réel (tail -f)
php briko logs:tail sms # Suivre le canal sms
php briko logs:clear # Supprimer tous les fichiers de log
# SMS
php briko sms:driver # Voir le driver actif et sa configuration
php briko sms:test +2250700000000 "Message de test"
php briko sms:otp +2250700000000 # Générer et envoyer un OTP
# Aide
php briko helpDisponibles dans tout le code sans use ni import.
// Lire une variable d'environnement
env('APP_NAME');
env('DB_HOST', '127.0.0.1'); // avec valeur par défaut
// Query Builder raccourci
db('users')->where('active', 1)->get();
// équivalent de DB::table('users')->where('active', 1)->get()
// Envoi SMS raccourci
sms('+2250700000000')->send('Bonjour');
// équivalent de SMS::to('+2250700000000')->send('Bonjour')
// Log raccourci
logger('Événement important', ['key' => 'value']);
logger('Erreur critique', ['exception' => $e->getMessage()], 'error');
// Chemin absolu depuis la racine du projet
base_path('storage/logs');
base_path('app/config.php');php briko fabrique:controller PaiementControllerGénère app/controllers/PaiementController.php avec les 5 méthodes REST :
index() → GET /ressource
show() → GET /ressource/{id}
store() → POST /ressource
update() → PUT /ressource/{id}
destroy() → DELETE /ressource/{id}php briko fabrique:model ProduitGénère app/models/Produit.php avec les méthodes statiques :
Produit::all();
Produit::find(1);
Produit::where('categorie', 'electronique');
Produit::create(['nom' => 'iPhone', 'prix' => 350000]);
Produit::update(1, ['prix' => 320000]);
Produit::delete(1);Toutes les variables de configuration se trouvent dans le fichier .env à la racine du projet.
php briko env:setupCopie .env.example vers .env. À faire une seule fois lors de l'installation.
APP_NAME=MonApp
APP_ENV=local # local | production
APP_DEBUG=true
APP_URL=http://localhost:8000
LOG_LEVEL=DEBUG # DEBUG | INFO | WARNING | ERROR | CRITICAL
# Base de données
DB_DRIVER=mysql # mysql | pgsql | sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=ma_base
DB_USER=root
DB_PASS=
# Mail (voir section mail ci-dessous)
MAIL_DRIVER=log
# SMS (voir section sms ci-dessous)
SMS_DRIVER=logLe système de migrations gère l'évolution du schéma de ta base de données via des fichiers PHP versionnés.
Les fichiers se placent dans app/migrations/ avec le format YYYY_MM_DD_HHMMSS_nom.php :
<?php
// app/migrations/2024_01_15_120000_create_users_table.php
return [
'up' => function (PDO $pdo): void {
$pdo->exec("
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
");
},
'down' => function (PDO $pdo): void {
$pdo->exec("DROP TABLE IF EXISTS users");
},
];# Exécuter toutes les migrations en attente
php briko migrate
# Voir l'état de chaque migration (pending / done)
php briko migrate:status
# Annuler le dernier batch
php briko migrate:rollback
# Supprimer toutes les tables et tout rejouer depuis zéro
php briko migrate:fresh ┌─────────────────────────────────────────────────┬────────┬───────┐
│ Migration │ Status │ Batch │
├─────────────────────────────────────────────────┼────────┼───────┤
│ 2024_01_15_120000_create_users_table │ done │ 1 │
│ 2024_02_01_090000_add_phone_to_users │ done │ 2 │
│ 2024_03_10_140000_create_orders_table │pending │ │
└─────────────────────────────────────────────────┴────────┴───────┘
Le module mail/ gère l'envoi d'emails avec 4 drivers interchangeables. Aucune dépendance externe — SMTP natif via sockets PHP.
MAIL_DRIVER=log
# Drivers : log | smtp | sendgrid | mailgun
MAIL_FROM_ADDRESS=noreply@monapp.ci
MAIL_FROM_NAME="Mon Application"
MAIL_REPLY_TO=
# SMTP (Gmail, OVH, Amazon SES, Orange CI...)
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=ton@gmail.com
MAIL_PASSWORD=ton_mot_de_passe_app
MAIL_ENCRYPTION=tls
# tls (port 587) | ssl (port 465) | none (port 25)
# SendGrid
SENDGRID_API_KEY=SG.xxxxx
# Mailgun
MAILGUN_API_KEY=key-xxxxx
MAILGUN_DOMAIN=mg.monapp.ci
MAILGUN_REGION=us
# us | eu| Driver | Quand l'utiliser |
|---|---|
log |
Développement — emails écrits dans les logs, rien envoyé |
smtp |
Serveur SMTP : Gmail, OVH, Outlook, Amazon SES SMTP |
sendgrid |
API SendGrid (volume + deliverability) |
mailgun |
API Mailgun (transactions, région US/EU) |
use Briko\Mail\Mail;
// HTML brut
Mail::to('aya@abidjan.ci')
->subject('Confirmation commande')
->html('<h1>Merci pour ta commande !</h1>')
->send();
// Avec CC / BCC / ReplyTo
Mail::to('client@ci.ci')
->cc('copie@ci.ci')
->bcc('archive@ci.ci')
->replyTo('support@ci.ci')
->subject('Votre facture')
->html($html)
->text($texte)
->send();
// Multi-destinataires
Mail::to(['a@ci.ci', 'b@ci.ci'])
->subject('Newsletter')
->html($html)
->send();mail_to('user@ci.ci')
->subject('Bienvenue')
->html('<p>Bonjour !</p>')
->send();Les templates se placent dans app/mails/. La méthode view() charge le template et injecte les variables.
Mail::to($user['email'])
->subject('Bienvenue !')
->view('welcome', ['user' => $user])
->send();app/mails/welcome.php reçoit la variable $user :
<!DOCTYPE html>
<html>
<body>
<h1>Bienvenue, <?= htmlspecialchars($user['nom']) ?> !</h1>
<p>Ton compte a été créé avec succès.</p>
</body>
</html>Pour les emails complexes ou réutilisables, crée une classe Mailable :
php briko fabrique:mail WelcomeGénère app/mailables/WelcomeMail.php :
<?php
namespace App\mailables;
use Briko\Mail\Mail;
use Briko\Mail\Mailable;
use Briko\Mail\MailMessage;
class WelcomeMail extends Mailable
{
public function __construct(private array $user) {}
public function build(): MailMessage
{
return Mail::to($this->user['email'])
->subject('Bienvenue sur ' . env('APP_NAME'))
->view('welcome', ['user' => $this->user]);
}
}Envoi :
use Briko\Mail\Mail;
use App\mailables\WelcomeMail;
Mail::send(new WelcomeMail($user));Mail::to('client@ci.ci')
->subject('Votre facture')
->html($html)
->attach(base_path('storage/factures/facture-1042.pdf'))
->attach(base_path('storage/cgv.pdf'), 'Conditions générales', 'application/pdf')
->send();$result = Mail::to('user@ci.ci')
->subject('Test')
->html('<p>Ok</p>')
->send();
if ($result->success) {
echo 'Envoyé — ID : ' . $result->messageId;
} else {
echo 'Échec : ' . $result->error;
}# Envoyer un email de test (vérifie la config)
php briko mail:test ton@email.ci
# Afficher le driver actif et sa configuration
php briko mail:driver
# Générer un Mailable + template
php briko fabrique:mail CommandeConfirmee ╔══════════════════════════════════════╗
║ Driver Mail actif : smtp ║
╚══════════════════════════════════════╝
De : noreply@monapp.ci (Mon Application)
Config :
Host : smtp.gmail.com
Port : 587
Encryption : tls
Username : ✅ défini
Password : ✅ défini
php briko fabrique:mail NomDuMailGénère app/mailables/NomDuMailMail.php + app/mails/nomdumail.php.
MIT — Fait avec 🔥 en Côte d'Ivoire.