Votre MVP commence ici.

CoreEngine est un moteur PHP ultra-léger (600ko) conçu pour les développeurs qui veulent valider leurs idées rapidement sans sacrifier la structure et la sécurité.

Installation Rapide (Docker)

CoreEngine est livré avec une pile Docker complète pour un déploiement instantané. Plus besoin de configurer Apache ou MySQL localement.

1. Pré-requis

  • Docker & Docker Compose
  • Node.js & NPM

2. Lancement

# 1. Lancer l'infrastructure
docker-compose up --build -d
# 2. Installer & Compiler le Front
npm install
npm run dev
                        

Et hop ! C'est prêt.

Rendez-vous sur localhost:8081. Le fichier .env est déjà pré-configuré pour communiquer avec les containers Docker (DB_HOST=db, etc.).

Configuration (.env)

CoreEngine utilise un chargeur d'environnement personnalisé pour isoler vos paramètres sensibles. Le fichier .env est automatiquement parsé et typé pour faciliter son utilisation dans votre code.

// Fichier .env

APP_NAME="CoreEngine"
APP_DEBUG=true
APP_MAINTENANCE=false

DB_HOST=127.0.0.1
DB_NAME=core_db
DB_USER=root

// Utilisation du Helper

Utilisez la fonction globale env(). Elle gère automatiquement les valeurs par défaut et le casting des types :

// Récupération avec valeur par défaut
$debug = env('APP_DEBUG', false); 

// Le helper transforme les strings du .env :
// "true"  => true  (bool)
// "false" => false (bool)
// "null"  => null  (null)
                        

À éviter

$db = $_ENV['DB_NAME'];

Recommandé (CoreEngine Way)

$db = env('DB_NAME', 'default_db');

Mode Maintenance intégré

Grâce à votre env_loader.php, passer APP_MAINTENANCE=true intercepte toutes les requêtes pour rediriger vers la vue de maintenance avec un code 503.

Maintenance & Mode Recovery

Parce qu'un crash en production arrive toujours au mauvais moment, CoreEngine intègre un Cockpit de survie autonome. Même si le noyau est corrompu ou les variables .env erronées, le mode Recovery vous permet de reprendre la main.

1. Accès Sécurisé

Accédez au fichier recovery.php à la racine de votre projet. Utilisez votre EMERGENCY_TOKEN défini dans le script pour déverrouiller la session.

// Chemin d'accès par défaut
https://votre-domaine.com/recovery.php

2. Capacités du Cockpit

  • Check d'intégrité via Manifeste
  • Hot-Swap de variables .env
  • Test de liaison Database en temps réel
  • Déploiement & Rollback de ZIP

Sécurité Critique

Le mode Recovery est conçu pour être 100% autonome. Il ne dépend d'aucune classe du CoreEngine pour fonctionner, ce qui garantit son exécution même si le framework est totalement hors service.

Conseil : Changez le token par défaut immédiatement après l'installation.

Architecture Avancée

Le framework repose sur une arborescence stricte séparant le code métier de la mécanique interne, avec une gestion hybride de l'autoloading (Natif + Composer optionnel).

// Service Providers

Les Providers sont les points centraux où les services (Database, Auth) sont enregistrés dans le Container.

// config/app.php
'providers' => [
    \App\Providers\DatabaseServiceProvider::class,
    \App\Providers\AuthServiceProvider::class,
]
                    

// Helpers Globaux

Des fonctions utilitaires disponibles partout sans import :

  • csrf_field() : Génère un input de sécurité
  • redirect($path) : Redirection HTTP immédiate
  • env($key, $default) : Accès sécurisé à la config

// Structure des Dossiers

/
├── app/               # Code métier (Contrôleurs, Modèles, Middlewares)
├── config/            # Cœur du framework (Facades, Router) - NE PAS TOUCHER
├── database/          # Fichiers de Seeders et Migrations
├── public/            # Point d'entrée (index.php) et ressources (css, js)
├── resources/         # Vues de l'application (HTML/PHP pur)
├── routes/            # Fichiers de définition des routes (web.php)
├── vendor/            # Autoloader natif + paquets Composer éventuels
├── .env               # Configuration d'environnement isolé
└── artisan            # Assistant en ligne de commande

