ajoutUtilisateurs

This commit is contained in:
Aco
2026-04-03 12:21:09 +02:00
parent b1f3806983
commit 7b886fef86
67 changed files with 1944 additions and 933 deletions

View File

@@ -1,3 +1,57 @@
# Les Recettes des Papis
# Les recettes des Papis
modif sur la partie bd jusqu'au 30/03/26 a 15h
## Installation
- Décompressez l'archive du site.
- Pointez votre domaine sur le dossier "public".
- Modifiez les fichiers de configuration dans "config".
## Informations pratiques sur le PHP.
- Ce site utilise le principe des routes. Afin de ne pas avoir x fichiers publiques identiques pour chaque page.
- Tout est géré par le fichier public/index.php.
- index.php va charger le Kernel (Noyau) de l'application.
- Le noyau fonctionne sur le principe d'élément unique. La méthode ``Kernel::getInstance()`` va permettre d'avoir l'unique instance de la classe Kernel.
- Une fois ``Kernel::getInstance()->init()`` déclenché, le noyau enregistre l'autoloading des fichiers, puis enregistre la configuration.
- La configuration sont des fichiers dans le dossier config/ qui vont renvoyer un tableau clé/valeur. Exemple
```php
// Fichier config/test.php
<?php return [
'cle_1' => "Val1",
'cle_2' => "Val2",
// ...
] ?>
```
- Les fichiers de configuration sont chargés via la méthode ``ConfigFactory::loadConfigFile()`` qui va vérifier si le fichier de configuration existe et le charge en conséquence.
- On enchaine ensuite sur le Router.
- Le Router est une classe utilitaire qui va permettre de faire le lien entre la route saisie ``/test/...`` de l'utilisateur et les routes enregistrés par le site.
- Le Router va d'abord commencer par récupérer la route voulue par l'utilisateur.
- Puis, le router va rechercher tous les Controllers existants.
- Un Controller est une classe qui va permettre de définir les routes et de dire, qu'est-ce qui se passe quand je prends cette route. Les routes du Controller sont définis par la methode ``Controller::defineRoutes()``
- Pour définir les routes dans la méthode précédente, vous pouvez soit créer un objet ``Route`` en remplissant tous les champs, ou bien la méthode ``Controller::Route()`` qui va préremplir certains champs.
- Exemple de Controller
```php
class TestController extends Controller {
public static function defineRoutes(): array {
return [
self::Route( routeUrl: '/test', routeName: "Test", routeAction: "test" ),
new Route( routeUrl: "/test2", routeName: "Test2", routeController: "TestController", routeAction: "test2", routeMethods = [ 'GET' ] )
];
}
public function test(){
echo "Je suis déclenché lorsque j'atteinds la route /test";
}
public function test2(){
echo "Je suis déclenché lorsque j'atteinds la route /test2 !!!";
}
}
```
- Retournons au routeur, pour chercher les controllers, il va scanner les fichiers du dossier src/Domain avec un Iterator Récursif. (Pour se simplifier un peu la tâche.)
- Il va traiter le nom du chemin absolu pour obtenir le nom de la classe, puis va vérifier si cette classe est une sous-classe de Controller.
- Après avoir trouvé tous les Controllers, il va récupérer toutes les routes de chaque controller.
- Enfin, il va vérifier si la route utilisateur correspond à une des routes dans sa liste, si ça correspond, le Router va charger la méthode ``new {RouteController}()->{RouteAction}()``
- Si par exemple, le router trouve la route ``/test`` voulu par l'utilisateur, il va déclencher la méthode ``new TestController()->test()``.

View File

@@ -1,44 +0,0 @@
<?php
$db_name = "gamesdb" ; // Nom de la base de données (pré-existante)
$db_host = "127.0.0.1" ; // Si le serveur MySQL est sur la machine locale
$db_port = "3306" ; // Port par défaut de MySQL
// Informations d'authentification de votre script PHP
$db_user = "gameuse" ; // Utilisateur par défaut de MySQL (... à changer)
$db_pwd = "MyStrongPassword123!" ; // Mot de passe par défaut pour l'utilisateur root (.. à changer !!!)
//GRANT ALL PRIVILEGES ON gamesdb.* TO 'gameuse'@'localhost';
//gameuse'@'localhost'
//MyStrongPassword123
// Connexion à la BDD
//j'ai fabriquer cet utilisateur spécialement pour cette exo sur le serv
//mysql
// Connexion à la BDD
// Connexion à la BDD
try{
// Agrégation des informations de connexion dans une chaine DSN (Data Source Name)
$dsn = 'mysql:dbname=' . $db_name . ';host='. $db_host. ';port=' . $db_port;
// Connexion et récupération de l'objet connecté
$pdo = new PDO($dsn, $db_user, $db_pwd);
}
// Récupération d'une éventuelle erreur
catch (\Exception $ex){ ?>
<!-- Affichage des informations liées à l'erreur-->
<div style="color: red">
<b>!!! ERREUR DE CONNEXION !!!</b><br>
Code : <?= $ex->getCode() ?><br>
Message : <?= $ex->getMessage() ?>
</div><?php
// Arrêt de l'exécution du script PHP
die("-> Exécution stoppée <-") ;
}
// Poursuite de l'exécution du script ?>
<div style="color: green">Connecté à <b><?= $dsn ?></b></div> <?php ?>

View File

