first commit

This commit is contained in:
julien
2026-02-22 14:36:40 +01:00
commit ebcd2f007f
11 changed files with 319 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
composer.lock
database/
vendor/

1
README.md Normal file
View File

@@ -0,0 +1 @@
# blog-slim

15
composer.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "user/blog-slim",
"require": {
"slim/slim": "4.*",
"slim/psr7": "^1.8",
"illuminate/database": "^12.52",
"twig/twig": "^3.23",
"slim/twig-view": "^3.4"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}

36
migrate.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use Illuminate\Database\Capsule\Manager as Capsule;
// ---------------------------------------------------------------------
// Connexion SQLite on utilise un chemin absolu (realpath) afin
// d'éviter le helper Laravel `base_path()` qui n'est pas disponible.
// ---------------------------------------------------------------------
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'sqlite',
// __DIR__ pointe sur le répertoire racine du projet
// le fichier SQLite se trouve dans le sousdossier `database`
'database' => realpath(__DIR__ . '/database/blog.sqlite'),
'prefix' => '',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
// ---------------------------------------------------------------------
// Création de la table `posts` si elle n'existe pas déjà
// ---------------------------------------------------------------------
if (!Capsule::schema()->hasTable('posts')) {
Capsule::schema()->create('posts', function ($table) {
$table->increments('id'); // clé primaire autoincrémentée
$table->string('title'); // titre de l'article
$table->text('content'); // contenu de l'article
});
echo "Table 'posts' créée avec succès.\n";
} else {
echo "La table 'posts' existe déjà.\n";
}

58
public/index.php Normal file
View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
use Slim\Factory\AppFactory;
use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
use Illuminate\Database\Capsule\Manager as Capsule;
require __DIR__ . '/../vendor/autoload.php';
/* -------------------------------------------------
Instanciation de lapplication Slim
------------------------------------------------- */
$app = AppFactory::create();
/* -------------------------------------------------
Middleware derreur (affiche les exceptions)
------------------------------------------------- */
$app->addErrorMiddleware(true, true, true);
/* -------------------------------------------------
Twig (templates)
------------------------------------------------- */
$twig = Twig::create(__DIR__ . '/../views', ['cache' => false]);
$app->add(TwigMiddleware::create($app, $twig));
/* -------------------------------------------------
Vérification / création du fichier SQLite
------------------------------------------------- */
$dbFile = __DIR__ . '/../database/blog.sqlite';
if (!file_exists($dbFile)) {
// crée un fichier vide et lui donne les permissions décriture
touch($dbFile);
chmod($dbFile, 0664);
}
/* -------------------------------------------------
Eloquent (connexion SQLite)
------------------------------------------------- */
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'sqlite',
// le chemin relatif fonctionne ; SQLite créera le fichier si besoin
'database' => $dbFile,
'prefix' => '',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
/* -------------------------------------------------
Chargement des routes
------------------------------------------------- */
(require __DIR__ . '/../src/routes.php')($app);
/* -------------------------------------------------
Démarrage de lapplication
------------------------------------------------- */
$app->run();

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

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Models; // ← namespace attendu par routes.php
use Illuminate\Database\Eloquent\Model;
/**
* Modèle Eloquent représentant un article.
*/
class Post extends Model
{
protected $table = 'posts';
protected $fillable = ['title', 'content'];
public $timestamps = false;
}