Mode Maintenance

Activez APP_MAINTENANCE=true dans votre .env pour rediriger instantanément tous les utilisateurs vers la page /maintenance avec un code HTTP 503.

Autoloader & Composer (Optionnel)

CoreEngine est conçu pour fonctionner de manière 100% autonome, sans nécessiter Composer. Cependant, il est assez flexible pour s'intégrer avec si vous souhaitez utiliser des librairies externes de l'écosystème PHP.

Fonctionnement de l'Autoloader Hybride

Le point d'entrée public/index.php vérifie d'abord l'existence de l'autoloader de Composer. S'il est trouvé, il est chargé. Ensuite, l'autoloader natif de CoreEngine est chargé, assurant que le framework fonctionne dans tous les cas.

// public/index.php

// 1. Intégration de l'autoloader Composer (Optionnel)
if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
    require_once dirname(__DIR__) . '/vendor/autoload.php';
}

// 2. Chargement de l'autoloader natif de CoreEngine
require_once dirname(__DIR__) . '/vendor/coreengine-autoload.php';
                

Liberté Totale

Cette approche vous donne le meilleur des deux mondes :

  • Utilisez composer require ma-librairie/super-outil pour vos projets complexes.
  • Déployez un projet CoreEngine par simple copie FTP/SSH sans jamais avoir à vous soucier de Composer pour les projets plus simples.
Le framework ne plantera jamais si le dossier vendor de Composer est absent.

Cycle d'une requête

Chaque interaction avec CoreEngine suit un flux linéaire et sécurisé, transformant une Request en Response à travers une pile de composants intelligents.

01. Entry

Capture & Sanitize

L'objet Request capture GET/POST/JSON et nettoie récursivement les données contre les XSS.

02. Routing

Regex Matching

Le Router analyse l'URI, extrait les paramètres dynamiques {id} et identifie la cible.

03. Pipeline

Middlewares

La requête traverse une pile de filtres. Chaque couche peut interrompre ou modifier le flux.

04. Dispatch

Auto-Injection

Le contrôleur est instancié via Reflection avec injection automatique des dépendances du Container.

L'Objet Request

// Utilisation dans un contrôleur

public function store(Request $request) {
    $email = $request->input('email');
    $data  = $request->only(['name', 'role']);
    $id    = $request->param('id'); // Param de route
}
                        

L'objet Request supporte nativement le JSON (pour vos APIs) et le form-data classique de manière totalement transparente.

L'Objet Response

// Helper statiques fluides

Response::json(['status' => 'success'], 201);
Response::redirect('/dashboard');
Response::notFound("Article introuvable");
                        

La classe Response standardise les sorties et gère les entêtes HTTP ainsi que les codes de statut automatiquement.

Résolution Intelligente (Reflection API)

Contrairement aux routeurs basiques, le Router de CoreEngine utilise l'API Reflection de PHP pour inspecter le constructeur de vos contrôleurs. Il injecte automatiquement les dépendances enregistrées dans le Container sans que vous n'ayez à instancier quoi que ce soit manuellement.

Système de Routage

Le routeur de CoreEngine est un moteur de résolution dynamique. Il gère l'extraction de paramètres via Regex, l'injection automatique de dépendances et une pile de middlewares bidirectionnelle.

// Syntaxe & Vues

Liez une URL à une logique métier ou rendez une vue statique instantanément.

// Closure simple
Route::get('/api/status', fn() => ["status" => "ok"]);

// Rendu direct (resources/Views/welcome.php)
Route::view('/', 'welcome');
                            

// Groupement de Contrôleur

Centralisez la logique d'un module pour un code plus propre.

Route::controller(UserController::class, function($router) {
    $router->get('/profile', 'show');
    $router->post('/profile', 'update');
});
                            

Paramètres Dynamiques

Les segments entre {accolades} sont convertis en groupes de capture Regex et injectés dans l'objet Request.

