From eff05b0971b4efb912aadb07dd8d8ce3d97a0685 Mon Sep 17 00:00:00 2001 From: julien Date: Mon, 9 Mar 2026 14:17:05 +0100 Subject: [PATCH] Simplified --- .env.example | 10 +- composer.json | 5 +- public/index.php | 87 +++++++-- src/Controllers/PostController.php | 113 +++++++---- src/Factories/PostControllerFactory.php | 31 --- src/Factories/ServiceFactory.php | 94 --------- src/Models/Post.php | 140 +++++++++++--- src/Repositories/PostRepositoryInterface.php | 53 ----- src/Repositories/PostRepositoryMedoo.php | 88 --------- src/Requests/PostRequest.php | 83 -------- .../2c/2c6a63a35910781ed2464d3430983db2.php | 72 +++++++ .../3c/3c39c0e6430a5103c42c27b4478a8b72.php | 84 ++++++++ .../47/47cab4dc7982a59d8be24ee5dbfcb55a.php | 151 +++++++++++++++ .../5f/5feb7baabf556b5d769418b4ed8398c5.php | 158 +++++++++++++++ .../e0/e0b9aff4ba5a590eadfd3a590206e0b2.php | 182 ++++++++++++++++++ views/pages/admin.twig | 57 ++++-- views/pages/home.twig | 30 ++- views/pages/post_form.twig | 97 ++++------ 18 files changed, 1012 insertions(+), 523 deletions(-) delete mode 100644 src/Factories/PostControllerFactory.php delete mode 100644 src/Factories/ServiceFactory.php delete mode 100644 src/Repositories/PostRepositoryInterface.php delete mode 100644 src/Repositories/PostRepositoryMedoo.php delete mode 100644 src/Requests/PostRequest.php create mode 100644 var/cache/twig/2c/2c6a63a35910781ed2464d3430983db2.php create mode 100644 var/cache/twig/3c/3c39c0e6430a5103c42c27b4478a8b72.php create mode 100644 var/cache/twig/47/47cab4dc7982a59d8be24ee5dbfcb55a.php create mode 100644 var/cache/twig/5f/5feb7baabf556b5d769418b4ed8398c5.php create mode 100644 var/cache/twig/e0/e0b9aff4ba5a590eadfd3a590206e0b2.php diff --git a/.env.example b/.env.example index 9a09b4a..c2750ef 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,2 @@ -# Environnement de l'application +# Environnement de l'application (development ou production) APP_ENV=development - -# Chemin de cache Twig (laisser vide en développement pour désactiver le cache) -# Exemples : -# TWIG_CACHE=var/cache/twig -# TWIG_CACHE=/srv/www/myapp/var/cache/twig -TWIG_CACHE= - -# (Optionnel) autres variables d'environnement peuvent être ajoutées ici diff --git a/composer.json b/composer.json index 554cdd2..1bb45c4 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,7 @@ "slim/psr7": "*", "twig/twig": "*", "slim/twig-view": "^3.4", - "catfan/medoo": "2.*", - "vlucas/phpdotenv": "^5.6" + "catfan/medoo": "2.*" }, "autoload": { "psr-4": { @@ -26,4 +25,4 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.94" } -} +} \ No newline at end of file diff --git a/public/index.php b/public/index.php index 8efaf9f..175f214 100644 --- a/public/index.php +++ b/public/index.php @@ -6,28 +6,85 @@ require __DIR__ . '/../vendor/autoload.php'; use Slim\Factory\AppFactory; use Slim\Views\TwigMiddleware; +use Slim\Views\Twig; +use Medoo\Medoo; +use App\Controllers\PostController; -// Charger et créer les services centralisés -$services = App\Factories\ServiceFactory::createServices(); +// ============================================ +// Configuration +// ============================================ -/** @var \Slim\Views\Twig $twig */ -$twig = $services['view']; +$env = $_ENV['APP_ENV'] ?? 'production'; +$isDev = strtolower($env) === 'development'; + +// Dossier de cache Twig (false en dev, chemin en prod) +$twigCache = $isDev ? false : __DIR__ . '/../var/cache/twig'; +if ($twigCache && !is_dir($twigCache)) { + @mkdir($twigCache, 0755, true); +} + +// Chemin base de données +$dbFile = __DIR__ . '/../database/app.sqlite'; +$dbDir = dirname($dbFile); +if (!is_dir($dbDir)) { + @mkdir($dbDir, 0755, true); +} +if (!file_exists($dbFile)) { + @touch($dbFile); + @chmod($dbFile, 0664); +} + +// ============================================ +// Initialisation des services +// ============================================ + +// Twig +$twig = Twig::create( + __DIR__ . '/../views', + ['cache' => $twigCache] +); + +// Medoo (SQLite) +$db = new Medoo([ + 'type' => 'sqlite', + 'database' => $dbFile, +]); + +// Créer la table si elle n'existe pas +$db->pdo->exec(" + CREATE TABLE IF NOT EXISTS post ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + content TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) +"); + +// ============================================ +// Slim App +// ============================================ -// Slim app $app = AppFactory::create(); - -// Middlewares essentiels $app->addBodyParsingMiddleware(); $app->add(TwigMiddleware::create($app, $twig)); -// Charger les routes -$routesPath = __DIR__ . '/../src/Routes/web.php'; -if (file_exists($routesPath)) { - /** @var callable $routes */ - $routes = require $routesPath; - $routes($app); -} +// ============================================ +// Routes +// ============================================ -$errorMiddleware = $app->addErrorMiddleware(true, true, true); +$controller = new PostController($twig, $db); +$app->get('/', [$controller, 'index']); +$app->get('/admin', [$controller, 'admin']); +$app->get('/admin/edit/{id}', [$controller, 'form']); +$app->post('/admin/create', [$controller, 'create']); +$app->post('/admin/edit/{id}', [$controller, 'update']); +$app->post('/admin/delete/{id}', [$controller, 'delete']); + +// ============================================ +// Run +// ============================================ + +$errorMiddleware = $app->addErrorMiddleware($isDev, $isDev, $isDev); $app->run(); diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 81a4e17..f7fb1ed 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -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); } } diff --git a/src/Factories/PostControllerFactory.php b/src/Factories/PostControllerFactory.php deleted file mode 100644 index d6e9b2b..0000000 --- a/src/Factories/PostControllerFactory.php +++ /dev/null @@ -1,31 +0,0 @@ - $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); - } -} diff --git a/src/Factories/ServiceFactory.php b/src/Factories/ServiceFactory.php deleted file mode 100644 index 8e1f25c..0000000 --- a/src/Factories/ServiceFactory.php +++ /dev/null @@ -1,94 +0,0 @@ - Twig - * - 'postRepository' => PostRepositoryInterface - * - * @param array|null $overrides Permet d'injecter des remplacements pour les tests. - * @return array - */ - 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; - } -} - \ No newline at end of file diff --git a/src/Models/Post.php b/src/Models/Post.php index 0aa9ab7..1fbbe62 100644 --- a/src/Models/Post.php +++ b/src/Models/Post.php @@ -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 $data Clé 'id' (optionnelle), 'title', 'content' + * @param array $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} */ diff --git a/src/Repositories/PostRepositoryInterface.php b/src/Repositories/PostRepositoryInterface.php deleted file mode 100644 index 5d6b69e..0000000 --- a/src/Repositories/PostRepositoryInterface.php +++ /dev/null @@ -1,53 +0,0 @@ -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]); - } -} diff --git a/src/Requests/PostRequest.php b/src/Requests/PostRequest.php deleted file mode 100644 index 7803bb5..0000000 --- a/src/Requests/PostRequest.php +++ /dev/null @@ -1,83 +0,0 @@ -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); - } -} diff --git a/var/cache/twig/2c/2c6a63a35910781ed2464d3430983db2.php b/var/cache/twig/2c/2c6a63a35910781ed2464d3430983db2.php new file mode 100644 index 0000000..59752eb --- /dev/null +++ b/var/cache/twig/2c/2c6a63a35910781ed2464d3430983db2.php @@ -0,0 +1,72 @@ + + */ + private array $macros = []; + + public function __construct(Environment $env) + { + parent::__construct($env); + + $this->source = $this->getSourceContext(); + + $this->parent = false; + + $this->blocks = [ + ]; + } + + protected function doDisplay(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + // line 1 + yield "
+

