diff --git a/README.md b/README.md index 8b34ef2..9a34211 100644 --- a/README.md +++ b/README.md @@ -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 + "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()``. \ No newline at end of file diff --git a/basededonnee/associations.txt b/basededonnee/associations.txt deleted file mode 100644 index e69de29..0000000 diff --git a/basededonnee/bdphp/connexionsimple.php b/basededonnee/bdphp/connexionsimple.php deleted file mode 100644 index 4a8077c..0000000 --- a/basededonnee/bdphp/connexionsimple.php +++ /dev/null @@ -1,44 +0,0 @@ - - -
- !!! ERREUR DE CONNEXION !!!
- Code : getCode() ?>
- Message : getMessage() ?> - -
Exécution stoppée <-") ; -} - -// Poursuite de l'exécution du script ?> -
Connecté à
- - diff --git a/basededonnee/codemysql/basedonneee.sql b/basededonnee/codemysql/basedonneee.sql deleted file mode 100644 index 106e50f..0000000 --- a/basededonnee/codemysql/basedonneee.sql +++ /dev/null @@ -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); diff --git a/basededonnee/contraintelisteingredientprobleme.txt b/basededonnee/contraintelisteingredientprobleme.txt deleted file mode 100644 index 7b535b4..0000000 --- a/basededonnee/contraintelisteingredientprobleme.txt +++ /dev/null @@ -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 ... : \ No newline at end of file diff --git a/basededonnee/entité.txt b/basededonnee/entité.txt deleted file mode 100644 index 8210c99..0000000 --- a/basededonnee/entité.txt +++ /dev/null @@ -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) diff --git a/basededonnee/test.drawio b/basededonnee/test.drawio deleted file mode 100644 index ec4b522..0000000 --- a/basededonnee/test.drawio +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..b168339 --- /dev/null +++ b/config/database.php @@ -0,0 +1,9 @@ + 'localhost', + 'port' => 3306, + 'user' => 'admin', + 'pass' => 'adminpass123', + 'name' => 'siterecette' +]; diff --git a/projetphp/config/general.php b/config/general.php similarity index 100% rename from projetphp/config/general.php rename to config/general.php diff --git a/projetphp/config/route_arguments.php b/config/route_arguments.php similarity index 100% rename from projetphp/config/route_arguments.php rename to config/route_arguments.php diff --git a/projetphp/config/views.php b/config/views.php similarity index 100% rename from projetphp/config/views.php rename to config/views.php diff --git a/projetphp/README.md b/projetphp/README.md deleted file mode 100644 index 9a34211..0000000 --- a/projetphp/README.md +++ /dev/null @@ -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 - "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()``. \ No newline at end of file diff --git a/projetphp/config/BDD.php b/projetphp/config/BDD.php deleted file mode 100644 index 6f1ab5d..0000000 --- a/projetphp/config/BDD.php +++ /dev/null @@ -1,53 +0,0 @@ - - Code : getCode() ?>
Message : getMessage() ?> - - - - diff --git a/projetphp/public/assets/css/style.css b/projetphp/public/assets/css/style.css deleted file mode 100644 index 9e6b0eb..0000000 --- a/projetphp/public/assets/css/style.css +++ /dev/null @@ -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; -} - - - diff --git a/projetphp/public/index.php b/projetphp/public/index.php deleted file mode 100644 index 79a13f7..0000000 --- a/projetphp/public/index.php +++ /dev/null @@ -1,28 +0,0 @@ -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; diff --git a/projetphp/src/Domain/Model.php b/projetphp/src/Domain/Model.php deleted file mode 100644 index 940c23b..0000000 --- a/projetphp/src/Domain/Model.php +++ /dev/null @@ -1,12 +0,0 @@ - - - \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/Recettes.php b/projetphp/src/Domain/Recettes/Recettes.php deleted file mode 100644 index 5912cb5..0000000 --- a/projetphp/src/Domain/Recettes/Recettes.php +++ /dev/null @@ -1,33 +0,0 @@ - -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; - - } - -} - - - -?> \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/RecettesRepository.php b/projetphp/src/Domain/Recettes/RecettesRepository.php deleted file mode 100644 index 75b21b7..0000000 --- a/projetphp/src/Domain/Recettes/RecettesRepository.php +++ /dev/null @@ -1,40 +0,0 @@ - - diff --git a/projetphp/src/Domain/Repository.php b/projetphp/src/Domain/Repository.php deleted file mode 100644 index 1d96973..0000000 --- a/projetphp/src/Domain/Repository.php +++ /dev/null @@ -1,99 +0,0 @@ - - - -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); -} - -} - - - - -?> diff --git a/projetphp/views/home.php b/projetphp/views/home.php deleted file mode 100644 index 3cb93ff..0000000 --- a/projetphp/views/home.php +++ /dev/null @@ -1,2 +0,0 @@ -