Route::get('/blog/{slug}', [PostController::class, 'show']);

Injection de Dépendances (DI)

Grâce à ReflectionClass, votre contrôleur reçoit automatiquement ses services via son constructeur.

public function __construct(Database $db, Mailer $mail) {
    $this->db = $db;
}
                            

Middleware Pipeline (Onion Architecture)

"Chaque middleware enveloppe le suivant. Le Router utilise array_reduce pour créer cette structure en oignon."

Entrée

AuthMiddleware

Traitement

LogMiddleware

Cible

Controller

Route::get('/admin', [AdminController::class, 'index'], [
    AuthMiddleware::class,
    AdminMiddleware::class
]);
                        

Middlewares

Les middlewares agissent comme des gardiens. Ils interceptent la Request avant qu'elle n'atteigne le contrôleur, permettant de filtrer, valider ou rediriger le flux selon des règles métier précises.

// 1. Structure d'un Middleware

namespace App\Middlewares;

use Config\Facades\Http\Request;
use Config\Facades\Http\Response;

class AuthMiddleware {
    public function handle(Request $request, callable $next) {
        if (!isset($_SESSION['user'])) {
            return Response::redirect('/login');
        }

        return $next($request);
    }
}
                        

// 2. Mécanique du Pipeline

  • Validation : Vérifie l'état de la session, les permissions ou la présence de jetons (JWT/CSRF).
  • Interruption : Si la condition échoue, le middleware utilise Response pour stopper le cycle.
  • Propagation : $next($request) passe la main à la couche suivante de l'oignon.

// 3. Enregistrement sur les Routes

L'application est simple : passez un tableau de classes (FQCN) en troisième argument de vos définitions de routes.

Route::get('/admin/dashboard', [AdminController::class, 'index'], [
    \App\Middlewares\AuthMiddleware::class,
    \App\Middlewares\RoleAdminMiddleware::class
]);
                    

Optimisation du Pipeline

CoreEngine utilise array_reduce pour compiler la pile de middlewares en une seule fonction anonyme (Closure) lors de la résolution de la route. Cette approche garantit une exécution ultra-rapide, peu importe le nombre de couches.

Base de Données & Service Provider

La connexion n'est pas simplement globale : elle est injectée via le Container. Le DatabaseServiceProvider centralise la configuration, rendant la base de données disponible partout de manière découplée et hautement performante.

// DatabaseServiceProvider.php

$container->set(PDO::class, function () {
    // Charge l'instance PDO depuis la config centrale
    return require 'config/database.php';
});

// Initialise la Facade DB avec l'instance résolue
DB::boot($container->get(PDO::class));
                        

Initialisation "Lazy Load"

Grâce à la fermeture (closure) dans le Container, la connexion PDO n'est établie que lors du premier appel. Si une requête (ex: page statique) n'utilise pas DB, aucune ressource serveur n'est consommée.

Le rôle de DB::boot()

Cette méthode lie l'instance PDO unique à la Facade statique. Cela permet d'utiliser DB::table() avec une syntaxe fluide tout en conservant une base saine et testable en arrière-plan.

Environnements Hybrides

Le Provider utilise les variables d'environnement pour basculer dynamiquement entre l'hôte Docker et 127.0.0.1, garantissant un workflow identique en local et en production.

Query Builder (DB Facade)

Avant l'ORM, il y a le Query Builder. C'est une interface fluide et sécurisée pour construire des requêtes SQL complexes via la façade DB. Il est idéal pour les requêtes qui ne nécessitent pas d'hydratation en Modèles.

// Sélectionner des utilisateurs actifs
$users = DB::table('users')
            ->select('id', 'email')
            ->where('status', '=', 'active')
            ->orderBy('created_at', 'DESC')
            ->get(); // Retourne une Collection de tableaux associatifs

// Compter les articles
$count = DB::table('articles')->count();

// Insertion
$id = DB::table('logs')->insert([
    'level' => 'info',
    'message' => 'User logged in'
]);
            

Artisan CLI