+ Mon Blog | + Admin +

+
+"; + yield from []; + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "partials/_header.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 42 => 1,); + } + + public function getSourceContext(): Source + { + return new Source("", "partials/_header.twig", "/home/julien/Documents/Git/julien/blog-slim/views/partials/_header.twig"); + } +} diff --git a/var/cache/twig/3c/3c39c0e6430a5103c42c27b4478a8b72.php b/var/cache/twig/3c/3c39c0e6430a5103c42c27b4478a8b72.php new file mode 100644 index 0000000..301e915 --- /dev/null +++ b/var/cache/twig/3c/3c39c0e6430a5103c42c27b4478a8b72.php @@ -0,0 +1,84 @@ + + */ + private array $macros = []; + + public function __construct(Environment $env) + { + parent::__construct($env); + + $this->source = $this->getSourceContext(); + + $this->parent = false; + + $this->blocks = [ + ]; + } + + protected function doDisplay(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + // line 1 + yield "
+

© "; + // line 2 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatDate("now", "Y"), "html", null, true); + yield " Mon Blog – Tous droits réservés.

+ +
+"; + yield from []; + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "partials/_footer.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function isTraitable(): bool + { + return false; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 45 => 2, 42 => 1,); + } + + public function getSourceContext(): Source + { + return new Source("", "partials/_footer.twig", "/home/julien/Documents/Git/julien/blog-slim/views/partials/_footer.twig"); + } +} diff --git a/var/cache/twig/47/47cab4dc7982a59d8be24ee5dbfcb55a.php b/var/cache/twig/47/47cab4dc7982a59d8be24ee5dbfcb55a.php new file mode 100644 index 0000000..a2e0f25 --- /dev/null +++ b/var/cache/twig/47/47cab4dc7982a59d8be24ee5dbfcb55a.php @@ -0,0 +1,151 @@ + + */ + private array $macros = []; + + public function __construct(Environment $env) + { + parent::__construct($env); + + $this->source = $this->getSourceContext(); + + $this->blocks = [ + 'title' => [$this, 'block_title'], + 'content' => [$this, 'block_content'], + ]; + } + + protected function doGetParent(array $context): bool|string|Template|TemplateWrapper + { + // line 1 + return "layout.twig"; + } + + protected function doDisplay(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + $this->parent = $this->load("layout.twig", 1); + yield from $this->parent->unwrap()->yield($context, array_merge($this->blocks, $blocks)); + } + + // line 3 + /** + * @return iterable + */ + public function block_title(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + yield "Mon Blog"; + yield from []; + } + + // line 5 + /** + * @return iterable + */ + public function block_content(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + // line 6 + $context['_parent'] = $context; + $context['_seq'] = CoreExtension::ensureTraversable(($context["posts"] ?? null)); + $context['_iterated'] = false; + foreach ($context['_seq'] as $context["_key"] => $context["post"]) { + // line 7 + yield "
+

"; + // line 8 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "title", [], "any", false, false, false, 8), "html", null, true); + yield "

+ +
+ Publié le "; + // line 11 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatDate(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "createdAt", [], "any", false, false, false, 11), "d/m/Y à H:i"), "html", null, true); + yield " + "; + // line 12 + if ((($tmp = CoreExtension::getAttribute($this->env, $this->source, $context["post"], "isRecent", [7], "method", false, false, false, 12)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) { + // line 13 + yield " Nouveau + "; + } + // line 15 + yield "
+ +

"; + // line 17 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "getExcerpt", [200], "method", false, false, false, 17), "html", null, true); + yield "

+ +

+ env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "getSlug", [], "method", false, false, false, 20), "html", null, true); + yield "\">Lire la suite → +

