This commit is contained in:
2026-04-02 16:33:32 +02:00
parent 72108d4d03
commit e79cc73e6d
17 changed files with 720 additions and 23 deletions

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Domain\Ingredients;
use App\Domain\Model;
class Ingredient extends Model {
public int $num_ingredient;
public string $nom_ingredient;
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Domain\Ingredients;
use App\Domain\Repository;
use App\Domain\Model;
class IngredientRepository extends Repository {
public static function getEntity(): string
{
return Ingredient::class;
}
public static function getStructure(): array
{
return [
'table' => 'Ingredient',
'columns' => [
'num_ingredient', 'nom_ingredient'
]
];
}
public function add( Model $ingredient ): bool {
}
}

8
src/Domain/Model.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
namespace App\Domain;
abstract class Model {
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Domain\Recettes;
use App\Domain\Model;
use App\Helpers\Markdown;
class Recette extends Model {
public int $num_recette;
public string $titre_recette;
public string $slug;
public string $description_recette;
public string $photo;
public string $publication_date;
public int $temps_de_preparation;
public function getHTMLDescription(): string {
return Markdown::convertToHTML( $this->description_recette );
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Domain\Recettes;
use App\Domain\Model;
use App\Domain\Repository;
class RecetteRepository extends Repository {
public static function getEntity(): string
{
return Recette::class;
}
public static function getStructure(): array
{
return [
'table' => 'Recette',
'columns' => [
'num_recette', 'titre_recette', 'slug', 'description_recette', 'photo', 'publication_date', 'temps_de_preparation'
]
];
}
/**
* Permet d'avoir une recette par un ID.
*
* @param int $id
* @return Recette|null
*/
public function getByID( int $id ): ?Recette {
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_recette = {$id}";
$results = $this->selectGetAll($sqlQuery);
if( $results === null || count( $results ) > 1 )
return null;
return $results[0];
}
/**
* Permet d'avoir une recette par un slug.
*
* @param string $slug
* @return Recette|null
*/
public function getBySlug( string $slug ): ?Recette {
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE slug = {$slug}";
$results = $this->selectGetAll($sqlQuery);
if( $results === null || count( $results ) > 1 )
return null;
return $results[0];
}
public function add( Model $recette ): bool {
return $this->addEntity( $recette );
}
public function update( Model $recette ): bool {
return $this->updateEntity( $recette, 'num_recette' );
}
public function delete( Model $recette ): bool {
return $this->deleteEntity( $recette, 'num_recette' );
}
}

99
src/Domain/Repository.php Normal file
View File

@@ -0,0 +1,99 @@
<?php
namespace App\Domain;
use App\Domain\Recettes\Recette;
use App\Kernel;
use PDO;
abstract class Repository {
/**
* Instancie des éléments de cette classe par rapport au repository.
* @return class-string
*/
abstract public static function getEntity(): string;
/**
* Doit retourner une liste du type :
* ['table' => '', 'columns' => [ ...Liste des colonnes dans la BDD ] ].
* @return array
*/
abstract public static function getStructure(): array;
final public string $tableName;
final public array $tableColumns;
public function __construct(){
$structure = static::getStructure();
$this->tableName = $structure['table'];
$this->tableColumns = $structure['columns'];
}
/**
* Permet d'avoir tous les éléments correspondant à la requête passée en paramètre.
*
* @param string $sqlQuery
* @return array|null
*/
public function selectGetAll( string $sqlQuery ): ?array {
$statement = Kernel::$DB->pdo->prepare( $sqlQuery );
if( !$statement->execute() )
return null;
$results = $statement->fetchAll( PDO::FETCH_CLASS, static::getEntity() );
if( empty( $results ) )
return null;
return $results;
}
public function addEntity( Model $entity ): bool {
$query = "INSERT INTO {$this->tableName} (";
$values = "(";
foreach( $this->tableColumns as $column ) {
$query .= "`{$column}`,";
$values .= ":{$column},";
}
$query = substr( $query, 0, -1 ) . ")";
$values = substr( $values, 0, -1 ) . ")";
$query .= " VALUES " . $values . ";";
$statement = Kernel::$DB->pdo->prepare( $query );
foreach( $this->tableColumns as $column ) {
$statement->bindValue(":{$column}", $entity->{$column} );
}
return $statement->execute();
}
public function updateEntity( Model $entity, string $identifier ): bool {
$query = "UPDATE {$this->tableName} SET ";
foreach( $this->tableColumns as $column ) {
$query .= "`{$column}` = :{$column},";
}
$query = substr( $query, 0, -1 ) . " WHERE {$identifier} = :{$identifier};";
$statement = Kernel::$DB->pdo->prepare( $query );
foreach( $this->tableColumns as $column ) {
$statement->bindValue(":{$column}", $entity->{$column} );
}
$statement->bindValue( ":{$identifier}", $entity->{$identifier} );
return $statement->execute();
}
public function deleteEntity( Model $entity, string $identifier ): bool {
$query = "DELETE FROM {$this->tableName} WHERE {$identifier} = :{$identifier};";
$statement = Kernel::$DB->pdo->prepare( $query );
$statement->bindValue( ":{$identifier}", $entity->{$identifier} );
return $statement->execute();
}
abstract public function add( Model $entity ): bool;
abstract public function update( Model $entity ): bool;
abstract public function delete( Model $entity ): bool;
}

View File

@@ -6,6 +6,7 @@ use App\Domain\Controller;
use App\Helpers\Authentification;
use App\Http\JSONResponse;
use App\Http\Request;
use App\Infrastructure\View;
class AuthentificationController extends Controller {
@@ -15,19 +16,29 @@ class AuthentificationController extends Controller {
// Public routes.
self::Route( routeUrl: '/login', routeName: 'login', routeAction: 'loginForm', pageHeadTitle: 'Connexion' ),
self::Route( routeUrl: '/logout', routeName: 'logout', routeAction: 'logoutPage', pageHeadTitle: 'Déconnexion' ),
// API Routes.
self::Route( routeUrl: '/api/auth', routeName: 'api->auth', routeAction: 'auth', routeMethods: ['POST'] ),
self::Route( routeUrl: '/api/auth/logout', routeName: 'api->auth->logout', routeAction: 'logout', routeMethods: ['POST'] ),
// self::Route( routeUrl: '/api/auth/logout', routeName: 'api->auth->logout', routeAction: 'logout', routeMethods: ['POST'] ),
];
}
public function login(): View {
public function loginForm(): View {
return new View( 'login' );
}
public function logoutPage(){
if( !Authentification::isLoggedIn() ) {
Request::redirectTo( 'home' );
}
Authentification::destroySession();
Request::redirectTo( 'home' );
}
public function auth(): JSONResponse {
Request::setCORS();
@@ -39,14 +50,14 @@ class AuthentificationController extends Controller {
$userId = 1;
Authentification::loginUser( $userId );
JSONResponse::sendSuccess( [ 'user_id' => $userId ] );
return JSONResponse::sendSuccess( [ 'user_id' => $userId ] );
}
public function logout(): JSONResponse {
if( !Authentification::isLoggedIn() ) {
return JSONResponse::sendError( [ 'message' => 'Alrady disconnected' ] );
return JSONResponse::sendError( [ 'message' => 'Already disconnected' ] );
}
Authentification::destroySession();

29
src/Helpers/Markdown.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
namespace App\Helpers;
class Markdown {
public static function getMarkdownEntities(): array {
return [
// Gras & Italique
'/\*\*(.*?)\*\*/' => '<b>$1</b>',
'/\*(.*?)\*/' => '<i>$1</i>',
// Titres
'/^## (.*?)$/m' => '<h3>$1</h3>',
'/^# (.*?)$/m' => '<h2>$1</h2>',
];
}
public static function convertToHTML( string $markdown ): string {
$safeMD = htmlspecialchars( $markdown, ENT_QUOTES );
foreach( Markdown::getMarkdownEntities() as $key => $value ) {
$safeMD = preg_replace( $key, $value, $safeMD );
}
return nl2br( $safeMD );
}
}

View File

@@ -50,4 +50,20 @@ class Request {
return self::sanitize( $_POST[$name] );
}
/**
* Permet de rediriger un utilisateur.
* /!\ Arrête tout traitement sur la page.
*
* @param string $routeName
* @param ...$args
*
* @return never
*/
public static function redirectTo( string $routeName, ...$args ): never {
$routeUrl = Router::getRouteURL( $routeName, ...$args );
header("Location: {$routeUrl}");
die();
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Infrastructure;
/**
* Permet de se connecter à la base de données et de faire le pont
* entre la classe PDO et nos actions.
*/
final class Database {
/**
* Contient les informations de connexion à notre BDD.
* @var array
*/
private array $databaseInfos;
/**
* Instance PDO.
* @var \PDO
*/
public private(set) \PDO $pdo;
/**
* Constructeur, lance la connexion.
* @param array $loginInfos ['name','host','port','user','pass']
*/
public function __construct( array $loginInfos ){
$this->databaseInfos = $loginInfos;
$this->tryLogin();
}
/**
* Permet d'essayer un login à la base de données.
* @return void - never si la connexion échoue.
*/
private function tryLogin(): void {
try {
$dsn = 'mysql:dbname=' . $this->databaseInfos['name'] . ';host=' . $this->databaseInfos['host'] . ';port=' . $this->databaseInfos['port'];
$this->pdo = new \PDO( $dsn, $this->databaseInfos['user'], $this->databaseInfos['pass'] );
} catch ( \PDOException $e ) {
die( $e->getMessage() );
}
}
}

View File

@@ -7,6 +7,7 @@ use App\Helpers\Authentification;
use App\Helpers\AutoLoader;
use App\Helpers\ConfigFactory;
use App\Http\Router;
use App\Infrastructure\Database;
/**
* Classe primaire du site.
@@ -22,6 +23,14 @@ final class Kernel {
*/
public private(set) static array $configs = [];
/**
* Instance à la base de données.
* Pour utiliser la classe PDO : $DB->PDO.
*
* @var Database|null
*/
public private(set) static ?Database $DB = null;
/**
* Instance actuelle de l'application.
* @var Kernel|null
@@ -62,6 +71,7 @@ final class Kernel {
public function init(): void {
$this->buildAutoloader();
$this->loadConfig();
$this->loadDatabase();
Authentification::startSession();
@@ -89,6 +99,7 @@ final class Kernel {
try {
self::$configs['general'] = ConfigFactory::loadConfigFile('general');
self::$configs['database'] = ConfigFactory::loadConfigFile('database');
self::$configs['route_arguments'] = ConfigFactory::loadConfigFile('route_arguments');
self::$configs['views'] = ConfigFactory::loadConfigFile('views');
@@ -97,4 +108,12 @@ final class Kernel {
}
}
/**
* Permet de se connecter à la base de données principale.
* @return void
*/
private function loadDatabase(): void {
self::$DB = new Database( self::$configs['database'] );
}
}