L'interface en ligne de commande de CoreEngine automatise la création de fichiers et la gestion de la base de données. Elle intègre une détection intelligente de l'environnement pour faciliter le passage entre Docker et votre système hôte.

// Génération de Boilerplate

# Créer un contrôleur

php artisan make:controller UserController

# Créer un modèle (Table auto au pluriel)

php artisan make:model UserModel

# Créer un modèle avec table spécifique

php artisan make:model PostModel blog_posts

// Base de données & Workflow

# Lancer le seeder par défaut (DatabaseSeeder)

php artisan db:seed

# Lancer un seeder spécifique

php artisan db:seed UserSeeder

Options Avancées

Pour les développements hors conteneur (ex: PowerShell), forcez la connexion locale :

--host=127.0.0.1

[CoreEngine] model créé avec succès : app/Models/PostModel.php (Table: blog_posts)

Success Output

Contrôleurs

Les contrôleurs étendent la classe Config\Facades\Controllers\Controllers. Ils bénéficient de l'injection automatique de PDO et de méthodes utilitaires pour le rendu, les API et la gestion de fichiers.

// Structure Standard

namespace App\Controllers;
use Config\Facades\Controllers\Controllers;
use Config\Facades\Http\Request;

class UserController extends Controllers {
    public function index(Request $request) {
        return $this->render('users/profile', [
            'user' => 'Jalal'
        ]);
    }
}
                    

// API & Téléchargements

CoreEngine facilite les réponses standardisées :

// Réponse JSON structurée
return $this->response("1", true, $data, "Succès", 200);

// Téléchargement sécurisé
return $this->makeDownload('facture.pdf');
                    

Auto-PDO

L'instance PDO est injectée au constructeur et disponible via $this->pdo dans toutes vos méthodes.

Smart Download

makeDownload() gère les headers HTTP, le nettoyage du tampon (OB) et la vérification d'existence du fichier.

JSON Factory

La méthode response() garantit que vos API renvoient toujours un format constant : {r, success, datas, message}.

Protection des vues

La méthode render de la classe Controllers inclut une protection !defined('UNIT_TESTING'), permettant de tester vos contrôleurs sans déclencher l'affichage des fichiers de vue.

Modèles & ORM

L'ORM de CoreEngine repose sur un QueryBuilder fluide et un système de Single Source of Truth. Le schéma de votre base de données est défini directement dans vos modèles, permettant l'auto-migration et une protection stricte des données.

1. Schéma & Source de Vérité

Définissez vos colonnes et leurs types SQL. Ce tableau sert à la fois de protection et de plan de construction pour la base de données.

class UserModel extends Models {
    protected static string $tableName = 'users';

    // Définition SQL + Protection de masse
    protected static array $schemaColumns = [
        "id"        => "INT AUTO_INCREMENT PRIMARY KEY",
        "email"     => "VARCHAR(255) NOT NULL UNIQUE",
        "firstName" => "VARCHAR(100)",
        "password"  => "VARCHAR(255) NOT NULL",
        "role_id"   => "INT DEFAULT 1"
    ];
}
                    

2. Requêtes Fluides

Le modèle agit comme un pont vers le QueryBuilder. Les appels statiques sont automatiquement redirigés.

// Récupère une Collection d'objets UserModel
$users = UserModel::where('role_id', '=', 2)
                ->orderBy('firstName', 'DESC')
                ->limit(10)
                ->get();

// Hydratation automatique depuis un ID
$user = UserModel::find(1);
                    

Auto-Migration

Générez vos tables SQL d'un simple appel à UserModel::migrate().

Sécurité Native

Protection stricte via __set() : impossible d'écrire dans une colonne non définie.

Active Record

save() détecte automatiquement s'il doit effectuer un INSERT ou un UPDATE.

Hydratation

Les résultats de requêtes sont transformés instantanément en instances de Modèles.

Cycle de vie des données

$user = new UserModel();
$user->firstName = "Jalal"; // Vérifie si 'firstName' existe dans $schemaColumns
$user->save();              // INSERT - ID généré automatiquement

$user->email = "jalal@coreengine.dev";
$user->save();              // UPDATE - L'ID est présent, le moteur cible l'enregistrement
                