+
+"; + $context['_iterated'] = true; + } + // line 23 + if (!$context['_iterated']) { + // line 24 + yield "

Aucun article publié.

+"; + } + $_parent = $context['_parent']; + unset($context['_seq'], $context['_key'], $context['post'], $context['_parent'], $context['_iterated']); + $context = array_intersect_key($context, $_parent) + $_parent; + yield from []; + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "pages/home.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function isTraitable(): bool + { + return false; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 114 => 24, 112 => 23, 104 => 20, 98 => 17, 94 => 15, 90 => 13, 88 => 12, 84 => 11, 78 => 8, 75 => 7, 70 => 6, 63 => 5, 52 => 3, 41 => 1,); + } + + public function getSourceContext(): Source + { + return new Source("", "pages/home.twig", "/home/julien/Documents/Git/julien/blog-slim/views/pages/home.twig"); + } +} diff --git a/var/cache/twig/5f/5feb7baabf556b5d769418b4ed8398c5.php b/var/cache/twig/5f/5feb7baabf556b5d769418b4ed8398c5.php new file mode 100644 index 0000000..f513208 --- /dev/null +++ b/var/cache/twig/5f/5feb7baabf556b5d769418b4ed8398c5.php @@ -0,0 +1,158 @@ + + */ + private array $macros = []; + + public function __construct(Environment $env) + { + parent::__construct($env); + + $this->source = $this->getSourceContext(); + + $this->parent = false; + + $this->blocks = [ + 'title' => [$this, 'block_title'], + 'content' => [$this, 'block_content'], + 'scripts' => [$this, 'block_scripts'], + ]; + } + + protected function doDisplay(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + // line 1 + yield " + + + + "; + // line 5 + yield from $this->unwrap()->yieldBlock('title', $context, $blocks); + yield " + "; + // line 7 + yield " + + + + "; + // line 17 + yield " "; + yield from $this->load("partials/_header.twig", 17)->unwrap()->yield($context); + // line 18 + yield " + "; + // line 20 + yield "
+ "; + // line 21 + yield from $this->unwrap()->yieldBlock('content', $context, $blocks); + // line 22 + yield "
+ + "; + // line 25 + yield " "; + yield from $this->load("partials/_footer.twig", 25)->unwrap()->yield($context); + // line 26 + yield " + "; + // line 28 + yield " "; + // line 29 + yield " "; + yield from $this->unwrap()->yieldBlock('scripts', $context, $blocks); + // line 30 + yield " + + +"; + yield from []; + } + + // line 5 + /** + * @return iterable + */ + public function block_title(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + yield "Mon Blog"; + yield from []; + } + + // line 21 + /** + * @return iterable + */ + public function block_content(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + yield from []; + } + + // line 29 + /** + * @return iterable + */ + public function block_scripts(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + yield from []; + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "layout.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function isTraitable(): bool + { + return false; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 121 => 29, 111 => 21, 100 => 5, 92 => 30, 89 => 29, 87 => 28, 84 => 26, 81 => 25, 77 => 22, 75 => 21, 72 => 20, 69 => 18, 66 => 17, 55 => 7, 51 => 5, 45 => 1,); + } + + public function getSourceContext(): Source + { + return new Source("", "layout.twig", "/home/julien/Documents/Git/julien/blog-slim/views/layout.twig"); + } +} diff --git a/var/cache/twig/e0/e0b9aff4ba5a590eadfd3a590206e0b2.php b/var/cache/twig/e0/e0b9aff4ba5a590eadfd3a590206e0b2.php new file mode 100644 index 0000000..9247b7c --- /dev/null +++ b/var/cache/twig/e0/e0b9aff4ba5a590eadfd3a590206e0b2.php @@ -0,0 +1,182 @@ + + */ + private array $macros = []; + + public function __construct(Environment $env) + { + parent::__construct($env); + + $this->source = $this->getSourceContext(); + + $this->blocks = [ + 'title' => [$this, 'block_title'], + 'content' => [$this, 'block_content'], + ]; + } + + protected function doGetParent(array $context): bool|string|Template|TemplateWrapper + { + // line 1 + return "layout.twig"; + } + + protected function doDisplay(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + $this->parent = $this->load("layout.twig", 1); + yield from $this->parent->unwrap()->yield($context, array_merge($this->blocks, $blocks)); + } + + // line 3 + /** + * @return iterable + */ + public function block_title(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + yield "Admin – Gestion des articles"; + yield from []; + } + + // line 5 + /** + * @return iterable + */ + public function block_content(array $context, array $blocks = []): iterable + { + $macros = $this->macros; + // line 6 + yield "

Gestion des articles

+ + +

+ + Ajouter un article +

+ +"; + // line 13 + if ((($tmp = !Twig\Extension\CoreExtension::testEmpty(($context["posts"] ?? null))) && $tmp instanceof Markup ? (string) $tmp : $tmp)) { + // line 14 + yield " + + + + + + + + + + "; + // line 24 + $context['_parent'] = $context; + $context['_seq'] = CoreExtension::ensureTraversable(($context["posts"] ?? null)); + foreach ($context['_seq'] as $context["_key"] => $context["post"]) { + // line 25 + yield " + + + + + + "; + } + $_parent = $context['_parent']; + unset($context['_seq'], $context['_key'], $context['post'], $context['_parent']); + $context = array_intersect_key($context, $_parent) + $_parent; + // line 46 + yield " +
TitreCréé leModifié leActions
+ "; + // line 27 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "title", [], "any", false, false, false, 27), "html", null, true); + yield " + "; + // line 28 + if ((($tmp = CoreExtension::getAttribute($this->env, $this->source, $context["post"], "isRecent", [7], "method", false, false, false, 28)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) { + // line 29 + yield " Nouveau + "; + } + // line 31 + yield " "; + // line 32 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatDate(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "createdAt", [], "any", false, false, false, 32), "d/m/Y H:i"), "html", null, true); + yield ""; + // line 33 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatDate(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "updatedAt", [], "any", false, false, false, 33), "d/m/Y H:i"), "html", null, true); + yield " + env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "id", [], "any", false, false, false, 35), "html", null, true); + yield "\" class=\"btn btn-sm btn-secondary\">Éditer + +
env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["post"], "id", [], "any", false, false, false, 37), "html", null, true); + yield "\" style=\"display:inline;\"> + +
+
+"; + } else { + // line 49 + yield "

