Added Models

This commit is contained in:
julien
2026-03-09 12:22:50 +01:00
parent 18eabb0560
commit 4342751c70
7 changed files with 146 additions and 44 deletions

View File

@@ -9,6 +9,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Views\Twig;
use App\Repositories\PostRepositoryInterface as PostRepository;
use App\Requests\PostRequest;
use App\Models\Post;
/**
* Contrôleur pour les posts.
@@ -26,7 +27,7 @@ class PostController
public function index(Request $req, Response $res): Response
{
$posts = $this->repo->allDesc();
$posts = $this->repo->allDesc(); // Post[]
return $this->view->render($res, 'pages/home.twig', ['posts' => $posts]);
}
@@ -51,9 +52,11 @@ class PostController
// Si id fourni mais post introuvable -> 404
if ($id > 0 && $post === null) {
return $res->withStatus(404)->write('Article non trouvé');
$res->getBody()->write('Article non trouvé');
return $res->withStatus(404);
}
// 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]);
}
@@ -62,12 +65,12 @@ class PostController
{
$postRequest = PostRequest::fromArray($req->getParsedBody());
if (! $postRequest->isValid()) {
// Simple gestion d'erreur : rediriger vers admin (on peut ajouter flash messages plus tard)
return $res->withHeader('Location', '/admin')->withStatus(302);
}
$data = $postRequest->validated();
$this->repo->create($data);
// Utilisation du helper toModel() pour construire l'entité Post
$post = $postRequest->toModel(0);
$this->repo->create($post);
return $res->withHeader('Location', '/admin')->withStatus(302);
}
@@ -76,7 +79,8 @@ class PostController
$id = (int)$args['id'];
$existing = $this->repo->find($id);
if ($existing === null) {
return $res->withStatus(404)->write('Article non trouvé');
$res->getBody()->write('Article non trouvé');
return $res->withStatus(404);
}
$postRequest = PostRequest::fromArray($req->getParsedBody());
@@ -84,8 +88,8 @@ class PostController
return $res->withHeader('Location', '/admin')->withStatus(302);
}
$data = $postRequest->validated();
$this->repo->update($id, $data);
$post = $postRequest->toModel($id);
$this->repo->update($id, $post);
return $res->withHeader('Location', '/admin')->withStatus(302);
}
@@ -94,7 +98,8 @@ class PostController
$id = (int)$args['id'];
$existing = $this->repo->find($id);
if ($existing === null) {
return $res->withStatus(404)->write('Article non trouvé');
$res->getBody()->write('Article non trouvé');
return $res->withStatus(404);
}
$this->repo->delete($id);

78
src/Models/Post.php Normal file
View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Models;
/**
* Représente un post (DTO / entité légère).
*
* 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.
*/
final class Post
{
private int $id;
private string $title;
private string $content;
/**
* @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;
}
/**
* Crée une instance depuis un tableau (par ex. ligne DB ou formulaire).
*
* Ce helper facilite la migration depuis le format tableau existant.
*
* @param array<string,mixed> $data Clé 'id' (optionnelle), 'title', 'content'
* @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'] : '';
return new self($id, $title, $content);
}
/** @return int */
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;
}
/**
* Retourne un tableau simple utile pour la persistance.
*
* @return array{title:string,content:string}
*/
public function toPersistableArray(): array
{
return [
'title' => $this->title,
'content' => $this->content,
];
}
}

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Repositories;
use App\Models\Post;
/**
* Interface décrivant les opérations sur les posts.
*/
@@ -12,7 +14,7 @@ interface PostRepositoryInterface
/**
* Retourne tous les posts triés par id descendant.
*
* @return array<int, array{id:int, title:string, content:string}>
* @return Post[] Tableau d'objets Post
*/
public function allDesc(): array;
@@ -20,26 +22,26 @@ interface PostRepositoryInterface
* Trouve un post par son id.
*
* @param int $id
* @return array{id:int, title:string, content:string}|null
* @return Post|null
*/
public function find(int $id): ?array;
public function find(int $id): ?Post;
/**
* Crée un post.
*
* @param array{title:string,content:string} $data
* @param Post $post Instance contenant les données à insérer (id ignoré)
* @return int id inséré
*/
public function create(array $data): int;
public function create(Post $post): int;
/**
* Met à jour un post.
*
* @param int $id
* @param array{title:string,content:string} $data
* @param Post $post Données à mettre à jour (id ignoré)
* @return void
*/
public function update(int $id, array $data): void;
public function update(int $id, Post $post): void;
/**
* Supprime un post.

View File

@@ -5,10 +5,12 @@ declare(strict_types=1);
namespace App\Repositories;
use Medoo\Medoo;
use App\Models\Post;
/**
* Repository pour "post" basé sur Medoo.
* Retourne et consomme des tableaux associatifs.
*
* Cette implémentation convertit les lignes DB en objets App\Models\Post.
*/
class PostRepositoryMedoo implements PostRepositoryInterface
{
@@ -25,19 +27,23 @@ class PostRepositoryMedoo implements PostRepositoryInterface
public function allDesc(): array
{
$rows = $this->db->select('post', ['id', 'title', 'content'], ['ORDER' => ['id' => 'DESC']]);
return is_array($rows) ? array_map(function ($r) {
return [
'id' => (int)($r['id'] ?? 0),
'title' => (string)($r['title'] ?? ''),
'content' => (string)($r['content'] ?? ''),
];
}, $rows) : [];
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): ?array
public function find(int $id): ?Post
{
$row = $this->db->get('post', ['id', 'title', 'content'], ['id' => $id]);
@@ -45,34 +51,31 @@ class PostRepositoryMedoo implements PostRepositoryInterface
return null;
}
return [
'id' => (int)($row['id'] ?? 0),
'title' => (string)($row['title'] ?? ''),
'content' => (string)($row['content'] ?? ''),
];
return new Post(
(int)($row['id'] ?? 0),
(string)($row['title'] ?? ''),
(string)($row['content'] ?? '')
);
}
/**
* @inheritDoc
*/
public function create(array $data): int
public function create(Post $post): int
{
$this->db->insert('post', [
'title' => $data['title'] ?? '',
'content' => $data['content'] ?? '',
]);
$data = $post->toPersistableArray();
$this->db->insert('post', $data);
return (int)$this->db->id();
}
/**
* @inheritDoc
*/
public function update(int $id, array $data): void
public function update(int $id, Post $post): void
{
$this->db->update('post', [
'title' => $data['title'] ?? '',
'content' => $data['content'] ?? '',
], ['id' => $id]);
$data = $post->toPersistableArray();
$this->db->update('post', $data, ['id' => $id]);
}
/**

View File

@@ -4,6 +4,8 @@ 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.
@@ -65,4 +67,17 @@ final class PostRequest
{
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);
}
}

View File

@@ -10,5 +10,4 @@
</div>
{% else %}
<p>Aucun article publié.</p>
{% endfor %}
{% endblock %}
{% endblock %}

View File

@@ -28,7 +28,7 @@
<p>
<label>Contenu<br>
<textarea id="editor" name="content" rows="6" {# required <- removed because conflicts with TinyMCE#}>{{ post.content|default('') }}</textarea>
<textarea id="editor" name="content" rows="6" >{{ post.content|default('') }}</textarea>
</label>
</p>
@@ -45,7 +45,7 @@
<script>
tinymce.init({
selector: '#editor',
base_url: '/js/tinymce', // assure le bon chemin relatif
base_url: '/js/tinymce',
license_key: 'gpl',
height: 400,
menubar: false,