Collections

Les Collections sont des enveloppes puissantes pour les tableaux de données. Elles sont retournées automatiquement par le QueryBuilder et offrent des méthodes de manipulation chaînables.

// Filtrage & Transformation

$users = UserModel::all();

// Extraire uniquement les emails
$emails = $users->pluck('email');

// Filtrer les admins et trier par nom
$admins = $users->filter(function($user) {
    return $user->role === 'admin';
})->sortBy('firstName', 'asc');
                        

// Analyse de données

// Calculer le total des factures
$total = $invoices->sum('amount');

// Grouper les utilisateurs par ville
$cities = $users->groupBy('city');

// Vérification rapide
if ($users->isEmpty()) {
    // ...
}
                        

Itérable

Utilisez vos collections directement dans un foreach grâce à l'interface IteratorAggregate.

JSON Ready

Envoyez une collection directement en réponse API : elle sera automatiquement sérialisée via json_encode.

Polyvalence

Supporte aussi bien les tableaux d'objets (Modèles) que les tableaux associatifs classiques.

Exemple concret : Statistiques de ventes

CoreEngine permet de traiter des données complexes en une seule ligne :

$revenue = $orders->filter(fn($o) => $o->status === 'paid')->unique('customer_id')->sum('total');

Validation & Sécurité

La sécurité des données est intégrée nativement. Le Validator permet de vérifier l'intégrité des entrées utilisateur avant tout traitement, tandis que les Modèles protègent la base de données via un filtrage par colonnes.

// 1. Définir les règles dans le Modèle

class User extends Models {
    protected static array $rules = [
        'email'    => 'required|email',
        'password' => 'required|min:8',
        'age'      => 'number',
        'status'   => 'boolean'
    ];
}
                        

// 2. Valider une requête

public function store(Request $request) {
    $validation = User::validate($request->all());

    if (!$validation['success']) {
        return view('register', [
            'errors' => $validation['errors']
        ]);
    }

    // Données filtrées et validées
    $user = (new User())->fill($validation['data']);
    $user->save();
}
                        

Règles disponibles

required : Champ obligatoire
email : Format email valide
text : Chaîne de caractères
number : Valeur numérique
boolean : `true`, `false`, `1`, `0`
min:x / max:x : Longueur min/max
date / timestamp : Format temporel

Protection Mass-Assignment

Grâce à la propriété $schemaColumns de vos modèles, CoreEngine ignore automatiquement toute donnée reçue qui ne correspond pas à une colonne réelle de votre table SQL. Cela empêche l'injection de colonnes malveillantes lors d'un fill().

Authentification & Rôles

CoreEngine intègre un système d'authentification complet via la façade Auth et une gestion des permissions via la façade Role.

// Façade Auth

Gérez le cycle de vie de la session utilisateur.

// Vérifier si l'utilisateur est connecté
if (Auth::check()) { ... }

// Récupérer l'ID de l'utilisateur connecté
$userId = Auth::id();

// Récupérer le modèle User complet
$user = Auth::user();

// Connecter un utilisateur (après vérification du mot de passe)
Auth::login($userModelInstance);

// Déconnecter l'utilisateur
Auth::logout();
                    

// Façade Role

Vérifiez les permissions de l'utilisateur connecté.

// Vérifier si l'utilisateur a un rôle spécifique
if (Role::is('ADMIN')) {
    // Logique réservée aux administrateurs
}

// Vérifier si l'utilisateur a l'un des rôles
if (Role::isOneOf(['ADMIN', 'MODERATOR'])) {
    // ...
}

// Middleware de rôle
class RoleAdminMiddleware {
    public function handle(Request $request, callable $next) {
        if (!Role::is('ADMIN')) {
            return Response::redirect('/unauthorized');
        }
        return $next($request);
    }
}
                    

Vues & Rendu

CoreEngine utilise un système de rendu basé sur PHP pur pour une performance maximale. Les vues sont isolées, ce qui signifie qu'elles n'ont accès qu'aux variables que vous leur passez explicitement.