94
src/routes.php Normal file
View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
use Slim\App;
use Slim\Psr7\Request;
use Slim\Psr7\Response;
use App\Models\Post;
use Slim\Views\Twig;
return function (App $app) {
// -------------------------------------------------
// Page publique liste des articles
// -------------------------------------------------
$app->get('/', function (Request $request, Response $response) use ($app) {
$posts = Post::orderByDesc('id')->get();
/** @var Twig $view */
$view = $request->getAttribute('view'); // <-- récupération correcte
return $view->render($response, 'posts.twig', ['posts' => $posts]);
});
// -------------------------------------------------
// Page admin tableau de bord
// -------------------------------------------------
$app->get('/admin', function (Request $request, Response $response) use ($app) {
$posts = Post::orderByDesc('id')->get();
/** @var Twig $view */
$view = $request->getAttribute('view');
return $view->render($response, 'admin.twig', ['posts' => $posts]);
});
// -------------------------------------------------
// Création d'un article (POST depuis admin)
// -------------------------------------------------
$app->post('/admin/create', function (Request $request, Response $response) {
$data = $request->getParsedBody();
Post::create([
'title' => $data['title'],
'content' => $data['content'],
]);
return $response
->withHeader('Location', '/admin')
->withStatus(302);
});
// -------------------------------------------------
// Formulaire d'édition (GET depuis admin)
// -------------------------------------------------
$app->get('/admin/edit/{id}', function (Request $request, Response $response, $args) use ($app) {
$id = (int)$args['id'];
$post = $id ? Post::findOrFail($id) : null; // id=0 → création
/** @var Twig $view */
$view = $request->getAttribute('view');
return $view->render($response, 'post_form.twig', [
'action' => $id ? "/admin/edit/{$id}" : "/admin/create",
'post' => $post,
]);
});
// -------------------------------------------------
// Enregistrement des modifications (POST depuis admin)
// -------------------------------------------------
$app->post('/admin/edit/{id}', function (Request $request, Response $response, $args) {
$post = Post::findOrFail($args['id']);
$data = $request->getParsedBody();
$post->update([
'title' => $data['title'],
'content' => $data['content'],
]);
return $response
->withHeader('Location', '/admin')
->withStatus(302);
});
// -------------------------------------------------
// Suppression d'un article (POST depuis admin)
// -------------------------------------------------
$app->post('/admin/delete/{id}', function (Request $request, Response $response, $args) {
$post = Post::findOrFail($args['id']);
$post->delete();
return $response
->withHeader('Location', '/admin')
->withStatus(302);
});
};

30
views/admin.twig Normal file
View File

@@ -0,0 +1,30 @@
{% extends "layout.twig" %}
{% block title %}Admin Gestion des articles{% endblock %}
{% block content %}
<h2>Gestion des articles</h2>
<!-- Lien dajout -->
<a href="/admin/edit/0">+ Ajouter un article</a>
{% for post in posts %}
<div class="post">
<h3>{{ post.title }}</h3>
<p>{{ post.content|nl2br }}</p>
<div class="admin-actions">
<a href="/admin/edit/{{ post.id }}">Éditer</a>
<form method="post" action="/admin/delete/{{ post.id }}" style="display:inline;">
<button type="submit"
onclick="return confirm('Supprimer cet article?')">
Supprimer
</button>
</form>
</div>
</div>
{% else %}
<p>Aucun article à gérer.</p>
{% endfor %}
{% endblock %}

21
views/layout.twig Normal file
View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>{% block title %}Mon Blog{% endblock %}</title>
<style>
body {font-family: Arial, sans-serif; margin: 2rem;}
.post {border-bottom: 1px solid #ccc; padding: 1rem 0;}
.admin-actions a,
.admin-actions form {margin-right: .5rem;}
</style>
</head>
<body>
<h1>
<a href="/">Mon Blog</a> |
<a href="/admin">Admin</a>
</h1>
{% block content %}{% endblock %}
</body>
</html>

31
views/post_form.twig Normal file
View File

@@ -0,0 +1,31 @@
{% extends "layout.twig" %}
{% block title %}
{% if post is defined %}Éditer larticle{% else %}Créer un article{% endif %}
{% endblock %}
{% block content %}
<h2>
{% if post is defined %}Éditer{% else %}Créer{% endif %} un article
</h2>
<form method="post" action="{{ action }}">
<p>
<label>Titre<br>
<input type="text" name="title"
value="{{ post.title|default('') }}" required>
</label>
</p>
<p>
<label>Contenu<br>
<textarea name="content" rows="6" required>{{ post.content|default('') }}</textarea>
</label>
</p>
<button type="submit">
{% if post is defined %}Mettre à jour{% else %}Enregistrer{% endif %}
</button>
</form>
<p><a href="/admin">Retour à ladmin</a></p>
{% endblock %}

14
views/posts.twig Normal file
View File

@@ -0,0 +1,14 @@
{% extends "layout.twig" %}
{% block title %}Articles{% endblock %}
{% block content %}
{% for post in posts %}
<div class="post">
<h2>{{ post.title }}</h2>
<p>{{ post.content|nl2br }}</p>
</div>
{% else %}
<p>Aucun article publié.</p>
{% endfor %}
{% endblock %}