Coucou

- \ No newline at end of file diff --git a/projetphp/views/partials/header.php b/projetphp/views/partials/header.php deleted file mode 100644 index 792aadb..0000000 --- a/projetphp/views/partials/header.php +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - <?php echo V::getHeadTitle(); ?> - - - - - diff --git a/projetphp/views/recettes/index.php b/projetphp/views/recettes/index.php deleted file mode 100644 index df1e1f3..0000000 --- a/projetphp/views/recettes/index.php +++ /dev/null @@ -1,20 +0,0 @@ - - - -
-
- -
- -
-
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
-
diff --git a/public/assets/css/style.css b/public/assets/css/style.css new file mode 100644 index 0000000..2156e6d --- /dev/null +++ b/public/assets/css/style.css @@ -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; +} + + + diff --git a/projetphp/public/assets/images/Logo.jpg b/public/assets/images/Logo.jpg similarity index 100% rename from projetphp/public/assets/images/Logo.jpg rename to public/assets/images/Logo.jpg diff --git a/public/assets/js/login.js b/public/assets/js/login.js new file mode 100644 index 0000000..e14ca46 --- /dev/null +++ b/public/assets/js/login.js @@ -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." ); + } + + }) + } + }) + + } + + +}) \ No newline at end of file diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..8bbcd78 --- /dev/null +++ b/public/index.php @@ -0,0 +1,13 @@ +num_ingredient; + } + +} \ No newline at end of file diff --git a/src/Domain/Ingredients/IngredientRepository.php b/src/Domain/Ingredients/IngredientRepository.php new file mode 100644 index 0000000..daca14c --- /dev/null +++ b/src/Domain/Ingredients/IngredientRepository.php @@ -0,0 +1,175 @@ + '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' ); + } + +} \ No newline at end of file diff --git a/src/Domain/Ingredients/UseIngredientsInterface.php b/src/Domain/Ingredients/UseIngredientsInterface.php new file mode 100644 index 0000000..9019473 --- /dev/null +++ b/src/Domain/Ingredients/UseIngredientsInterface.php @@ -0,0 +1,38 @@ + 'bla' ] ); } - public function test( string $id ): void { - echo "Coucou" . $id; - } - } \ No newline at end of file diff --git a/src/Domain/Recettes/Recette.php b/src/Domain/Recettes/Recette.php new file mode 100644 index 0000000..6073250 --- /dev/null +++ b/src/Domain/Recettes/Recette.php @@ -0,0 +1,52 @@ +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; + } + +} \ No newline at end of file diff --git a/src/Domain/Recettes/RecetteRepository.php b/src/Domain/Recettes/RecetteRepository.php new file mode 100644 index 0000000..893bfe0 --- /dev/null +++ b/src/Domain/Recettes/RecetteRepository.php @@ -0,0 +1,162 @@ + '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 ); + } + +} \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/RecettesAPIController.php b/src/Domain/Recettes/RecettesAPIController.php similarity index 100% rename from projetphp/src/Domain/Recettes/RecettesAPIController.php rename to src/Domain/Recettes/RecettesAPIController.php diff --git a/projetphp/src/Domain/Recettes/RecettesController.php b/src/Domain/Recettes/RecettesController.php similarity index 55% rename from projetphp/src/Domain/Recettes/RecettesController.php rename to src/Domain/Recettes/RecettesController.php index 1ed1dc8..6264eb0 100644 --- a/projetphp/src/Domain/Recettes/RecettesController.php +++ b/src/Domain/Recettes/RecettesController.php @@ -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 ), + ] ); } } \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/RecettesManagementController.php b/src/Domain/Recettes/RecettesManagementController.php similarity index 100% rename from projetphp/src/Domain/Recettes/RecettesManagementController.php rename to src/Domain/Recettes/RecettesManagementController.php diff --git a/src/Domain/Repository.php b/src/Domain/Repository.php new file mode 100644 index 0000000..c6fb738 --- /dev/null +++ b/src/Domain/Repository.php @@ -0,0 +1,170 @@ + '', '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; + + +} diff --git a/src/Domain/Tags/Tag.php b/src/Domain/Tags/Tag.php new file mode 100644 index 0000000..56f1102 --- /dev/null +++ b/src/Domain/Tags/Tag.php @@ -0,0 +1,33 @@ +num_tag; + } + +} \ No newline at end of file diff --git a/src/Domain/Tags/TagRepository.php b/src/Domain/Tags/TagRepository.php new file mode 100644 index 0000000..e42e1f3 --- /dev/null +++ b/src/Domain/Tags/TagRepository.php @@ -0,0 +1,174 @@ + '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' ); + } + +} \ No newline at end of file diff --git a/src/Domain/Tags/UseTagsInterface.php b/src/Domain/Tags/UseTagsInterface.php new file mode 100644 index 0000000..90a6fa2 --- /dev/null +++ b/src/Domain/Tags/UseTagsInterface.php @@ -0,0 +1,38 @@ +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(); diff --git a/src/Domain/Utilisateurs/Utilisateurs.php b/src/Domain/Utilisateurs/Utilisateurs.php new file mode 100644 index 0000000..91ded8b --- /dev/null +++ b/src/Domain/Utilisateurs/Utilisateurs.php @@ -0,0 +1,23 @@ +num_user; + } + +} \ No newline at end of file diff --git a/src/Domain/Utilisateurs/UtilisateursRepository.php b/src/Domain/Utilisateurs/UtilisateursRepository.php new file mode 100644 index 0000000..a9d4bd9 --- /dev/null +++ b/src/Domain/Utilisateurs/UtilisateursRepository.php @@ -0,0 +1,75 @@ + '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' ); + } + + + + +} \ No newline at end of file diff --git a/projetphp/src/Exceptions/ConfigFailedLoadingException.php b/src/Exceptions/ConfigFailedLoadingException.php similarity index 100% rename from projetphp/src/Exceptions/ConfigFailedLoadingException.php rename to src/Exceptions/ConfigFailedLoadingException.php diff --git a/projetphp/src/Exceptions/InvalidRouteException.php b/src/Exceptions/InvalidRouteException.php similarity index 100% rename from projetphp/src/Exceptions/InvalidRouteException.php rename to src/Exceptions/InvalidRouteException.php diff --git a/projetphp/src/Exceptions/InvalidViewException.php b/src/Exceptions/InvalidViewException.php similarity index 100% rename from projetphp/src/Exceptions/InvalidViewException.php rename to src/Exceptions/InvalidViewException.php diff --git a/projetphp/src/Helpers/Authentification.php b/src/Helpers/Authentification.php similarity index 100% rename from projetphp/src/Helpers/Authentification.php rename to src/Helpers/Authentification.php diff --git a/projetphp/src/Helpers/AutoLoader.php b/src/Helpers/AutoLoader.php similarity index 100% rename from projetphp/src/Helpers/AutoLoader.php rename to src/Helpers/AutoLoader.php diff --git a/projetphp/src/Helpers/ConfigFactory.php b/src/Helpers/ConfigFactory.php similarity index 100% rename from projetphp/src/Helpers/ConfigFactory.php rename to src/Helpers/ConfigFactory.php diff --git a/src/Helpers/Markdown.php b/src/Helpers/Markdown.php new file mode 100644 index 0000000..bfe4b55 --- /dev/null +++ b/src/Helpers/Markdown.php @@ -0,0 +1,29 @@ + '$1', + '/\*(.*?)\*/' => '$1', + + // Titres + '/^## (.*?)$/m' => '