// Structure des fichiers

  • resources/
  • Views/
  • home.php
  • dashboard.php

// Retourner une vue

Depuis un contrôleur ou directement dans une route :

// Dans le Router
Route::view('/', 'home');

// Dans un Contrôleur
return $this->render('user/profile', [
    'name' => 'Jalal',
    'role' => 'Admin'
]);
                        

Fonctionnement interne

Isolation via Extract La méthode extract($datas) transforme les clés de votre tableau en variables locales disponibles dans la vue.
Output Buffering CoreEngine utilise ob_start() pour capturer le rendu PHP avant de l'envoyer au navigateur, permettant des manipulations de dernière minute.
Clean Echo Les vues sont de simples fichiers PHP, vous permettant d'utiliser <?= $var ?> pour un affichage rapide et lisible.

Exemple : Afficher les erreurs de validation

<?php if(isset($errors)): ?>
  <ul class="alert-red">
    <?php foreach($errors as $field => $msgs): ?>
      <li><?= $msgs[0] ?></li>
    <?php endforeach; ?>
  </ul>
<?php endif; ?>

Mail & Logs

CoreEngine fournit une interface unifiée pour l'envoi d'e-mails et la journalisation. Basé sur le Design Pattern Factory, le module Mail vous permet de basculer instantanément entre différents fournisseurs.

Mail Facade

use Config\Facades\Mail\Mail;

// Configuration via .env (MAIL_DRIVER=sendgrid|resend|smtp)
Mail::to('client@exemple.com')
    ->cc('admin@exemple.com')
    ->subject('Votre Commande')
    ->html('<h1>Merci !</h1>')
    ->send();
                        

Changez de fournisseur en modifiant simplement MAIL_DRIVER dans votre .env. Le code métier reste 100% identique.

Log Facade

use Config\Facades\Log;

Log::info("Nouvel utilisateur inscrit");
Log::warning("Tentative de connexion échouée");
Log::exception("Erreur inattendue détectée");
                        

Le système de log gère l'écriture structurée de l'historique de votre application, nativement lisible depuis l'Administration Suite.

Tuto 1 : CRUD Complet de Tâches

Apprenez à combiner Modèle, ORM, Validation et Contrôleur pour construire un système complet de gestion de tâches (Create, Read, Update, Delete).

1 Le Modèle (Task.php)

On définit le schéma et les règles. On ajoute une colonne user_id pour lier la tâche à un utilisateur.

// app/Models/Task.php
class Task extends Models {
    protected static string $tableName = 'tasks';
    
    protected static array $schemaColumns = [
        "id"          => "INT AUTO_INCREMENT PRIMARY KEY",
        'title'       => 'VARCHAR(255) NOT NULL',
        'user_id'     => 'INT NOT NULL',
        'is_done'     => 'BOOLEAN DEFAULT FALSE'
    ];
    
    protected static array $rules = [
        'title' => 'required|min:3|max:50',
    ];
}
                    

2 Le Contrôleur (TaskController.php)

On crée les méthodes pour chaque action du CRUD.

// app/Controllers/TaskController.php
class TaskController extends Controllers {
    // Affiche la liste des tâches et le formulaire
    public function index() {
        $tasks = Task::where('user_id', Auth::id())->get();
        return $this->render('tasks/index', ['tasks' => $tasks]);
    }

    // Enregistre une nouvelle tâche
    public function store(Request $request) {
        $validation = Task::validate($request->all());
        if (!$validation['success']) {
            // Gérer les erreurs...
            return Response::redirect('/tasks');
        }

        $task = new Task();
        $task->fill($validation['data']);
        $task->user_id = Auth::id();
        $task->save();

        return Response::redirect('/tasks');
    }

    // Met à jour le statut d'une tâche
    public function update(Request $request) {
        $task = Task::find($request->param('id'));
        // Vérifier que la tâche appartient à l'utilisateur connecté
        if ($task && $task->user_id == Auth::id()) {
            $task->is_done = !$task->is_done;
            $task->save();
        }
        return Response::redirect('/tasks');
    }