Aucun article à gérer.

+"; + } + yield from []; + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "pages/admin.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function isTraitable(): bool + { + return false; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 148 => 49, 143 => 46, 128 => 37, 123 => 35, 118 => 33, 114 => 32, 111 => 31, 107 => 29, 105 => 28, 101 => 27, 97 => 25, 93 => 24, 81 => 14, 79 => 13, 70 => 6, 63 => 5, 52 => 3, 41 => 1,); + } + + public function getSourceContext(): Source + { + return new Source("", "pages/admin.twig", "/home/julien/Documents/Git/julien/blog-slim/views/pages/admin.twig"); + } +} diff --git a/views/pages/admin.twig b/views/pages/admin.twig index b4fe578..c97dc95 100644 --- a/views/pages/admin.twig +++ b/views/pages/admin.twig @@ -3,28 +3,49 @@ {% block title %}Admin – Gestion des articles{% endblock %} {% block content %} -

Gestion des articles

+

Gestion des articles

- - + Ajouter un article + +

+ + Ajouter un article +

- {% for post in posts %} -
-

{{ post.title }}

-

{{ post.content|raw }}

- -
- Éditer +{% if posts is not empty %} + + + + + + + + + + + {% for post in posts %} + + + + + + + {% endfor %} + +
TitreCréé leModifié leActions
+ {{ post.title }} + {% if post.isRecent(7) %} + Nouveau + {% endif %} + {{ post.createdAt|date("d/m/Y H:i") }}{{ post.updatedAt|date("d/m/Y H:i") }} + Éditer
-
- - - {% else %} -