$1

', + '/^# (.*?)$/m' => '

$1

', + + ]; + } + + 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 ); + } +} \ No newline at end of file diff --git a/projetphp/src/Helpers/SanitizeTrait.php b/src/Helpers/SanitizeTrait.php similarity index 100% rename from projetphp/src/Helpers/SanitizeTrait.php rename to src/Helpers/SanitizeTrait.php diff --git a/projetphp/src/Http/JSONResponse.php b/src/Http/JSONResponse.php similarity index 100% rename from projetphp/src/Http/JSONResponse.php rename to src/Http/JSONResponse.php diff --git a/projetphp/src/Http/Request.php b/src/Http/Request.php similarity index 72% rename from projetphp/src/Http/Request.php rename to src/Http/Request.php index 7172602..a4c49df 100644 --- a/projetphp/src/Http/Request.php +++ b/src/Http/Request.php @@ -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(); + } + } \ No newline at end of file diff --git a/projetphp/src/Http/Route.php b/src/Http/Route.php similarity index 100% rename from projetphp/src/Http/Route.php rename to src/Http/Route.php diff --git a/projetphp/src/Http/Router.php b/src/Http/Router.php similarity index 91% rename from projetphp/src/Http/Router.php rename to src/Http/Router.php index 907a5ff..fb0d2c7 100644 --- a/projetphp/src/Http/Router.php +++ b/src/Http/Router.php @@ -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, '/' ); } } diff --git a/src/Infrastructure/Database.php b/src/Infrastructure/Database.php new file mode 100644 index 0000000..7209ad4 --- /dev/null +++ b/src/Infrastructure/Database.php @@ -0,0 +1,45 @@ +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() ); + } + } + +} \ No newline at end of file diff --git a/projetphp/src/Infrastructure/View.php b/src/Infrastructure/View.php similarity index 100% rename from projetphp/src/Infrastructure/View.php rename to src/Infrastructure/View.php diff --git a/projetphp/src/Kernel.php b/src/Kernel.php similarity index 80% rename from projetphp/src/Kernel.php rename to src/Kernel.php index 83017c6..485eafd 100644 --- a/projetphp/src/Kernel.php +++ b/src/Kernel.php @@ -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'] ); + } + } \ No newline at end of file diff --git a/projetphp/views/base.php b/views/base.php similarity index 100% rename from projetphp/views/base.php rename to views/base.php diff --git a/views/home.php b/views/home.php new file mode 100644 index 0000000..24534a6 --- /dev/null +++ b/views/home.php @@ -0,0 +1,32 @@ +