    // Supprime une tâche
    public function destroy(Request $request) {
        $task = Task::find($request->param('id'));
        if ($task && $task->user_id == Auth::id()) {
            $task->delete();
        }
        return Response::redirect('/tasks');
    }
}
                    

3 Les Routes (web.php)

On relie les URLs aux méthodes du contrôleur, en protégeant le tout derrière un middleware d'authentification.

// routes/web.php
Route::middlewares([AuthMiddleware::class], function() {
    Route::get('/tasks', [TaskController::class, 'index']);
    Route::post('/tasks', [TaskController::class, 'store']);
    Route::post('/tasks/{id}/update', [TaskController::class, 'update']);
    Route::post('/tasks/{id}/delete', [TaskController::class, 'destroy']);
});
                    
Résultat

Vous avez maintenant un système de gestion de tâches fonctionnel et sécurisé, où chaque utilisateur ne voit et ne gère que ses propres tâches.

Tuto 2 : Espace Membre Sécurisé

Apprenez à protéger des sections de votre site avec les Middlewares et à afficher des données spécifiques à l'utilisateur connecté.

1 Le Middleware d'Authentification

Ce gardien vérifie si un utilisateur est connecté. Sinon, il le redirige vers la page de connexion.

// app/Middlewares/AuthMiddleware.php
class AuthMiddleware implements Middleware {
    public function handle(Request $request, callable $next) {
        if (!Auth::check()) {
            return Response::redirect('/signin');
        }
        return $next($request);
    }
}
                    

2 Grouper les Routes Protégées

Utilisez Route::middlewares() pour appliquer un ou plusieurs middlewares à tout un groupe de routes.

// routes/web.php
Route::middlewares([AuthMiddleware::class], function() {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::get('/profile', [ProfileController::class, 'show']);
    // ... toutes vos autres routes membres
});
                    

3 Le Contrôleur du Dashboard

Le contrôleur récupère l'utilisateur connecté via la façade Auth et ses tâches.

// app/Controllers/DashboardController.php
class DashboardController extends Controllers {
    public function index() {
        $user = Auth::user(); // Récupère l'objet UserModel complet
        $tasks = Task::where('user_id', $user->id)->get();
        $taskCount = $tasks->count();

        return $this->render('dashboard', [
            'user' => $user,
            'tasks' => $tasks,
            'taskCount' => $taskCount
        ]);
    }
}
                    

4 La Vue (dashboard.php)

Affichez les informations de manière dynamique.

<h1>Bienvenue, <?= $user->firstName ?> !</h1>
<p>Vous avez <?= $taskCount ?> tâches en cours.</p>

<ul>
<?php foreach($tasks as $task): ?>
    <li><?= $task->title ?></li>
<?php endforeach; ?>
</ul>
                    

Tuto 3 : Construire une API REST

Créez une API stateless et sécurisée pour gérer vos tâches. Idéal pour une application front-end (React, Vue.js) ou une app mobile.

1 Le Middleware d'API

Ce middleware vérifiera la présence d'un token dans l'en-tête Authorization. Pour cet exemple, on imagine que chaque utilisateur a un api_token dans la base de données.

// app/Middlewares/ApiAuthMiddleware.php
class ApiAuthMiddleware implements Middleware { // Note: `Middleware` interface should be imported
    public function handle(Request $request, callable $next) {
        $authHeader = $request->header('Authorization');
        $token = null;

        if ($authHeader && str_starts_with($authHeader, 'Bearer ')) {
            $token = substr($authHeader, 7);
        }

        if (!$token) {
            return Response::json(['message' => 'Unauthorized'], 401);
        }

        // Cherche l'utilisateur par son token
        $user = UserModel::where('api_token', '=', $token)->first();

        if (empty($user)) {
            return Response::json(['message' => 'Invalid token'], 401);
        }

        // Authentifie l'utilisateur pour cette requête
        Auth::login($user);

        return $next($request);
    }
}
                    

2 Le Contrôleur d'API

Ce contrôleur ne renvoie jamais de vue, uniquement du JSON via la méthode response().