Aucun article à gérer.

- {% endfor %} -{% endblock %} +
+{% else %} +

Aucun article à gérer.

+{% endif %} +{% endblock %} \ No newline at end of file diff --git a/views/pages/home.twig b/views/pages/home.twig index 128701e..af344cc 100644 --- a/views/pages/home.twig +++ b/views/pages/home.twig @@ -3,12 +3,24 @@ {% block title %}Mon Blog{% endblock %} {% block content %} - {% for post in posts %} -
-

{{ post.title }}

-

{{ post.content|raw }}

-
- {% else %} -

Aucun article publié.

- {% endfor %} -{% endblock %} +{% for post in posts %} +
+

{{ post.title }}

+ + + +

{{ post.getExcerpt(200) }}

+ +

+ Lire la suite → +

+
+{% else %} +

Aucun article publié.

+{% endfor %} +{% endblock %} \ No newline at end of file diff --git a/views/pages/post_form.twig b/views/pages/post_form.twig index ca1bd14..6c3b8f6 100644 --- a/views/pages/post_form.twig +++ b/views/pages/post_form.twig @@ -1,68 +1,55 @@ {% extends "layout.twig" %} {% block title %} - {% if post is defined and post is not null and post.id is defined %} - Éditer l’article - {% else %} - Créer un article - {% endif %} +{% if post is defined and post is not null and post.id > 0 %} +Éditer l'article +{% else %} +Créer un article +{% endif %} {% endblock %} {% block content %} -

- {% if post is defined and post is not null and post.id is defined %} - Éditer l’article - {% else %} - Créer un article - {% endif %} -

- {# Formulaire identifié pour le script JavaScript #} -
-

- -

+{% if post is defined and post is not null and post.id > 0 %} +Éditer l'article +{% else %} +Créer un article +{% endif %} -

- -

+{# Formulaire identifié pour le script JavaScript #} + +

+ +

- -
+ Annuler +

+ + +{% if post is defined and post is not null and post.id > 0 %} +
+ + Créé le : {{ post.createdAt|date("d/m/Y à H:i") }}
+ Modifié le : {{ post.updatedAt|date("d/m/Y à H:i") }} +
+{% endif %} -

Retour à l’admin

{% endblock %} -{% block scripts %} - - -{% endblock %} +{% block scripts %} \ No newline at end of file