Coucou

+$md

"; ?> + + + +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); + +?> diff --git a/views/login.php b/views/login.php new file mode 100644 index 0000000..7835617 --- /dev/null +++ b/views/login.php @@ -0,0 +1,23 @@ + + +
+
auth' ); ?>' method='post'> + +
+ +
+ +
+ +
+ +
+ +
+ + + +
+
+ diff --git a/projetphp/views/partials/footer.php b/views/partials/footer.php similarity index 100% rename from projetphp/views/partials/footer.php rename to views/partials/footer.php diff --git a/views/partials/header.php b/views/partials/header.php new file mode 100644 index 0000000..292c5c5 --- /dev/null +++ b/views/partials/header.php @@ -0,0 +1,30 @@ + + + + + + <?php echo V::getHeadTitle(); ?> + + + + + diff --git a/views/partials/tag-sidebar.php b/views/partials/tag-sidebar.php new file mode 100644 index 0000000..23b25d9 --- /dev/null +++ b/views/partials/tag-sidebar.php @@ -0,0 +1,39 @@ + + + \ No newline at end of file diff --git a/views/recettes/index.php b/views/recettes/index.php new file mode 100644 index 0000000..974caff --- /dev/null +++ b/views/recettes/index.php @@ -0,0 +1,19 @@ + + + +
+
+ + + +
+

titre_recette; ?>

+
    +
  • Temps de préparation : temps_de_preparation; ?>
  • +
  • Nombre d'ingrédients : getNumberOfIngredients(); ?>
  • +
+
+
+ +
+