// app/Controllers/Api/TaskController.php
class TaskController extends Controllers {
    public function index() {
        $tasks = Task::where('user_id', Auth::id())->get();
        return $this->response("1", true, $tasks, "Tasks retrieved");
    }

    public function store(Request $request) {
        // ... validation ...
        $task = new Task();
        $task->fill($request->only(['title']));
        $task->user_id = Auth::id();
        $task->save();
        return $this->response("1", true, $task, "Task created", 201);
    }
    // ... méthodes show, update, destroy ...
}
                    

3 Les Routes d'API

On groupe les routes sous un préfixe /api et on les protège avec notre nouveau middleware.

// routes/web.php
Route::prefix('api', function() {
    Route::middlewares([ApiAuthMiddleware::class], function() {
        Route::get('/tasks', [Api\TaskController::class, 'index']);
        Route::post('/tasks', [Api\TaskController::class, 'store']);
        Route::get('/tasks/{id}', [Api\TaskController::class, 'show']);
        Route::put('/tasks/{id}', [Api\TaskController::class, 'update']);
        Route::delete('/tasks/{id}', [Api\TaskController::class, 'destroy']);
    });
});
                    

4 Tester avec cURL

Utilisez un client HTTP comme cURL pour interagir avec votre nouvelle API.

# Récupérer les tâches
curl -X GET http://localhost:8081/api/tasks \
-H "Authorization: Bearer VOTRE_API_TOKEN"

# Créer une tâche
curl -X POST http://localhost:8081/api/tasks \
-H "Authorization: Bearer VOTRE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Apprendre CoreEngine"}'
                    

Tuto 4 : Formulaire de Contact

Mettez en pratique la validation et le module d'envoi d'e-mails en créant un formulaire de contact fonctionnel.

1 Le Contrôleur (ContactController.php)

Ce contrôleur gérera l'affichage du formulaire et le traitement des données envoyées.

// app/Controllers/ContactController.php
use Config\Facades\Validation\Validator;
use Config\Facades\Mail\Mail;

class ContactController extends Controllers {
    public function index() {
        return $this->render('contact/form');
    }

    public function send(Request $request) {
        $validator = new Validator();
        $rules = [
            'name' => 'required|text',
            'email' => 'required|email',
            'message' => 'required|min:10'
        ];

        if (!$validator->validate($request->all(), $rules)) {
            // Rediriger avec les erreurs et les anciennes données
            return $this->render('contact/form', [
                'errors' => $validator->errors(),
                'old' => $request->all()
            ]);
        }

        // Envoi de l'email à l'administrateur
        Mail::to(env('ADMIN_EMAIL', 'admin@test.com'))
            ->replyTo($request->input('email'))
            ->subject('Nouveau message de ' . $request->input('name'))
            ->text($request->input('message'))
            ->send();

        // Rediriger avec un message de succès
        return $this->render('contact/form', ['success' => true]);
    }
}
                    

2 Les Routes (web.php)

Deux routes suffisent : une pour afficher le formulaire, une pour le traiter.

// routes/web.php
Route::get('/contact', [ContactController::class, 'index']);
Route::post('/contact', [ContactController::class, 'send']);
                    

3 La Vue (resources/Views/contact/form.php)

La vue affiche le formulaire, les messages d'erreur et le message de succès.

<?php if(isset($success)): ?> <div class="alert-success">Message envoyé !</div> <?php else: ?> <?php if(isset($errors)): ?> <ul class="alert-danger"> <?php foreach($errors as $field => $error): ?> <li><?= htmlspecialchars($error[0]) ?></li> <?php endforeach; ?> </ul> <?php endif; ?> <form action="/contact" method="POST"> <?= csrf_field() ?> <input type="text" name="name" value="<?= htmlspecialchars($old['name'] ?? '') ?>"> <input type="email" name="email" value="<?= htmlspecialchars($old['email'] ?? '') ?>"> <textarea name="message"><?= htmlspecialchars($old['message'] ?? '') ?></textarea> <button type="submit">Envoyer</button> </form> <?php endif; ?>