Simplified
This commit is contained in:
@@ -7,8 +7,7 @@ namespace App\Controllers;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Slim\Views\Twig;
|
||||
use App\Repositories\PostRepositoryInterface as PostRepository;
|
||||
use App\Requests\PostRequest;
|
||||
use Medoo\Medoo;
|
||||
use App\Models\Post;
|
||||
|
||||
/**
|
||||
@@ -16,93 +15,133 @@ use App\Models\Post;
|
||||
*/
|
||||
class PostController
|
||||
{
|
||||
private Twig $view;
|
||||
private PostRepository $repo;
|
||||
|
||||
public function __construct(Twig $view, PostRepository $repo)
|
||||
public function __construct(private Twig $view, private Medoo $db)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->repo = $repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la page d'accueil avec tous les articles.
|
||||
*/
|
||||
public function index(Request $req, Response $res): Response
|
||||
{
|
||||
$posts = $this->repo->allDesc(); // Post[]
|
||||
$rows = $this->db->select('post', '*', ['ORDER' => ['id' => 'DESC']]);
|
||||
$posts = array_map(fn ($row) => Post::fromArray($row), $rows ?: []);
|
||||
|
||||
return $this->view->render($res, 'pages/home.twig', ['posts' => $posts]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la page d'administration.
|
||||
*/
|
||||
public function admin(Request $req, Response $res): Response
|
||||
{
|
||||
$posts = $this->repo->allDesc();
|
||||
$rows = $this->db->select('post', '*', ['ORDER' => ['id' => 'DESC']]);
|
||||
$posts = array_map(fn ($row) => Post::fromArray($row), $rows ?: []);
|
||||
|
||||
return $this->view->render($res, 'pages/admin.twig', ['posts' => $posts]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formulaire de création / édition.
|
||||
*
|
||||
* @param Request $req
|
||||
* @param Response $res
|
||||
* @param array $args
|
||||
* @return Response
|
||||
* Affiche le formulaire de création/édition.
|
||||
*/
|
||||
public function form(Request $req, Response $res, array $args): Response
|
||||
{
|
||||
$id = (int)($args['id'] ?? 0);
|
||||
$post = $id ? $this->repo->find($id) : null;
|
||||
$post = null;
|
||||
|
||||
// Si id fourni mais post introuvable -> 404
|
||||
if ($id > 0 && $post === null) {
|
||||
$res->getBody()->write('Article non trouvé');
|
||||
return $res->withStatus(404);
|
||||
if ($id > 0) {
|
||||
$row = $this->db->get('post', '*', ['id' => $id]);
|
||||
if (!$row) {
|
||||
$res->getBody()->write('Article non trouvé');
|
||||
return $res->withStatus(404);
|
||||
}
|
||||
$post = Post::fromArray($row);
|
||||
}
|
||||
|
||||
// Twig peut accéder aux getters (post.title, post.content)
|
||||
$action = $id ? "/admin/edit/{$id}" : "/admin/create";
|
||||
return $this->view->render($res, 'pages/post_form.twig', ['post' => $post, 'action' => $action]);
|
||||
return $this->view->render($res, 'pages/post_form.twig', [
|
||||
'post' => $post,
|
||||
'action' => $action,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un nouvel article.
|
||||
*/
|
||||
public function create(Request $req, Response $res): Response
|
||||
{
|
||||
$postRequest = PostRequest::fromArray($req->getParsedBody());
|
||||
if (! $postRequest->isValid()) {
|
||||
$data = $req->getParsedBody();
|
||||
$title = trim((string)($data['title'] ?? ''));
|
||||
$content = trim((string)($data['content'] ?? ''));
|
||||
|
||||
// Créer un objet Post pour valider
|
||||
try {
|
||||
$post = new Post(0, $title, $content);
|
||||
} catch (\InvalidArgumentException) {
|
||||
// Validation échouée, retour à l'admin
|
||||
return $res->withHeader('Location', '/admin')->withStatus(302);
|
||||
}
|
||||
|
||||
// Utilisation du helper toModel() pour construire l'entité Post
|
||||
$post = $postRequest->toModel(0);
|
||||
$this->repo->create($post);
|
||||
// Persister en DB
|
||||
$this->db->insert('post', [
|
||||
'title' => $post->getTitle(),
|
||||
'content' => $post->getContent(),
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
return $res->withHeader('Location', '/admin')->withStatus(302);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un article existant.
|
||||
*/
|
||||
public function update(Request $req, Response $res, array $args): Response
|
||||
{
|
||||
$id = (int)$args['id'];
|
||||
$existing = $this->repo->find($id);
|
||||
if ($existing === null) {
|
||||
$existing = $this->db->get('post', 'id', ['id' => $id]);
|
||||
|
||||
if (!$existing) {
|
||||
$res->getBody()->write('Article non trouvé');
|
||||
return $res->withStatus(404);
|
||||
}
|
||||
|
||||
$postRequest = PostRequest::fromArray($req->getParsedBody());
|
||||
if (! $postRequest->isValid()) {
|
||||
$data = $req->getParsedBody();
|
||||
$title = trim((string)($data['title'] ?? ''));
|
||||
$content = trim((string)($data['content'] ?? ''));
|
||||
|
||||
// Créer un objet Post pour valider
|
||||
try {
|
||||
$post = new Post($id, $title, $content);
|
||||
} catch (\InvalidArgumentException) {
|
||||
// Validation échouée, retour à l'admin
|
||||
return $res->withHeader('Location', '/admin')->withStatus(302);
|
||||
}
|
||||
|
||||
$post = $postRequest->toModel($id);
|
||||
$this->repo->update($id, $post);
|
||||
// Persister en DB
|
||||
$this->db->update('post', [
|
||||
'title' => $post->getTitle(),
|
||||
'content' => $post->getContent(),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
], ['id' => $id]);
|
||||
|
||||
return $res->withHeader('Location', '/admin')->withStatus(302);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un article.
|
||||
*/
|
||||
public function delete(Request $req, Response $res, array $args): Response
|
||||
{
|
||||
$id = (int)$args['id'];
|
||||
$existing = $this->repo->find($id);
|
||||
if ($existing === null) {
|
||||
$existing = $this->db->get('post', 'id', ['id' => $id]);
|
||||
|
||||
if (!$existing) {
|
||||
$res->getBody()->write('Article non trouvé');
|
||||
return $res->withStatus(404);
|
||||
}
|
||||
|
||||
$this->repo->delete($id);
|
||||
$this->db->delete('post', ['id' => $id]);
|
||||
return $res->withHeader('Location', '/admin')->withStatus(302);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Factories;
|
||||
|
||||
use App\Controllers\PostController;
|
||||
use Slim\Views\Twig;
|
||||
use App\Repositories\PostRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Fabrique d'instance de PostController pour faciliter l'intégration avec un container.
|
||||
*/
|
||||
final class PostControllerFactory
|
||||
{
|
||||
/**
|
||||
* Crée une instance de PostController depuis le tableau de services renvoyé par ServiceFactory.
|
||||
*
|
||||
* @param array<string,mixed> $services
|
||||
* @return PostController
|
||||
*/
|
||||
public static function create(array $services): PostController
|
||||
{
|
||||
/** @var Twig $view */
|
||||
$view = $services['view'];
|
||||
/** @var PostRepositoryInterface $repo */
|
||||
$repo = $services['postRepository'];
|
||||
|
||||
return new PostController($view, $repo);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Factories;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use Medoo\Medoo;
|
||||
use Dotenv\Dotenv;
|
||||
use App\Repositories\PostRepositoryMedoo;
|
||||
use App\Repositories\PostRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Fabrique des services de l'application.
|
||||
*
|
||||
* Cette fabrique centralise la création des dépendances (Twig, DB, repositories...)
|
||||
* pour garder public/index.php minimal et faciliter les tests/unit.
|
||||
*/
|
||||
final class ServiceFactory
|
||||
{
|
||||
/**
|
||||
* Crée et retourne un tableau associatif de services.
|
||||
*
|
||||
* Clefs retournées :
|
||||
* - 'view' => Twig
|
||||
* - 'postRepository' => PostRepositoryInterface
|
||||
*
|
||||
* @param array<string,mixed>|null $overrides Permet d'injecter des remplacements pour les tests.
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function createServices(?array $overrides = null): array
|
||||
{
|
||||
// Charger .env si présent (tolérant l'absence)
|
||||
$dotenv = Dotenv::createImmutable(__DIR__ . '/../../');
|
||||
$dotenv->safeLoad();
|
||||
|
||||
// Config basique (idem que précédemment mais centralisé)
|
||||
$env = strtolower((string) ($_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'production'));
|
||||
$devEnvs = ['development', 'dev'];
|
||||
$twigCacheEnv = $_ENV['TWIG_CACHE'] ?? $_SERVER['TWIG_CACHE'] ?? null;
|
||||
if ($twigCacheEnv !== null && $twigCacheEnv !== '') {
|
||||
$twigCache = (string)$twigCacheEnv;
|
||||
} else {
|
||||
$twigCache = in_array($env, $devEnvs, true) ? false : __DIR__ . '/../../var/cache/twig';
|
||||
}
|
||||
|
||||
$dbFile = $_ENV['DB_FILE'] ?? __DIR__ . '/../../database/app.sqlite';
|
||||
$dbFileMode = 0664;
|
||||
|
||||
// Créer dossier cache si nécessaire
|
||||
if ($twigCache && $twigCache !== false && !is_dir((string) $twigCache)) {
|
||||
@mkdir((string) $twigCache, 0755, true);
|
||||
}
|
||||
|
||||
// Twig
|
||||
$loader = new FilesystemLoader(__DIR__ . '/../../views');
|
||||
$twig = new Twig($loader, ['cache' => $twigCache]);
|
||||
|
||||
// Medoo (SQLite)
|
||||
$dbDir = dirname($dbFile);
|
||||
if (!is_dir($dbDir)) {
|
||||
@mkdir($dbDir, 0755, true);
|
||||
}
|
||||
if (!file_exists($dbFile)) {
|
||||
@touch($dbFile);
|
||||
@chmod($dbFile, $dbFileMode);
|
||||
}
|
||||
|
||||
$medooOptions = [
|
||||
'database_type' => 'sqlite',
|
||||
'database_name' => $dbFile,
|
||||
'charset' => 'utf8',
|
||||
];
|
||||
$database = new Medoo($medooOptions);
|
||||
|
||||
// Repository
|
||||
$postRepository = new PostRepositoryMedoo($database);
|
||||
|
||||
$services = [
|
||||
'view' => $twig,
|
||||
'database' => $database,
|
||||
'postRepository' => $postRepository,
|
||||
];
|
||||
|
||||
// Appliquer overrides si fournis (utile pour tests)
|
||||
if (is_array($overrides)) {
|
||||
$services = array_merge($services, $overrides);
|
||||
}
|
||||
|
||||
return $services;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,67 +4,151 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Représente un post (DTO / entité légère).
|
||||
* Modèle Post pour un blog en production.
|
||||
*
|
||||
* Cette classe est immuable par simplicité : on construit une instance
|
||||
* depuis les données (DB ou formulaire) et on récupère ses valeurs via des getters.
|
||||
* Encapsule la logique métier et la validation des articles.
|
||||
*/
|
||||
final class Post
|
||||
{
|
||||
private int $id;
|
||||
private string $title;
|
||||
private string $content;
|
||||
private DateTime $createdAt;
|
||||
private DateTime $updatedAt;
|
||||
|
||||
/**
|
||||
* @param int $id Identifiant (0 si non persisté)
|
||||
* @param string $title Titre de l'article
|
||||
* @param string $content Contenu HTML ou texte de l'article
|
||||
*/
|
||||
public function __construct(int $id, string $title, string $content)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->title = $title;
|
||||
$this->content = $content;
|
||||
public function __construct(
|
||||
private readonly int $id,
|
||||
private readonly string $title,
|
||||
private readonly string $content,
|
||||
?DateTime $createdAt = null,
|
||||
?DateTime $updatedAt = null,
|
||||
) {
|
||||
$this->createdAt = $createdAt ?? new DateTime();
|
||||
$this->updatedAt = $updatedAt ?? new DateTime();
|
||||
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une instance depuis un tableau (par ex. ligne DB ou formulaire).
|
||||
* Crée une instance depuis un tableau (ligne DB ou formulaire).
|
||||
*
|
||||
* Ce helper facilite la migration depuis le format tableau existant.
|
||||
*
|
||||
* @param array<string,mixed> $data Clé 'id' (optionnelle), 'title', 'content'
|
||||
* @param array<string,mixed> $data
|
||||
* @return self
|
||||
*/
|
||||
public static function fromArray(array $data): self
|
||||
{
|
||||
$id = isset($data['id']) ? (int)$data['id'] : 0;
|
||||
$title = isset($data['title']) ? (string)$data['title'] : '';
|
||||
$content = isset($data['content']) ? (string)$data['content'] : '';
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
$title = (string)($data['title'] ?? '');
|
||||
$content = (string)($data['content'] ?? '');
|
||||
|
||||
return new self($id, $title, $content);
|
||||
$createdAt = isset($data['created_at'])
|
||||
? new DateTime($data['created_at'])
|
||||
: new DateTime();
|
||||
|
||||
$updatedAt = isset($data['updated_at'])
|
||||
? new DateTime($data['updated_at'])
|
||||
: new DateTime();
|
||||
|
||||
return new self($id, $title, $content, $createdAt, $updatedAt);
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
/**
|
||||
* Valide les données du post.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function validate(): void
|
||||
{
|
||||
if (empty($this->title)) {
|
||||
throw new \InvalidArgumentException('Le titre ne peut pas être vide');
|
||||
}
|
||||
|
||||
if (mb_strlen($this->title) > 255) {
|
||||
throw new \InvalidArgumentException('Le titre ne peut pas dépasser 255 caractères');
|
||||
}
|
||||
|
||||
if (empty($this->content)) {
|
||||
throw new \InvalidArgumentException('Le contenu ne peut pas être vide');
|
||||
}
|
||||
|
||||
if (mb_strlen($this->content) > 65535) {
|
||||
throw new \InvalidArgumentException('Le contenu ne peut pas dépasser 65535 caractères');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Getters
|
||||
// ============================================
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): DateTime
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Logique métier
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Retourne un tableau simple utile pour la persistance.
|
||||
* Retourne un extrait du contenu (sans HTML).
|
||||
*
|
||||
* @param int $length Longueur maximale en caractères
|
||||
* @return string
|
||||
*/
|
||||
public function getExcerpt(int $length = 150): string
|
||||
{
|
||||
$text = strip_tags($this->content);
|
||||
return mb_strlen($text) > $length
|
||||
? mb_substr($text, 0, $length) . '...'
|
||||
: $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un slug (URL-friendly) du titre.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSlug(): string
|
||||
{
|
||||
$slug = strtolower($this->title);
|
||||
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
|
||||
return trim($slug, '-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Indique si l'article a été créé récemment.
|
||||
*
|
||||
* @param int $days Nombre de jours
|
||||
* @return bool
|
||||
*/
|
||||
public function isRecent(int $days = 7): bool
|
||||
{
|
||||
$limit = new DateTime("-{$days} days");
|
||||
return $this->createdAt > $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les données prêtes à persister en DB.
|
||||
*
|
||||
* @return array{title:string,content:string}
|
||||
*/
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\Post;
|
||||
|
||||
/**
|
||||
* Interface décrivant les opérations sur les posts.
|
||||
*/
|
||||
interface PostRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Retourne tous les posts triés par id descendant.
|
||||
*
|
||||
* @return Post[] Tableau d'objets Post
|
||||
*/
|
||||
public function allDesc(): array;
|
||||
|
||||
/**
|
||||
* Trouve un post par son id.
|
||||
*
|
||||
* @param int $id
|
||||
* @return Post|null
|
||||
*/
|
||||
public function find(int $id): ?Post;
|
||||
|
||||
/**
|
||||
* Crée un post.
|
||||
*
|
||||
* @param Post $post Instance contenant les données à insérer (id ignoré)
|
||||
* @return int id inséré
|
||||
*/
|
||||
public function create(Post $post): int;
|
||||
|
||||
/**
|
||||
* Met à jour un post.
|
||||
*
|
||||
* @param int $id
|
||||
* @param Post $post Données à mettre à jour (id ignoré)
|
||||
* @return void
|
||||
*/
|
||||
public function update(int $id, Post $post): void;
|
||||
|
||||
/**
|
||||
* Supprime un post.
|
||||
*
|
||||
* @param int $id
|
||||
* @return void
|
||||
*/
|
||||
public function delete(int $id): void;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use Medoo\Medoo;
|
||||
use App\Models\Post;
|
||||
|
||||
/**
|
||||
* Repository pour "post" basé sur Medoo.
|
||||
*
|
||||
* Cette implémentation convertit les lignes DB en objets App\Models\Post.
|
||||
*/
|
||||
class PostRepositoryMedoo implements PostRepositoryInterface
|
||||
{
|
||||
private Medoo $db;
|
||||
|
||||
public function __construct(Medoo $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function allDesc(): array
|
||||
{
|
||||
$rows = $this->db->select('post', ['id', 'title', 'content'], ['ORDER' => ['id' => 'DESC']]);
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_map(function ($r) {
|
||||
return new Post(
|
||||
(int)($r['id'] ?? 0),
|
||||
(string)($r['title'] ?? ''),
|
||||
(string)($r['content'] ?? '')
|
||||
);
|
||||
}, $rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function find(int $id): ?Post
|
||||
{
|
||||
$row = $this->db->get('post', ['id', 'title', 'content'], ['id' => $id]);
|
||||
|
||||
if (empty($row) || $row === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Post(
|
||||
(int)($row['id'] ?? 0),
|
||||
(string)($row['title'] ?? ''),
|
||||
(string)($row['content'] ?? '')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function create(Post $post): int
|
||||
{
|
||||
$data = $post->toPersistableArray();
|
||||
|
||||
$this->db->insert('post', $data);
|
||||
return (int)$this->db->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function update(int $id, Post $post): void
|
||||
{
|
||||
$data = $post->toPersistableArray();
|
||||
$this->db->update('post', $data, ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$this->db->delete('post', ['id' => $id]);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Requests;
|
||||
|
||||
use App\Models\Post;
|
||||
|
||||
/**
|
||||
* Classe simple responsable de la validation / sanitation des données
|
||||
* provenant des formulaires de post.
|
||||
*
|
||||
* Usage : $valid = PostRequest::fromArray($data)->validated();
|
||||
*/
|
||||
final class PostRequest
|
||||
{
|
||||
private string $title;
|
||||
private string $content;
|
||||
|
||||
private function __construct(string $title, string $content)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une instance à partir d'un tableau (ex: $request->getParsedBody()).
|
||||
*
|
||||
* @param mixed $input
|
||||
* @return self
|
||||
*/
|
||||
public static function fromArray($input): self
|
||||
{
|
||||
$title = isset($input['title']) ? (string)$input['title'] : '';
|
||||
$content = isset($input['content']) ? (string)$input['content'] : '';
|
||||
|
||||
$title = trim($title);
|
||||
$content = trim($content);
|
||||
|
||||
// Limites raisonnables (configurable si besoin)
|
||||
$title = mb_substr($title, 0, 255);
|
||||
$content = mb_substr($content, 0, 65535);
|
||||
|
||||
return new self($title, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les données validées/sanitizées prêtes à persister.
|
||||
*
|
||||
* @return array{title:string,content:string}
|
||||
*/
|
||||
public function validated(): array
|
||||
{
|
||||
// Ici on pourrait ajouter des validations plus strictes (ex: required, longueur minimale)
|
||||
return [
|
||||
'title' => $this->title,
|
||||
'content' => $this->content,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indique si le formulaire est suffisamment rempli.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
return $this->title !== '' && $this->content !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit la requête validée en modèle App\Models\Post.
|
||||
*
|
||||
* Ce helper facilite la construction d'un Post directement depuis la requête.
|
||||
*
|
||||
* @param int $id Identifiant (0 si nouvel enregistrement)
|
||||
* @return Post
|
||||
*/
|
||||
public function toModel(int $id = 0): Post
|
||||
{
|
||||
return new Post($id, $this->title, $this->content);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user