@@ -1,156 +0,0 @@
-- MySQL dump 10.13 Distrib 5.5.27, for Win64 (x86)
--
-- Host: localhost Database: cinema
-- ------------------------------------------------------
-- Server version 5.5.27
-- note pour l'execution, 'sur mon ordinateur personnel' dans le terminal
-- sudo mysql --local-infile=1 -p < codemysql/basedonneee.sql (obliger de sudo pour que ca marche sur mon ordi perso)
-- et aussi sudo mysql -p pour juste lancer
-- comment faire tourner mon code sur workbench
-- j'ai modifier les chemins des loads data
-- pour utiliser un chemin relatif* (car chemin relatif ne marche pas dans
-- workbench, mais fonctionne dans les fichiers de script. (il doit y avoir une option pour sur WB)
DROP DATABASE IF EXISTS siterecette;
CREATE DATABASE siterecette;
DROP USER IF EXISTS 'admin'@'localhost';
CREATE USER 'admin'@'localhost' IDENTIFIED BY 'adminpass123';
GRANT ALL PRIVILEGES ON siterecette.* TO 'admin'@'localhost';
USE siterecette;
--
-- Table structure for table acteur
--
DROP TABLE IF EXISTS Recette;
CREATE TABLE Recette (
num_recette INT NOT NULL,
titre_recette VARCHAR(60) NOT NULL,
slug VARCHAR(200) NOT NULL,
description_recette VARCHAR(1000) NOT NULL,
photo VARCHAR(200) NOT NULL,
publication_date DATE NOT NULL,
temps_de_preparation INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table structure for table realisateur
--
DROP TABLE IF EXISTS Tag;
CREATE TABLE Tag (
num_tag INT NOT NULL,
nom_tag VARCHAR(30) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table structure for table salle
--
DROP TABLE IF EXISTS Ingredient;
CREATE TABLE Ingredient (
num_ingredient INT NOT NULL,
nom_ingredient INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table structure for table film
--
DROP TABLE IF EXISTS Listeingredient;
CREATE TABLE Listeingredient (
num_recette INT NOT NULL,
num_ingredient INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table structure for table projection
--
DROP TABLE IF EXISTS Referencetag;
CREATE TABLE Referencetag (
num_recette INT NOT NULL,
num_tag INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
DROP TABLE IF EXISTS User;
CREATE TABLE User (
num_user INT NOT NULL,
username VARCHAR(20) NOT NULL,
userpassword VARCHAR(60) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- CONTRAINTES
--
-- en utilisant les 'alter table' sinon on peut directement les specifier dans les create table
ALTER TABLE Recette
ADD CONSTRAINT pk_recette PRIMARY KEY (num_recette);
ALTER TABLE Ingredient
ADD CONSTRAINT pk_ingredient PRIMARY KEY (num_ingredient);
ALTER TABLE Tag
ADD CONSTRAINT pk_tag PRIMARY KEY (num_tag);
ALTER TABLE User
ADD CONSTRAINT pk_user PRIMARY KEY (num_user);
ALTER TABLE Listeingredient
ADD CONSTRAINT fk_numrecette_ingredient FOREIGN KEY (num_recette) REFERENCES Recette (num_recette),
ADD CONSTRAINT fk_recette_numingredient FOREIGN KEY (num_ingredient) REFERENCES Ingredient (num_ingredient);
ALTER TABLE Referencetag
ADD CONSTRAINT fk_numrecette_tag FOREIGN KEY (num_recette) REFERENCES Recette (num_recette),
ADD CONSTRAINT fk_recette_numtag FOREIGN KEY (num_tag) REFERENCES Tag (num_tag);
-- si besoin exemple de chargement csv
-- LOAD DATA LOCAL INFILE 'nom.csv'
-- INTO TABLE nom
-- FIELDS TERMINATED BY ','
-- ENCLOSED BY '"'
-- LINES TERMINATED BY '\n'
-- IGNORE 1 ROWS;
-- les chemin ./filename.csv ne marche que dans le terminal avec le cli mysql via la commande $
-- select * from column;
INSERT INTO Recette (num_recette,titre_recette,slug,description_recette,photo,publication_date,temps_de_preparation)
values(1,"nomdrecette","slugdpage","descriptiondrecette","urldephoto",'2023-12-31',500),
(2,"nomdrecette","slugdpage","descriptiondrecette","urldephoto",'2023-12-31',500),
(3,"nomdrecette","slugdpage","descriptiondrecette","urldephoto",'2023-12-31',500);
-- select * from Recette;
-- INSERT INTO Tag (num_tag,nom_tag)
-- values(1,"nomdtag");
-- INSERT INTO Referencetag(num_recette, num_tag)
-- values(1,1);
-- INSERT INTO Ingredient(num_ingredient,nom_ingredient)
-- values(2,2);
-- INSERT INTO Listeingredient(num_recette,num_ingredient)
-- values(1,2);

View File

@@ -1,16 +0,0 @@
quand on créer une recette
(le but c'est que quand on en créer une
on est en même temps au moins une instance d'association
qui enregistre cette recette et ses ingrédients simultanément
de manière obligatoire)
idée 1 : on ignore l'obligation d'associer une liste d'ingredients a une recette à sa création.
idée 2 : les ingredients d'une liste d'ingredients qui est associés a une recette sont représenter comme une suite de caractère en une ligne séparer par des espace ce qui ferait un seul élément respectant la normalisation 2NF
idée 3 : ajouter des règles de contraintes à la créations d'une recette par exemple quand on créer une recette il faut que ...
soit dans la base de données soit juste dans le backend
idée ... :

View File

@@ -1,45 +0,0 @@
recettes ID
Titre
Slug
Description
Photo
Date d'ajout
liste-recettes (entités)
-Id listerecettes
Recette-Ingrédients
ID_Recette
ID_Ingrédient
Recette-Tags
ID_Recette
ID_Tag
Ingrédients
ID
Nom
Image
Tag
ID
Nom
Utilisateurs
ID
Username
MDP (Hashé, bien entendu...)
. Permission (utilisateurs)

View File

@@ -1,103 +0,0 @@
<mxfile host="65bd71144e">
<diagram id="i3f546belnF2-wjsbEOy" name="Page-1">
<mxGraphModel dx="1489" dy="755" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="Recette" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;html=1;" vertex="1" parent="1">
<mxGeometry x="300" y="232" width="170" height="182" as="geometry"/>
</mxCell>
<mxCell id="3" value="(PK)idrecette" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="2">
<mxGeometry y="26" width="170" height="26" as="geometry"/>
</mxCell>
<mxCell id="4" value="titrerecette varchar(60)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="2">
<mxGeometry y="52" width="170" height="26" as="geometry"/>
</mxCell>
<mxCell id="14" value="slug varchar(200) (idweb)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="2">
<mxGeometry y="78" width="170" height="26" as="geometry"/>
</mxCell>
<mxCell id="15" value="Description varchar(1000)&amp;nbsp;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="2">
<mxGeometry y="104" width="170" height="26" as="geometry"/>
</mxCell>
<mxCell id="16" value="photo (url) varchar(200)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="2">
<mxGeometry y="130" width="170" height="26" as="geometry"/>
</mxCell>
<mxCell id="17" value="publicationdate DATE" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="2">
<mxGeometry y="156" width="170" height="26" as="geometry"/>
</mxCell>
<mxCell id="6" value="Referencetag" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;html=1;" vertex="1" parent="1">
<mxGeometry x="140" y="320" width="111" height="78" as="geometry"/>
</mxCell>
<mxCell id="8" value="(FK)idrecette" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="6">
<mxGeometry y="26" width="111" height="26" as="geometry"/>
</mxCell>
<mxCell id="9" value="(FK)idtag" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="6">
<mxGeometry y="52" width="111" height="26" as="geometry"/>
</mxCell>
<mxCell id="10" value="Tag" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;html=1;" vertex="1" parent="1">
<mxGeometry x="-60" y="340" width="121" height="78" as="geometry"/>
</mxCell>
<mxCell id="11" value="(PK)idtag" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="10">
<mxGeometry y="26" width="121" height="26" as="geometry"/>
</mxCell>
<mxCell id="19" value="nomtag varchar(30)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="10">
<mxGeometry y="52" width="121" height="26" as="geometry"/>
</mxCell>
<mxCell id="23" value="" style="endArrow=none;html=1;exitX=1;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="10" target="6">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="360" y="410" as="sourcePoint"/>
<mxPoint x="410" y="360" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="24" value="" style="endArrow=none;html=1;entryX=0;entryY=0;entryDx=0;entryDy=0;exitX=1;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="6" target="2">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="200" y="300" as="sourcePoint"/>
<mxPoint x="290" y="240" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="28" value="1,n" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="50" y="334" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="31" value="1,1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="250" y="238" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="338" value="" style="endArrow=none;html=1;exitX=0.982;exitY=1.115;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" edge="1" parent="1" source="354" target="2">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="350" y="80" as="sourcePoint"/>
<mxPoint x="310" y="250" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="339" value="1,1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="280" y="202" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="341" value="" style="endArrow=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=-0.018;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="354" target="342">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="-5" y="90" as="sourcePoint"/>
<mxPoint x="60" y="130" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="342" value="Ingredient" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;html=1;" vertex="1" parent="1">
<mxGeometry x="-40" y="190" width="121" height="78" as="geometry"/>
</mxCell>
<mxCell id="343" value="(PK)idingredient" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="342">
<mxGeometry y="26" width="121" height="26" as="geometry"/>
</mxCell>
<mxCell id="344" value="nomtag varchar(30)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="342">
<mxGeometry y="52" width="121" height="26" as="geometry"/>
</mxCell>
<mxCell id="345" value="1,n" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry y="160" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="351" value="Listeingredient" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;html=1;" vertex="1" parent="1">
<mxGeometry x="140" y="70" width="111" height="78" as="geometry"/>
</mxCell>
<mxCell id="353" value="(FK)idrecette" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="351">
<mxGeometry y="26" width="111" height="26" as="geometry"/>
</mxCell>
<mxCell id="354" value="(FK)idingredient" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" vertex="1" parent="351">
<mxGeometry y="52" width="111" height="26" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

9
config/database.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
return [
'host' => 'localhost',
'port' => 3306,
'user' => 'admin',
'pass' => 'adminpass123',
'name' => 'siterecette'
];

View File

@@ -1,57 +0,0 @@
# Les recettes des Papis
## Installation
- Décompressez l'archive du site.
- Pointez votre domaine sur le dossier "public".
- Modifiez les fichiers de configuration dans "config".
## Informations pratiques sur le PHP.
- Ce site utilise le principe des routes. Afin de ne pas avoir x fichiers publiques identiques pour chaque page.
- Tout est géré par le fichier public/index.php.
- index.php va charger le Kernel (Noyau) de l'application.
- Le noyau fonctionne sur le principe d'élément unique. La méthode ``Kernel::getInstance()`` va permettre d'avoir l'unique instance de la classe Kernel.
- Une fois ``Kernel::getInstance()->init()`` déclenché, le noyau enregistre l'autoloading des fichiers, puis enregistre la configuration.
- La configuration sont des fichiers dans le dossier config/ qui vont renvoyer un tableau clé/valeur. Exemple
```php
// Fichier config/test.php
<?php return [
'cle_1' => "Val1",
'cle_2' => "Val2",
// ...
] ?>
```
- Les fichiers de configuration sont chargés via la méthode ``ConfigFactory::loadConfigFile()`` qui va vérifier si le fichier de configuration existe et le charge en conséquence.
- On enchaine ensuite sur le Router.
- Le Router est une classe utilitaire qui va permettre de faire le lien entre la route saisie ``/test/...`` de l'utilisateur et les routes enregistrés par le site.
- Le Router va d'abord commencer par récupérer la route voulue par l'utilisateur.
- Puis, le router va rechercher tous les Controllers existants.
- Un Controller est une classe qui va permettre de définir les routes et de dire, qu'est-ce qui se passe quand je prends cette route. Les routes du Controller sont définis par la methode ``Controller::defineRoutes()``
- Pour définir les routes dans la méthode précédente, vous pouvez soit créer un objet ``Route`` en remplissant tous les champs, ou bien la méthode ``Controller::Route()`` qui va préremplir certains champs.
- Exemple de Controller
```php
class TestController extends Controller {
public static function defineRoutes(): array {
return [
self::Route( routeUrl: '/test', routeName: "Test", routeAction: "test" ),
new Route( routeUrl: "/test2", routeName: "Test2", routeController: "TestController", routeAction: "test2", routeMethods = [ 'GET' ] )
];
}
public function test(){
echo "Je suis déclenché lorsque j'atteinds la route /test";
}
public function test2(){
echo "Je suis déclenché lorsque j'atteinds la route /test2 !!!";
}
}
```
- Retournons au routeur, pour chercher les controllers, il va scanner les fichiers du dossier src/Domain avec un Iterator Récursif. (Pour se simplifier un peu la tâche.)
- Il va traiter le nom du chemin absolu pour obtenir le nom de la classe, puis va vérifier si cette classe est une sous-classe de Controller.
- Après avoir trouvé tous les Controllers, il va récupérer toutes les routes de chaque controller.
- Enfin, il va vérifier si la route utilisateur correspond à une des routes dans sa liste, si ça correspond, le Router va charger la méthode ``new {RouteController}()->{RouteAction}()``
- Si par exemple, le router trouve la route ``/test`` voulu par l'utilisateur, il va déclencher la méthode ``new TestController()->test()``.

View File

@@ -1,53 +0,0 @@
<?php
class BDD{
private static ?PDO $pdo=null;
public const DB_NAME = 'siterecette';
private static string $db_name=BDD::DB_NAME;
public static function getPDO(){
if(self::$pdo==null){
$db_host = "127.0.0.1" ; $db_port = "3306" ;
// identifiant
$db_user = "admin" ; $db_pwd = "adminpass123" ;
// connection a la database
try{
$dsn = 'mysql:dbname=' . self::$db_name . ';host='. $db_host. ';port=' . $db_port;
$pdoo = new PDO($dsn, $db_user, $db_pwd);
}
//erreur?
catch (\Exception $ex){ ?>
Code : <?= $ex->getCode() ?><br> Message : <?= $ex->getMessage() ?>
<?php
die("message d'arreet connexion stopee") ;}
self::$pdo = $pdoo;
}
return self::$pdo;
}
public function getname(){
return self::$db_name;
}
}
?>

View File

@@ -1,182 +0,0 @@
/* Sommaire :
- body et html
- Header et ses contenues
- Body contenue
- Content
- Page de présentation des recettes
- Page d'une recette
- Footer
*/
/*Tout*/
html, body{
height: 100%;
text-align: center;
/*font-size: 98%;*/
}
/*Header et son contenu*/
#header {
display: flex;
flex-direction: row;
padding: 3px;
padding-left: 2%;
padding-bottom: 10px;
background-color: blanchedalmond;
}
#logo {
background-color: aqua;
background-image: url("Logo.jpg");
/*background:no-repeat;*/
height: 103px;
width: 141px;
}
.logo{
height: 100%;
width: 100%;
border: 1px white solid;
border-radius: 4px;
box-shadow: 1px 1px 1px black;
}
nav {
flex: 9;
text-align: initial;
letter-spacing: 2px;
padding: 10px;
}
.nav-list {
list-style-type: none;
margin: 0;
padding: 0;
background-color: rgb(255, 217, 160);
display: flex;
border: 1px solid rgba(0,0,0,.125);
border-radius: 40px;
}
.nav-element {
line-height: 2.5;
padding: 10px;
}
/*Body et son contenu */
body {
background-color: blanchedalmond;
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
height: 100%;
}
.sidebar {
background-color: aquamarine;
border: 1px solid rgba(0,0,0,.125);
border-radius: 10px;
width: 128px;
}
.main-body {
display: flex;
flex-direction: row;
overflow: auto;
flex: 1;
background-clip: border-box;
padding-right: 5%;
padding-left: 5%;
}
/* content : le cadre de base du centre de notre site
*/
.content {
background-color: #ffe4bb;
text-align: left;
width: 100%;
height: 100%;
border: 1px solid rgba(0,0,0,.125);
border-radius: 6px;
overflow: auto;
}
/*Recettes :
représente le contenue centrale de la page.
*/
.recettes {
--UneVariable: calc( 100vw / 500 );
display: grid;
grid-template-columns: repeat(var(--UneVariable), 1fr);
grid-gap: 10px;
grid-auto-rows: minmax(300px, auto);
grid-auto-columns: auto;
}
/* Classe recette icone :
C'est la classe de chaques éléments de notre liste de recette
*/
.recette-icone{
border: 3px solid rgba(0,0,0,.125);
border-radius: 40px;
text-align: left;
padding: 15px;
vertical-align: middle;
box-shadow: 3px 5px 5px black;
width: 500px;
}
.recette-preview-image{
max-width: calc( 10vh + 10vw );
max-height: calc( 10vh + 10vw );
border: 1px solid rgb(252, 223, 57);
border-radius: 25px;
}
/*Ici commence le CSS pour la page de chaques recettes */
/*contenue*/
.recette-content {
display: flex;
flex-direction: column;
}
/*titre*/
.recette-title {
text-align: center;
}
/*image*/
.recette-image {
background-color: red;
aspect-ratio: 1/1;
max-height: calc( 30vh + 30vw );
max-width: calc( 30vh + 30vw );
border: 1px solid white;
border-radius: 30px;
}
.recette-div-image {
display: flex;
justify-content: center;
}
/*description*/
.recette-desc {
padding : 10px;
}
/*Footer et son contenue*/
footer{
padding: 25px;
background: rgb(11, 189, 144);
color: white;
}

View File

@@ -1,28 +0,0 @@
<?php
use App\Kernel;
if( !defined( 'APP_ROOT' ) )
/**
* Définit la racine de l'application en chemin absolue.
*/
define( 'APP_ROOT', realpath( __DIR__ . '/../') . '/' );
//require_once '../src/Kernel.php';
//Kernel::start();
echo "bonjour";
require '../config/BDD.php';
require '../src/Domain/Repository.php';
require_once '../src/Domain/Recettes/Recettes.php';
require '../src/Domain/Recettes/RecettesRepository.php';
$bd = new BDD();
$test = new RecettesRepository($bd->getpdo(),$bd->getname());
//public static function add(int $num_recette, string $titre_recette, string $slug, string $description_recette, string $photo, string $publication_date, int $temps_de_preparation ): Bool{
$test::add(5,"a",'a','a','a','2028-12-31',1);
$recetest = $test::getAll();
echo $recetest->slug;

View File

@@ -1,12 +0,0 @@
<?php
class Model{
}
?>

View File

@@ -1,33 +0,0 @@
<?php
class Recettes { // extends Model {
public int $num_recette;
public string $titre_recette; //inférieur ou égal a 60
public string $slug; //max = 200
public string $description_recette; // max 1000
public string $photo; // max 200, url
public string $publication_date; //une date
public int $temps_de_preparation; //en minute;
//public DateTime date;
public string $column;
public function __construct(int $num_recette, string $titre_recette, string $slug,
string $description_recette, string $photo, string $publication_date, int $temps_de_preparation){
$this->column="Recette";
$this->num_recette=$num_recette;
$this->titre_recette=$titre_recette;
$this->slug=$slug;
$this->description_recette=$description_recette;
$this->photo=$photo;
$this->publication_date=$publication_date;
$this->temps_de_preparation=$temps_de_preparation;
}
}
?>

View File

@@ -1,40 +0,0 @@
<?php
class RecettesRepository extends Repository{
private static string $nomBDD=BDD::DB_NAME;
private static string $tablename="Recette"; //tablename
private static string $attributesname="(num_recette,titre_recette,slug,description_recette,photo,publication_date,temps_de_preparation)";
private static string $idname="num_recette";
public function __construct(){
}
public static function getAll(): ?array{
return parent::selectgetAll(self::$tablename);
}
public static function getByID($id): ?Recettes{
return parent::selectgetByID($id,self::$tablename,self::$idname);
}
//on peut passer un array (ou objet sous forme (array)objet) en paramètre à la place de tout les autres paramètres avec (...array) au choix, pas de tableaux en paramètre normal pour éviter d'envoyer un mauvais tableaux
//ca généralise quand même si chaque array pour différentes entitees sont scannee comme parametre car chacun ont leurs propre attributs
public static function add(int $num_recette, string $titre_recette, string $slug, string $description_recette, string $photo, string $publication_date, int $temps_de_preparation ): Bool{
(array)$attributes=[ $num_recette, $titre_recette, $slug, $description_recette, $photo, $publication_date, $temps_de_preparation];
return parent::insertadd($attributes,self::$tablename,self::$attributesname,self::$idname,1); //le '1' car isentity, on vérifie que existe pas dejaa avec cet id
return false;
}
}
?>

View File

@@ -1,99 +0,0 @@
<?php
class Repository{
//private static string $nomBDD;
//private static string $attributesname;
//private static ?PDO $pdo;
//public function __construct(?PDO $pdoo, string $nombdd){
//self::$pdo=$pdoo; self::$nomBDD = $nombdd; self::$column="Recette";
//self::$attributesname="(num_recette,titre_recette,slug,description_recette,photo,publication_date)";
//}
//les infos nécessaires sont passé par les classe filles
public static function selectgetAll($tablename): ?array{
$sql = "SELECT * FROM " . $tablename;
$pdo=BDD::getPDO();
$statement = $pdo->prepare($sql) ;
$statement->execute() or die(var_dump($statement->errorInfo())) ;
$results = $statement->fetchAll(PDO::FETCH_OBJ) ;
//print_r($results);
if(empty($results))return null;
return (array)$results;
}
public static function selectgetByID($id,$tablename,$idname): ?Recettes{
$sql = "SELECT * FROM " . $tablename . " WHERE " . $idname ." = " . $id;
print_r($sql);
$pdo=BDD::getPDO();
$statement = $pdo->prepare($sql) ;
$statement->execute() or die(var_dump($statement->errorInfo())) ;
$results = $statement->fetchAll(PDO::FETCH_OBJ);
$valuearray = (array)$results[0];
//print_r($valuearray);
//$results = un array de 1 dimension de taille 1 elt avec chaque attribut $result[0]->attribut echo print_r($results[0]->titre_recette);
if(empty($valuearray))return null;
return new Recettes(...$valuearray); // '...' unpack l'array et comme les clés correspondent a celles du constructeurs ça marche.
}
public static function insertaddall(array $attributesattributes,string $tablename,string $attributesname, string $idname): bool{
$pdo = BDD::getPDO();
$sql = "INSERT INTO " . $tablename . " " . $attributesname;
//je vais utiliser bind value car en fonction de quel entitee arrive on a des str ou des int c'est plus dur de generaliser si nécessaire de gerer des ,'str',int,int,'str', etc.
if(empty($attributesattributes))return false;
$attribtag="val"; $interativ=0;
$attribbind=" VALUES (";
foreach($attributesattributes[0] as $a){
$interativ++;
$attribtagn=$attribtag . $interativ;
if($interativ>1){$attribbind = $attribbind . ", ";}
$attribbind = $attribbind . ":" . $attribtagn;
}
$attribbind = $attribbind . ")";
$query = $sql . $attribbind;
$statement = $pdo->prepare($query);
// Liaison des paramètres à des valeurs
$interativ=0;
foreach($attributesattributes as $attributes)
foreach($attributes as $attribute){
$interativ++;
$attribtagn=$attribtag . $interativ;
$statement->bindValue($attribtagn, $attribute);
}
$statement->execute() or die(var_dump($statement->errorInfo())) ;
return true;
}
public static function insertadd(array $attributes,$tablename,$attributesname, $idname, $isentity): bool{
// vérification id n'existe pas déjà :
if($isentity){
$isitalreadyusedid = self::selectgetById($attributes[0],$tablename,$idname); //le dernier param = id, accessible ainsi car simple array des attributs d'une seule entitee,
// à modifier pour insertall si on fait la verif dedans mais distinguer les array attributs d'entitee et d'association, la verif des associations doit être autre (si idtable1 combinee a idtable2 existe deja dans un même enregistrement)
if(!empty($isitalreadyusedid)) return false;}
//je vais utiliser bind value car en fonction de quel entitee arrive on a des str ou des int c'est plus dur de generaliser si nécessaire de gerer des ,'str',int,int,'str', etc.
//le mieux serait de mettre tout le code de insertadd dans une fonction insertadds qui gère une multiinsertion, insertadd appelle insertadds pour un seul elt.
(array)$attributesattributes=[$attributes];
return self::insertaddall($attributesattributes,$tablename,$attributesname,$idname);
}
}
?>

View File

@@ -1,2 +0,0 @@
<h1>Coucou</h1>
<?php echo \App\Http\Router::getRouteURL( 'test', 3 ); ?>

View File

@@ -1,23 +0,0 @@
<?php use App\Infrastructure\View as V; ?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo V::getHeadTitle(); ?></title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="<?php V::assetUrl( 'css/style.css' ); ?>" />
</head>
<body>
<header id="header">x
<div id="logo">
<a href="index.php">
<img src="<?php V::assetUrl( 'images/Logo.jpg' ); ?>" class="logo">
</a>
</div>
<nav>
<ul class="nav-list">
<li><a id="google" class="nav-element" href="google.com">Google</a></li>
<li><a id="unTest" class="nav-element" href="bing.com">Bing</a></li>
</ul>
</nav>
</header>

View File

@@ -1,20 +0,0 @@
<?php use App\Infrastructure\View as V; ?>
<div class="sidebar"> une sidebar</div>
<div class="content">
<div class="recettes">
<a href="recette-en-dur.php">
<div class="recette-icone">
<img class="recette-preview-image" src="random-recette.jpg">
</div>
</a>
<div class="recette-icone"> 2 </div>
<div class="recette-icone"> 3 </div>
<div class="recette-icone"> 4 </div>
<div class="recette-icone"> 5 </div>
<div class="recette-icone"> 6 </div>
<div class="recette-icone"> 7 </div>
<div class="recette-icone"> 8 </div>
<div class="recette-icone"> 9 </div>
</div>
</div>

462
public/assets/css/style.css Normal file
View File

@@ -0,0 +1,462 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap');
/* Sommaire :
- body et html
- Header et ses contenues
- Body contenue
- Content
- Page de présentation des recettes
- Page d'une recette
- Footer
*/
/*Tout*/
html, body{
height: 100%;
text-align: center;
/*font-size: 98%;*/
font-family: "Roboto", sans-serif;
}
/*Header et son contenu*/
#header {
display: flex;
flex-direction: row;
padding: 3px;
padding-left: 2%;
padding-bottom: 10px;
background-color: blanchedalmond;
}
#logo {
background-color: aqua;
background-image: url("Logo.jpg");
/*background:no-repeat;*/
height: 103px;
width: 141px;
}
.logo{
height: 100%;
width: 100%;
border: 1px white solid;
border-radius: 4px;
box-shadow: 1px 1px 1px black;
}
.logo:hover{
box-shadow: 1px 1px 2px #0bbd90;
border: 1px solid #0bbd90;
}
nav {
flex: 9;
text-align: initial;
letter-spacing: 2px;
padding: 10px;
}
.nav-list {
list-style-type: none;
margin: 0;
padding: 0;
background-color: rgb(255, 217, 160);
display: flex;
border: 1px solid rgba(0,0,0,.125);
border-radius: 40px;
}
.nav-element {
line-height: 2.5;
padding: 10px;
text-decoration: none;
color: black;
}
.nav-list{
li:nth-of-type(3){
position: fixed;
right: 25px;
}
}
#login {
border: 1px solid black;
border-radius: 6px;
background-color: #ffc478;
}
#login:hover {
background-color: #0bbd90;
}
/*Body et son contenu */
body {
background-color: blanchedalmond;
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
height: 100%;
}
.sidebar {
background-color: #caecd6;
border: 1px solid rgba(0,0,0,.125);
border-radius: 6px;
width: 128px;
display: flex;
flex-direction: column;
overflow: scroll;
}
.tag-cont {
/*display:none;*/
}
.ingr-cont {
/*display:none;*/
}
.tag-selected-div {
flex: 4;
}
.tag-unselected-div {
flex: 4;
}
.sidebar-search {
flex: 1;
}
.search-form-tag {
width: 85%;
}
.search-form-recette {
width: 100%;
height: 25px;
}
.search-form {
border: 1px solid black;
border-radius: 20px;
box-shadow: 0px 0px 10px rgb(131, 131, 131);
}
nav {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.tag {
border: 1px solid black;
border-radius: 10px;
width: 95%;
max-width: calc(128px*0.95);
box-shadow: 1px 1px 1px black;
}
.tag-selected {
background-color: #ffa04d;
}
.tag-selected:hover {
background-color: white;
}
.tag-unselected {
background-color: white;
}
.tag-unselected:hover {
background-color: #ffa04d;
}
#hr {
border: 1px solid #0bbd90;
width: 93%;
box-shadow: 1px 3px 5px #0bbd90;
}
.main-body {
display: flex;
flex-direction: row;
flex: 1;
background-clip: border-box;
padding-right: 2%;
padding-left: 2%;
overflow: hidden;
}
/* content : le cadre de base du centre de notre site
*/
.content {
background-color: #ffe4bb;
text-align: left;
width: 100%;
height: 100%;
border: 1px solid rgba(0,0,0,.125);
border-radius: 6px;
overflow: scroll;
}
/*Recettes :
représente le contenue centrale de la page.
*/
.recettes {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
/* grid-auto-rows: minmax(300px, auto);
grid-auto-columns: auto;*/
padding: 10px;
}
@media screen and (max-width : 1200px) {
.recettes {
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (max-width : 800px) {
.recettes {
grid-template-columns: repeat(1, 1fr);
}
}
@media screen and (max-width : 600px) {
.sidebar {
display: none;
}
}
ul {
list-style-type: none;
margin-left: 0px;
text-align: center;
padding : 0px;
}
/* Classe recette icone :
C'est la classe de chaques éléments de notre liste de recette
*/
.recette-icone{
display: flex;
border: 3px solid rgba(0,0,0,.125);
border-radius: 40px;
text-align: left;
padding: 15px;
vertical-align: middle;
box-shadow: 3px 5px 5px black;
max-width: 400px;
text-decoration: none;
color: black;
}
.recette-icone:hover{
border-color: #0bbd90;
}
.recette-icone-content{
display: flex;
flex-direction: column;
padding-left: 10px;
}
.recette-preview-image{
max-width: calc( 5vh + 5vw );
max-height: calc( 5vh + 5vw );
border: 1px solid rgb(252, 223, 57);
border-radius: 25px;
}
/*Ici commence le CSS pour la page de chaques recettes */
/*contenue*/
.recette-content {
display: flex;
flex-direction: column;
}
/*titre*/
.recette-title {
text-align: center;
}
/*image*/
.recette-image {
background-color: red;
aspect-ratio: 1/1;
max-height: calc( 30vh + 30vw );
max-width: calc( 30vh + 30vw );
border: 1px solid white;
border-radius: 30px;
}
.recette-div-image {
display: flex;
justify-content: center;
}
.recette-div-info {
display: flex;
flex-direction: row;
padding: 20px;
}
.recette-div-liste-info{
padding: 30px;
margin: 10px;
width: 100%;
flex:1;
}
.recette-list-buttons {
display: flex;
flex-direction: row-reverse;
}
.recette-button {
border: 1px solid black;
border-radius: 10px;
padding : 10px;
box-shadow: 1px 1px 1px black;
background-color: blanchedalmond;
}
.recette-button:hover{
background-color: rgb(243, 215, 174);
}
.recette-liste-info-elem {
text-align: left;
padding: 4px;
padding-top: 6px;
padding-bottom: 6px;
color: #535353bd;
border-right: 1px solid #535353bd;
border-left: 1px solid #535353bd;
}
.recette-liste-info {
display: flex;
flex-direction: row;
}
.recette-liste-tag {
display: flex;
flex-wrap: wrap; /*trouvé à l'aide de ChatGPT car je n'arrivais pas à trouver*/
}
/*description*/
.recette-desc {
padding : 30px;
}
/*Login form*/
.formcont {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.formcont form {
width: 50vmin;
height: 40vmin;
border: 2px solid rgb(0, 0, 0);
padding: 20px;
display: flex;
flex-direction: column;
gap: 1.5vmin;
background:white;
border-radius: 12px;
box-shadow: 5px 5px 30px #555;
}
.formcont form .form-group {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding: 1.2vmin;
padding-left:0px;
}
.formcont form .form-group label {
width: 95%;
text-align: center;
color:black;
font-size:5vmin;
}
.formcont form .form-group input {
width: 95%;
box-sizing: border-box;
font-size:3vmin;
padding:1vmin;
}
.formcont form button {
width: 48%;
height: 19%;
padding:0.5vmin;
box-sizing: border-box;
margin: 0 auto;
background: #ffa04d;
color: black;
font-size: 4vmin;
border-radius: 7px;
box-shadow: 2px 2px 0px #000000;
}
.btn:hover {
background-color: #0bbd90;
}
.form-control {
border-radius: 10px;
box-shadow: 3px 4px 5px #8c8c8c;
}
.recette-form-div-desc {
height: 90%;
}
.recette-form {
height: 100%;
width: 100%;
margin: 10px;
padding: 10px;
}
.recette-form-group {
display: flex;
flex-direction: column;
}
#recette-form-div-desc {
height: 400px;
}
#recette-form-description {
height: 95%;
}
/*Footer et son contenue*/
footer{
padding: 25px;
background: rgb(11, 189, 144);
color: white;
}

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

66
public/assets/js/login.js Normal file
View File

@@ -0,0 +1,66 @@
document.addEventListener( 'DOMContentLoaded', function(){
const FORM = document.getElementById( 'login-form' );
const FORM_ERROR = document.getElementById( 'login-errors' );
if( !FORM )
return;
/**
* Permet d'afficher une erreur dans le formulaire.
* @param message
*/
FORM.showError = function( message ){
FORM_ERROR.style.display = 'block';
FORM_ERROR.innerText = message;
}
/**
* Permet de retirer l'erreur du formulaire.
*/
FORM.removeError = function(){
FORM_ERROR.style.display = 'none';
FORM_ERROR.innerText = '';
}
/**
* Gère l'envoi du formulaire.
* @param e
* @returns {boolean}
*/
FORM.onsubmit = function( e ){
e.preventDefault();
FORM.removeError();
let userField = e.target.username;
let passwordField = e.target.password;
if( !userField || !passwordField || userField.value === "" || passwordField.value === "" ){
FORM.showError( "Le nom d'utilisateur ou le mot de passe ne doivent pas être vide.");
return false;
}
let formData = new FormData( FORM );
let options = {
method: "POST",
contentType: "application/json",
body: formData,
}
fetch( FORM.action, options ).then( ( response ) => {
if( response.ok ){
response.json().then( ( responseJSON ) => {
if( responseJSON.success === true ){
window.location.href = window.location.origin; // Redirection sur la page d'accueil si succès.
} else {
FORM.showError( responseJSON.message || "Mauvais nom d'utilisateur ou mauvais mot de passe." );
}
})
}
})
}
})

13
public/index.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
use App\Kernel;
if( !defined( 'APP_ROOT' ) )
/**
* Définit la racine de l'application en chemin absolue.
*/
define( 'APP_ROOT', realpath( __DIR__ . '/../') . '/' );
require_once '../src/Kernel.php';
Kernel::start();

View File

@@ -4,6 +4,9 @@ namespace App\Domain;
use App\Http\Route;
/**
* Classe abstraite des controllers qui vont pouvoir permettre de gérer les routes et leur retour.
*/
abstract class Controller {
/**

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Domain\Ingredients;
use App\Domain\Model;
/**
* Classe qui va gérer les ingrédients.
*/
class Ingredient extends Model {
/**
* Le numéro de l'ingrédient.
* @var int
*/
public int $num_ingredient;
/**
* Le nom de l'ingrédient
* @var string
*/
public string $nom_ingredient;
/**
* Retourne le numéro de l'ingrédient.
* @return int
*/
public function getID(): int
{
return $this->num_ingredient;
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace App\Domain\Ingredients;
use App\Domain\LinkableInterface;
use App\Domain\Recettes\Recette;
use App\Domain\Repository;
use App\Domain\Model;
use App\Domain\Tags\Tag;
use App\Kernel;
/**
* Classe qui va permettre de gérer les requêtes BDD en lien avec les ingrédients.
* Les ingrédients sont liables à d'autres entités comme les Recettes.
*/
class IngredientRepository extends Repository implements LinkableInterface {
/**
* Permet d'avoir le nom de l'entité dont dépend ce repo.
* @return string
*/
public static function getEntity(): string
{
return Ingredient::class;
}
/**
* La structure de notre table dans la BDD.
* Un champ link_recettes a été ajouté pour donner le nom de la table de lien entre nos recettes.
*
* @return array
*/
public static function getStructure(): array
{
return [
'table' => 'Ingredient',
'columns' => [
'num_ingredient', 'nom_ingredient'
],
'link_recettes' => 'Listeingredient'
];
}
/**
* Permet d'obtenir une liste de tous les ingrédients objet Ingreident.
*
* @return Ingredient[]|null
*/
public function getAll(): ?array {
$sqlQuery = "SELECT * FROM {$this->tableName};";
$results = $this->selectGetAll($sqlQuery);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'obtenir un ingrédient spécifique par son ID.
*
* @param int $id
*
* @return Ingredient|null
*/
public function getByID( int $id ): ?Ingredient {
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_ingredient = {$id}";
$results = $this->selectGetAll($sqlQuery);
if( $results === null || count( $results ) > 1 )
return null;
return $results[0];
}
/**
* Permet d'obtenir, sous forme de liste, toutes les entrées qui lient des ingrédients.
*
* @param string $linkedTo La table qui permet de faire la liaison des ingrédients avec une autre entité.
* @param string $linkingField Le champ qui permet de faire la liaison des ingrédients avec une autre entité.
* @param Model $linkedEntity L'autre entité.
*
* @see LinkableInterface
* @return array|null
*/
public function getIdLinkedTo( string $linkedTo, string $linkingField, Model $linkedEntity ): ?array {
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return null;
$sqlQuery = "SELECT * FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()};";
$results = $this->selectGetAll($sqlQuery, true);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'ajouter un lien entre un ingrédient et une autre entité comme Recette.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $ingredientEntity
*
* @see LinkableInterface
* @return bool
*/
public function addLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $ingredientEntity ): bool {
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return false;
$query = "INSERT INTO {$this->globalStructure[$linkName]} ({$linkingField},num_ingredient) VALUES ({$linkedEntity->getId()}, {$ingredientEntity->getID()});";
$statement = Kernel::$DB->pdo->prepare( $query );
return $statement->execute();
}
/**
* Retire un lien dans la BDD entre un ingrédient et une autre entité.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $ingredientEntity
*
* @see LinkableInterface
* @return bool
*/
public function removeLinkBetween(string $linkedTo, string $linkingField, Model $linkedEntity, Model $ingredientEntity ): bool
{
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return false;
$query = "DELETE FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()} AND num_ingredient = {$ingredientEntity->getId()};";
$statement = Kernel::$DB->pdo->prepare( $query );
return $statement->execute();
}
/**
* Ajouter un ingrédient dans la BDD.
*
* @param Model $ingredient
*
* @return bool
*/
public function add( Model $ingredient ): bool {
return $this->addEntity( $ingredient );
}
/**
* Met à jour un ingrédient dans la BDD.
*
* @param Model $ingredient
*
* @return bool
*/
public function update( Model $ingredient ): bool {
return $this->updateEntity( $ingredient, 'num_ingredient' );
}
/**
* Supprime un ingrédient de la BDD.
*
* @param Model $ingredient
*
* @return bool
*/
public function delete( Model $ingredient ): bool {
return $this->deleteEntity( $ingredient, 'num_ingredient' );
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Domain\Ingredients;
use App\Domain\Model;
/**
* Interface utilisée par tous les repository qui sont en lien avec les ingrédients.
*/
interface UseIngredientsInterface {
/**
* Permet de récupérer tous les ingrédients liés à ce Modèle.
* @return array|null
*/
public function getAllLinkedIngredients( Model $entity ): ?array;
/**
* Permet d'ajouter un ingrédient en lien avec notre autre entité.
*
* @param Ingredient $ingredient
* @param Model $entity
*
* @return bool
*/
public function addAnIngredient( Ingredient $ingredient, Model $entity ): bool;
/**
* Permet de retirer un lien entre un ingrédient et une autre entité.
*
* @param Ingredient $ingredient
* @param Model $entity
*
* @return bool
*/
public function removeAnIngredient( Ingredient $ingredient, Model $entity ): bool;
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Domain;
/**
* Interface pour dire qu'un objet peut avoir un lien avec un autre objet par une table.
* Il s'agit d'une interface qui s'utilise sur des repositories de meta-données (Tag, Ingrédients)
*
* Les champs de toutes les méthodes se présentent de la même façon.
* $linkedTo : Champ qui va désigner le nom de la table de liens que vous avez dans getStructure().
* $linkingField : Champ dans la BDD qui va permettre de lier l'élément courant et l'étranger.
* $linkedEntity : Une instance de l'entité étrangère que l'on veut lier.
*/
interface LinkableInterface {
/**
* Permet de récupérer tous les liens entre notre entité implémentée et notre entité étrangère.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
*
* @return array|null
*/
public function getIdLinkedTo( string $linkedTo, string $linkingField, Model $linkedEntity ): ?array;
/**
* Permet d'ajouter un lien entre notre entité implémentée et notre entité étrangère.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $entity
*
* @return bool
*/
public function addLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $entity ): bool;
/**
* Permet de retirer un lien entre notre entité implémentée et notre entité étrangère.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $entity
*
* @return bool
*/
public function removeLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $entity ): bool;
}

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

@@ -0,0 +1,16 @@
<?php
namespace App\Domain;
/**
* Classe abstraite pour gérer l'implémentation des entités de données.
*/
abstract class Model {
/**
* Getter de l'ID primaire du model spécifique.
* @return int
*/
abstract public function getID(): int;
}

View File

@@ -11,11 +11,14 @@ use App\Infrastructure\View;
*/
class PagesController extends Controller {
/**
* Définit les routes globales d'acceuil.
* @return array|\App\Http\Route[]
*/
public static function defineRoutes(): array
{
return [
self::Route( routeUrl: '/', routeName: 'home', routeAction: 'index', pageHeadTitle: "Home Page" ),
self::Route( routeUrl: '/test/{int}/', routeName: 'test', routeAction: 'test' ),
];
}
@@ -28,8 +31,4 @@ class PagesController extends Controller {
return new View( 'home', [ 'ok' => 'bla' ] );
}
public function test( string $id ): void {
echo "Coucou" . $id;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Domain\Recettes;
use App\Domain\Ingredients\Ingredient;
use App\Domain\Ingredients\IngredientRepository;
use App\Domain\Ingredients\UseIngredientsInterface;
use App\Domain\Model;
use App\Helpers\Markdown;
/**
* Classe qui va permettre de gérer tous les objets recette.
*/
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 getID(): int
{
return $this->num_recette;
}
/**
* Convertit la description de Markdown à HTML.
* @return string
*/
public function getHTMLDescription(): string {
return Markdown::convertToHTML( $this->description_recette );
}
/**
* Récupère une liste de tous les ingrédients liés à la recette.
* @return Ingredient[]|null
*/
public function getAllLinkedIngredients(): ?array
{
return new RecetteRepository()->getAllLinkedIngredients( $this );
}
public function getNumberOfIngredients(): int {
$response = $this->getAllLinkedIngredients();
return $response !== null ? count( $response ) : 0;
}
}

View File

@@ -0,0 +1,162 @@
<?php
namespace App\Domain\Recettes;
use App\Domain\Ingredients\Ingredient;
use App\Domain\Ingredients\IngredientRepository;
use App\Domain\Ingredients\UseIngredientsInterface;
use App\Domain\Model;
use App\Domain\Repository;
use App\Domain\Tags\Tag;
use App\Domain\Tags\TagRepository;
use App\Domain\Tags\UseTagsInterface;
/**
* Classe qui permet de faire le lien entre la BDD et le site pour les recettes.
* Les recettes sont en lien avec les ingrédients.
*/
class RecetteRepository extends Repository implements UseIngredientsInterface, UseTagsInterface {
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'obtenir une liste de toutes les recettes objet Recette.
*
* @return Recette[]|null
*/
public function getAll(): ?array {
$sqlQuery = "SELECT * FROM {$this->tableName};";
$results = $this->selectGetAll($sqlQuery);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'obtenir toutes les recettes paginées.
* Mise par défaut dans l'ordre croissant du titre et avec une pagination de 15 éléments.
*
* @param int $page
* @param int $pagination
*
* @return array|null
*/
public function getAllRecettesBrowse( int $page = 1, int $pagination = 15 ){
if( $page <= 0 )
$page = 1;
$offset = ( $page - 1 ) * $pagination;
$sqlQuery = "SELECT * FROM {$this->tableName} ORDER BY titre_recette ASC LIMIT {$pagination} OFFSET {$offset};";
$results = $this->selectGetAll($sqlQuery);
if( $results === null )
return null;
return $results;
}
/**
* 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' );
}
public function getAllLinkedIngredients(Model $entity): ?array
{
$ingredientRepo = new IngredientRepository();
$response = $ingredientRepo->getIdLinkedTo( 'recettes', 'num_recette', $entity );
if( $response === null )
return null;
return array_map( function($arr) use($ingredientRepo) {
return $ingredientRepo->getByID( $arr['num_ingredient'] );
}, $response );
}
public function addAnIngredient(Ingredient $ingredient, Model $entity): bool
{
$ingredientRepo = new IngredientRepository();
return $ingredientRepo->addLinkBetween( 'recettes', 'num_recette', $entity, $ingredient );
}
public function removeAnIngredient(Ingredient $ingredient, Model $entity): bool
{
$ingredientRepo = new IngredientRepository();
return $ingredientRepo->removeLinkBetween( 'recettes', 'num_recette', $entity, $ingredient );
}
public function getAllLinkedTags(Model $entity): ?array
{
$tagRepo = new TagRepository();
$response = $tagRepo->getIdLinkedTo( 'recettes', 'num_recette', $entity );
if( $response === null )
return null;
return array_map( function($arr) use($tagRepo) {
return $tagRepo->getByID( $arr['num_tag'] );
}, $response );
}
public function addATag(Tag $tag, Model $entity): bool
{
$tagRepo = new TagRepository();
return $tagRepo->addLinkBetween( 'recettes', 'num_recette', $entity, $tag );
}
public function removeATag(Tag $tag, Model $entity): bool
{
$tagRepo = new TagRepository();
return $tagRepo->removeLinkBetween( 'recettes', 'num_recette', $entity, $tag );
}
}

View File

@@ -3,7 +3,10 @@
namespace App\Domain\Recettes;
use App\Domain\Controller;
use App\Domain\Ingredients\IngredientRepository;
use App\Domain\Tags\TagRepository;
use App\Http\JSONResponse;
use App\Http\Request;
use App\Infrastructure\View;
class RecettesController extends Controller {
@@ -18,7 +21,16 @@ class RecettesController extends Controller {
}
public function index(): View {
return new View( 'recettes/index', [] );
$page = Request::get( 'page' );
if( $page == null )
$page = 1;
return new View( 'recettes/index', [
'tagsList' => new TagRepository()->getAll(),
'ingredientsList' => new IngredientRepository()->getAll(),
'recettesList' => new RecetteRepository()->getAllRecettesBrowse( $page ),
] );
}
}

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

@@ -0,0 +1,170 @@
<?php
namespace App\Domain;
use App\Domain\Recettes\Recette;
use App\Kernel;
use PDO;
/**
* Classe abstraite avec des méthodes pour pouvoir gérer les liens entre le site et la base de données.
*/
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;
/**
* Contient le nom de la table principale du repo.
* @var string|mixed
*/
final public string $tableName;
/**
* Contient le nom des colonnes de la table du repo.
* @var array|mixed
*/
final public array $tableColumns;
/**
* Contient les mêmes données que getStructure().
* @var array
*/
public private(set) array $globalStructure;
/**
* Constructeur.
* Reprend les informations de getStructure et les met dans des attributs.
*/
public function __construct(){
$structure = static::getStructure();
$this->tableName = $structure['table'];
$this->tableColumns = $structure['columns'];
$this->globalStructure = $structure;
}
/**
* Permet d'avoir tous les éléments correspondant à la requête passée en paramètre.
*
* @param string $sqlQuery
* @param bool $asArray Permet de savoir si on veut que le retour soit une array ou bien un tableau de Model.
* @return array|null
*/
public function selectGetAll( string $sqlQuery, bool $asArray = false ): ?array {
$statement = Kernel::$DB->pdo->prepare( $sqlQuery );
if( !$statement->execute() )
return null;
if( $asArray )
$results = $statement->fetchAll( PDO::FETCH_ASSOC );
else
$results = $statement->fetchAll( PDO::FETCH_CLASS, static::getEntity() );
if( empty( $results ) )
return null;
return $results;
}
/**
* Permet d'ajouter une entité à la base de données.
*
* @param Model $entity
* @return bool
*/
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();
}
/**
* Permet de mettre à jour les informations de l'entité dans la base de données.
*
* @param Model $entity
* @param string $identifier
*
* @return bool
*/
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();
}
/**
* Permet de supprimer une entrée d'entité dans la base de données.
*
* @param Model $entity
* @param string $identifier
*
* @return bool
*/
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();
}
/**
* Fonction raccourcie pour préparer les champs nécessaires pour addEntity.
*
* @param Model $entity
*
* @return bool
*/
abstract public function add( Model $entity ): bool;
/**
* Fonction raccourcie pour préparer les champs nécessaires pour updateEntity
*
* @param Model $entity
*
* @return bool
*/
abstract public function update( Model $entity ): bool;
/**
* Fonction raccourcie pour préparer les champs nécessaires pour deleteEntity
*
* @param Model $entity
*
* @return bool
*/
abstract public function delete( Model $entity ): bool;
}

33
src/Domain/Tags/Tag.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
namespace App\Domain\Tags;
use App\Domain\Model;
/**
* Classe qui va gérer les tags.
*/
class Tag extends Model {
/**
* Le numéro du tag.
* @var int
*/
public int $num_tag;
/**
* Le nom du tag
* @var string
*/
public string $nom_tag;
/**
* Retourne le numéro du tag.
* @return int
*/
public function getID(): int
{
return $this->num_tag;
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace App\Domain\Tags;
use App\Domain\LinkableInterface;
use App\Domain\Recettes\Recette;
use App\Domain\Repository;
use App\Domain\Model;
use App\Kernel;
/**
* Classe qui va permettre de gérer les requêtes BDD en lien avec les tags.
* Les tags sont liables à d'autres entités comme les Recettes.
*/
class TagRepository extends Repository implements LinkableInterface {
/**
* Permet d'avoir le nom de l'entité dont dépend ce repo.
* @return string
*/
public static function getEntity(): string
{
return Tag::class;
}
/**
* La structure de notre table dans la BDD.
* Un champ link_recettes a été ajouté pour donner le nom de la table de lien entre nos recettes.
*
* @return array
*/
public static function getStructure(): array
{
return [
'table' => 'Tag',
'columns' => [
'num_tag', 'nom_tag'
],
'link_recettes' => 'Referencetag'
];
}
/**
* Permet d'obtenir une liste de tous les tags objet Tag.
*
* @return Tag[]|null
*/
public function getAll(): ?array {
$sqlQuery = "SELECT * FROM {$this->tableName};";
$results = $this->selectGetAll($sqlQuery);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'obtenir un tag spécifique par son ID.
*
* @param int $id
*
* @return Tag|null
*/
public function getByID( int $id ): ?Tag {
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_tag = {$id}";
$results = $this->selectGetAll($sqlQuery);
if( $results === null || count( $results ) > 1 )
return null;
return $results[0];
}
/**
* Permet d'obtenir, sous forme de liste, toutes les entrées qui lient des tags.
*
* @param string $linkedTo La table qui permet de faire la liaison des tags avec une autre entité.
* @param string $linkingField Le champ qui permet de faire la liaison des tags avec une autre entité.
* @param Model $linkedEntity L'autre entité.
*
* @see LinkableInterface
* @return array|null
*/
public function getIdLinkedTo( string $linkedTo, string $linkingField, Model $linkedEntity ): ?array {
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return null;
$sqlQuery = "SELECT * FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()};";
$results = $this->selectGetAll($sqlQuery, true);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'ajouter un lien entre un tag et une autre entité comme Recette.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $tagEntity
*
* @see LinkableInterface
* @return bool
*/
public function addLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $tagEntity ): bool {
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return false;
$query = "INSERT INTO {$this->globalStructure[$linkName]} ({$linkingField},num_tag) VALUES ({$linkedEntity->getId()}, {$tagEntity->getID()});";
$statement = Kernel::$DB->pdo->prepare( $query );
return $statement->execute();
}
/**
* Retire un lien dans la BDD entre un tag et une autre entité.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $tagEntity
*
* @see LinkableInterface
* @return bool
*/
public function removeLinkBetween(string $linkedTo, string $linkingField, Model $linkedEntity, Model $tagEntity ): bool
{
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return false;
$query = "DELETE FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()} AND num_tag = {$tagEntity->getId()};";
$statement = Kernel::$DB->pdo->prepare( $query );
return $statement->execute();
}
/**
* Ajouter un tag dans la BDD.
*
* @param Model $ingredient
*
* @return bool
*/
public function add( Model $tag ): bool {
return $this->addEntity( $tag );
}
/**
* Met à jour un tag dans la BDD.
*
* @param Model $tag
*
* @return bool
*/
public function update( Model $tag ): bool {
return $this->updateEntity( $tag, 'num_tag' );
}
/**
* Supprime un tag de la BDD.
*
* @param Model $tag
*
* @return bool
*/
public function delete( Model $tag ): bool {
return $this->deleteEntity( $tag, 'num_tag' );
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Domain\Tags;
use App\Domain\Model;
/**
* Interface utilisée par tous les repository qui sont en lien avec les tags.
*/
interface UseTagsInterface {
/**
* Permet de récupérer tous les tags liés à ce Modèle.
* @return array|null
*/
public function getAllLinkedTags( Model $entity ): ?array;
/**
* Permet d'ajouter un tag en lien avec notre autre entité.
*
* @param Tag $tag
* @param Model $entity
*
* @return bool
*/
public function addATag( Tag $tag, Model $entity ): bool;
/**
* Permet de retirer un lien entre un tag et une autre entité.
*
* @param Tag $tag
* @param Model $entity
*
* @return bool
*/
public function removeATag( Tag $tag, 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();

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Domain\Utilisateurs;
use App\Domain\Model;
//use App\Helpers\Markdown;
/**
* Classe qui va permettre de gérer tous les objets recette.
*/
class Utilisateurs extends Model {
public int $num_user;
public string $username;
public string $userpassword;
public function getID(): int
{
return $this->num_user;
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace App\Domain\Utilisateurs;
use App\Domain\Model;
use App\Domain\Repository;
/**
* Classe qui permet de faire le lien entre la BDD et le site pour les recettes.
* Les recettes sont en lien avec les ingrédients.
*/
class UtilisateursRepository extends Repository {
public static function getEntity(): string
{
return Utilisateurs::class;
}
public static function getStructure(): array
{
return [
'table' => 'User',
'columns' => [
'num_user', 'username', 'userpassword'
]
];
}
/**
* Permet d'obtenir une liste de toutes les recettes objet Recette.
*
* @return Recette[]|null
*/
public function getAll(): ?array {
$sqlQuery = "SELECT * FROM {$this->tableName};";
$results = $this->selectGetAll($sqlQuery);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'avoir une recette par un ID.
*
* @param int $id
* @return Recette|null
*/
public function getByID( int $id ): ?Utilisateurs {
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_user = {$id}";
$results = $this->selectGetAll($sqlQuery);
if( $results === null || count( $results ) > 1 )
return null;
return $results[0];
}
public function add( Model $utilisateurs ): bool {
return $this->addEntity( $utilisateurs );
}
public function update( Model $utilisateurs ): bool {
return $this->updateEntity( $utilisateurs, 'num_user' );
}
public function delete( Model $utilisateurs ): bool {
return $this->deleteEntity( $utilisateurs, 'num_user' );
}
}

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

@@ -15,19 +15,19 @@ final class Router {
* @var string
*/
public private(set) static string $clientRouteString;
// private(set)
/**
* Informations de la route du client.
* @var Route
*/
public private(set) static Route $clientRoute;
public static Route $clientRoute;
// private(set)
/**
* Informations sur les arguments passés à la route.
* @var array
*/
public private(set) static array $clientRouteArguments;
public static array $clientRouteArguments;
// private(set)
/**
* Liste des controllers sorti du cache ou récupéré via fetchControllers.
* @var array
@@ -48,7 +48,7 @@ final class Router {
*/
public static function routeTo(): void {
self::$clientRouteString = $_SERVER['REQUEST_URI'];
self::$clientRouteString = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
self::$controllers = self::fetchControllers();
self::$routes = self::fetchRoutes();
@@ -166,7 +166,10 @@ final class Router {
$controller = self::$clientRoute->routeController;
$method = self::$clientRoute->routeAction;
new $controller()->$method( ...self::$clientRouteArguments);
(new $controller())->$method( ...self::$clientRouteArguments);
//correction controler()->method to controler().method
// new $controller()->$method( ...self::$clientRouteArguments);
}
/**
@@ -185,7 +188,7 @@ final class Router {
return $args[$i++] ?? "";
}, $route->routeUrl);
return rtrim( Kernel::$configs['general']['website_url'] . $routeUrl, '/' );
return rtrim( rtrim( Kernel::$configs['general']['website_url'], '/' ) . $routeUrl, '/' );
}
}

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'] );
}
}

32
views/home.php Normal file
View File

@@ -0,0 +1,32 @@
<h1>Coucou</h1>
<?php $str = "Markdown **text**\n# SOLONG\n*text*."; $md = \App\Helpers\Markdown::convertToHTML( $str ); echo "<p>$md</p>"; ?>
<?php use App\Infrastructure\View as V; use App\Http\Router; ?>
<?php
$recette = new \App\Domain\Recettes\RecetteRepository()->getByID( 2 );
$modelingredient = new \App\Domain\Ingredients\Ingredient();
$modelingredient->num_ingredient=0;
$modelingredient->nom_ingredient='nomdingredientaupif';
var_dump($modelingredient);
//$ingredient = new \App\Domain\Ingredients\IngredientRepository()->add($modelingredient);
//pas de add si numing existe déjà use autoincrement
$ingredient = new \App\Domain\Ingredients\IngredientRepository()->getByID( 1 );
var_dump( $recette );
var_dump( $ingredient );
// new \App\Domain\Recettes\RecetteRepository()->removeAnIngredient( $ingredient, $recette );
var_dump( $recette->getAllLinkedIngredients() );
echo "\n\n";
$modelUtilisateurs = new \App\Domain\Utilisateurs\Utilisateurs();
$modelUtilisateurs->num_user=0;
$modelUtilisateurs->username="usnmaemaee";
$modelUtilisateurs->userpassword="pawssorddd";
$user = new \App\Domain\Utilisateurs\UtilisateursRepository()->add($modelUtilisateurs);
$user = new \App\Domain\Utilisateurs\UtilisateursRepository()->getByID( 2 );
var_dump($user);
?>

23
views/login.php Normal file
View File

@@ -0,0 +1,23 @@
<?php use App\Infrastructure\View as V; use App\Http\Router; ?>
<div class=formcont>
<form id='login-form' action='<?php echo Router::getRouteURL( 'api->auth' ); ?>' method='post'>
<div class='form-group'>
<label for='logmsg'>Se Connecter</label>
</div>
<div id="login-errors" class="form-error" style="display:none;color:red;">
</div>
<div class='form-group'>
<input type='text' class='form-control' name='username' id='username' value='' placeholder="Pseudo">
</div>
<div class='form-group'>
<input type='text' class='form-control' name='password' id='password' value='' placeholder="Mot de Passe">
</div>
<button type='submit' class='btn btn-primary'>Confirmer</button>
</form>
</div>
<script src="<?php echo Router::getAssetURL( 'js/login.js' ); ?>" defer></script>

30
views/partials/header.php Normal file
View File

@@ -0,0 +1,30 @@
<?php use App\Infrastructure\View as V; ?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo V::getHeadTitle(); ?></title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="<?php V::assetUrl( 'css/style.css' ); ?>" />
</head>
<body>
<header id="header">
<div id="logo">
<a href="index.php">
<img src="<?php V::assetUrl( 'images/Logo.jpg'); ?>" class="logo">
</a>
</div>
<nav>
<ul class="nav-list">
<li><a id="google" class="nav-element" href="<?php V::routeUrl( 'home'); ?>">Acceuil</a></li>
<li><a id="unTest" class="nav-element" href="<?php V::routeUrl( 'recettes->index'); ?>">Liste des recettes</a></li>
<?php if( \App\Helpers\Authentification::isLoggedIn() ): ?>
<li><a id="login" class= "nav-element" href="<?php V::routeUrl( 'logout'); ?>">Logout</a></li>
<li><a id="addBouton" class="nav-element" href="<?php V::routeUrl( 'recettes->create'); ?>">Ajouter une Recette</a></li>
<?php else: ?>
<li><a id="login" class= "nav-element" href="<?php V::routeUrl( 'login'); ?>">Login</a></li>
<?php endif; ?>
</ul>
<input type="text" class="search-form search-form-recette" placeholder="Rechercher une recette ...">
</nav>
</header>

View File

@@ -0,0 +1,39 @@
<?php use App\Infrastructure\View as V; ?>
<div class="sidebar">
<div class="tag-cont">
<h4>Tag</h4>
<div class="tag-selected-div">
<ul>
</ul>
</div>
<form class="sidebar-search" action="none">
<input type="text" class="search-form search-form-tag" name="search-tag" placeholder="Rechercher..." >
</form>
<div class="tag-unselected-div">
<ul>
<?php foreach( V::arg( 'tagsList') as $tag ): ?>
<li class="tag tag-unselected" onclick="test()"><?php echo $tag->nom_tag; ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
<hr id="hr">
<div class="ingr-cont">
<h4>Ingrédient</h4>
<div class="tag-selected-div">
<ul>
</ul>
</div>
<form class="sidebar-search" action="none">
<input type="text" class="search-form search-form-tag" name="search-ingr" placeholder="Rechercher..." >
</form>
<div class="tag-unselected-div">
<ul>
<?php foreach( V::arg( 'ingredientsList') as $tag ): ?>
<li class="tag tag-unselected" onclick="test()"><?php echo $tag->nom_ingredient; ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>

19
views/recettes/index.php Normal file
View File

@@ -0,0 +1,19 @@
<?php use App\Infrastructure\View as V; ?>
<?php V::partial( 'tag-sidebar' ); ?>
<div class="content">
<div class="recettes">
<?php if( V::arg( 'recettesList' ) != null ) foreach( V::arg( 'recettesList' ) as $recette ): ?>
<a class="recette-icone" href="<?php V::routeUrl( 'recettes->show', $recette->slug ); ?>">
<img class="recette-preview-image" src="random-recette.jpg">
<div class="recette-icone-content">
<h3><?php echo $recette->titre_recette; ?></h3>
<ul>
<li>Temps de préparation : <?php echo $recette->temps_de_preparation; ?></li>
<li>Nombre d'ingrédients : <?php echo $recette->getNumberOfIngredients(); ?></li>
</ul>
</div>
</a>
<?php endforeach; ?>
</div>
</div>