diff --git a/basededonnee/associations.txt b/basededonnee/associations.txt new file mode 100644 index 0000000..e69de29 diff --git a/basededonnee/bdphp/connexionsimple.php b/basededonnee/bdphp/connexionsimple.php new file mode 100644 index 0000000..4a8077c --- /dev/null +++ b/basededonnee/bdphp/connexionsimple.php @@ -0,0 +1,44 @@ + + +
+ !!! 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 new file mode 100644 index 0000000..106e50f --- /dev/null +++ b/basededonnee/codemysql/basedonneee.sql @@ -0,0 +1,156 @@ + +-- 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 new file mode 100644 index 0000000..7b535b4 --- /dev/null +++ b/basededonnee/contraintelisteingredientprobleme.txt @@ -0,0 +1,16 @@ + + +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 new file mode 100644 index 0000000..8210c99 --- /dev/null +++ b/basededonnee/entité.txt @@ -0,0 +1,45 @@ + + + + +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 new file mode 100644 index 0000000..ec4b522 --- /dev/null +++ b/basededonnee/test.drawio @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/projetphp/README.md b/projetphp/README.md new file mode 100644 index 0000000..9a34211 --- /dev/null +++ b/projetphp/README.md @@ -0,0 +1,57 @@ +# 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 new file mode 100644 index 0000000..6f1ab5d --- /dev/null +++ b/projetphp/config/BDD.php @@ -0,0 +1,53 @@ + + Code : getCode() ?>
Message : getMessage() ?> + + + + diff --git a/projetphp/config/general.php b/projetphp/config/general.php new file mode 100644 index 0000000..862b32d --- /dev/null +++ b/projetphp/config/general.php @@ -0,0 +1,8 @@ + 'http://127.0.0.1:8080/', + 'website_name' => 'Les recettes des Papis', + + 'website_path' => APP_ROOT, +]; \ No newline at end of file diff --git a/projetphp/config/route_arguments.php b/projetphp/config/route_arguments.php new file mode 100644 index 0000000..88b0198 --- /dev/null +++ b/projetphp/config/route_arguments.php @@ -0,0 +1,6 @@ + '([0-9]+)', + '{chars}' => '([A-Za-z]+)', + '{string}' => '([0-9A-Za-z]+)', +]; \ No newline at end of file diff --git a/projetphp/config/views.php b/projetphp/config/views.php new file mode 100644 index 0000000..28e6f05 --- /dev/null +++ b/projetphp/config/views.php @@ -0,0 +1,4 @@ + 'base' +]; \ No newline at end of file diff --git a/projetphp/public/assets/css/style.css b/projetphp/public/assets/css/style.css new file mode 100644 index 0000000..9e6b0eb --- /dev/null +++ b/projetphp/public/assets/css/style.css @@ -0,0 +1,182 @@ +/* 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/assets/images/Logo.jpg b/projetphp/public/assets/images/Logo.jpg new file mode 100644 index 0000000..8310814 Binary files /dev/null and b/projetphp/public/assets/images/Logo.jpg differ diff --git a/projetphp/public/index.php b/projetphp/public/index.php new file mode 100644 index 0000000..79a13f7 --- /dev/null +++ b/projetphp/public/index.php @@ -0,0 +1,28 @@ +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/Controller.php b/projetphp/src/Domain/Controller.php new file mode 100644 index 0000000..4311c0b --- /dev/null +++ b/projetphp/src/Domain/Controller.php @@ -0,0 +1,38 @@ + static::class, + 'routeMethods' => [ 'GET' ], + 'pageHeadTitle' => 'Page', + ]; + + $args = array_merge($defaults, $args); + return new Route( ...$args ); + } + + /** + * Permet de définir les routes du controller sous le format d'une liste d'objets Route. + * Vous pouvez utiliser la method self::Route() pour préremplir des champs. + * + * @return Route[] + * @see self::Route() + */ + abstract public static function defineRoutes(): array; + +} \ No newline at end of file diff --git a/projetphp/src/Domain/Model.php b/projetphp/src/Domain/Model.php new file mode 100644 index 0000000..940c23b --- /dev/null +++ b/projetphp/src/Domain/Model.php @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/projetphp/src/Domain/Pages/PagesController.php b/projetphp/src/Domain/Pages/PagesController.php new file mode 100644 index 0000000..3d8612b --- /dev/null +++ b/projetphp/src/Domain/Pages/PagesController.php @@ -0,0 +1,35 @@ + 'bla' ] ); + } + + public function test( string $id ): void { + echo "Coucou" . $id; + } + +} \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/Recettes.php b/projetphp/src/Domain/Recettes/Recettes.php new file mode 100644 index 0000000..5912cb5 --- /dev/null +++ b/projetphp/src/Domain/Recettes/Recettes.php @@ -0,0 +1,33 @@ + +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/RecettesAPIController.php b/projetphp/src/Domain/Recettes/RecettesAPIController.php new file mode 100644 index 0000000..ec76612 --- /dev/null +++ b/projetphp/src/Domain/Recettes/RecettesAPIController.php @@ -0,0 +1,16 @@ +recettes->list', routeAction: 'list', routeMethods: ['POST'] ), + ]; + + } +} \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/RecettesController.php b/projetphp/src/Domain/Recettes/RecettesController.php new file mode 100644 index 0000000..1ed1dc8 --- /dev/null +++ b/projetphp/src/Domain/Recettes/RecettesController.php @@ -0,0 +1,24 @@ +index', routeAction: 'index', pageHeadTitle: 'Liste des recettes' ), + self::Route( routeUrl: '/recettes/{string}', routeName: 'recettes->show', routeAction: 'show', pageHeadTitle: 'Recette' ), + ]; + + } + + public function index(): View { + return new View( 'recettes/index', [] ); + } + +} \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/RecettesManagementController.php b/projetphp/src/Domain/Recettes/RecettesManagementController.php new file mode 100644 index 0000000..1a4cdab --- /dev/null +++ b/projetphp/src/Domain/Recettes/RecettesManagementController.php @@ -0,0 +1,17 @@ +create', routeAction: 'create' ), + self::Route( routeUrl: '/recettes/edit/{int}', routeName: 'recettes->edit', routeAction: 'edit' ), + ]; + + } +} \ No newline at end of file diff --git a/projetphp/src/Domain/Recettes/RecettesRepository.php b/projetphp/src/Domain/Recettes/RecettesRepository.php new file mode 100644 index 0000000..75b21b7 --- /dev/null +++ b/projetphp/src/Domain/Recettes/RecettesRepository.php @@ -0,0 +1,40 @@ + + diff --git a/projetphp/src/Domain/Repository.php b/projetphp/src/Domain/Repository.php new file mode 100644 index 0000000..1d96973 --- /dev/null +++ b/projetphp/src/Domain/Repository.php @@ -0,0 +1,99 @@ + + + +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/src/Domain/Utilisateurs/AuthentificationController.php b/projetphp/src/Domain/Utilisateurs/AuthentificationController.php new file mode 100644 index 0000000..04c1d30 --- /dev/null +++ b/projetphp/src/Domain/Utilisateurs/AuthentificationController.php @@ -0,0 +1,57 @@ +auth', routeAction: 'auth', routeMethods: ['POST'] ), + self::Route( routeUrl: '/api/auth/logout', routeName: 'api->auth->logout', routeAction: 'logout', routeMethods: ['POST'] ), + + ]; + + } + + public function login(): View { + return new View( 'login' ); + } + + public function auth(): JSONResponse { + + Request::setCORS(); + + $username = Request::get( 'username' ); + $password = Request::get( 'password' ); + + // TODO : Récupération de l'utilisateur et verify_password. + + $userId = 1; + Authentification::loginUser( $userId ); + JSONResponse::sendSuccess( [ 'user_id' => $userId ] ); + + } + + public function logout(): JSONResponse { + + if( !Authentification::isLoggedIn() ) { + return JSONResponse::sendError( [ 'message' => 'Alrady disconnected' ] ); + } + + Authentification::destroySession(); + return JSONResponse::sendSuccess( [ 'message' => 'Logged out' ] ); + + } + +} \ No newline at end of file diff --git a/projetphp/src/Exceptions/ConfigFailedLoadingException.php b/projetphp/src/Exceptions/ConfigFailedLoadingException.php new file mode 100644 index 0000000..aea3887 --- /dev/null +++ b/projetphp/src/Exceptions/ConfigFailedLoadingException.php @@ -0,0 +1,28 @@ +configFilePath = $configFilePath; + $this->message = "Failed to load configuration file '{$configFilePath}'."; + parent::__construct(); + } + +} \ No newline at end of file diff --git a/projetphp/src/Exceptions/InvalidRouteException.php b/projetphp/src/Exceptions/InvalidRouteException.php new file mode 100644 index 0000000..4b97e00 --- /dev/null +++ b/projetphp/src/Exceptions/InvalidRouteException.php @@ -0,0 +1,15 @@ +clientRoute = $clientRoute; + $message = "{$clientRoute} doesn't exist"; + parent::__construct($message, 404); + } + +} \ No newline at end of file diff --git a/projetphp/src/Exceptions/InvalidViewException.php b/projetphp/src/Exceptions/InvalidViewException.php new file mode 100644 index 0000000..9447cde --- /dev/null +++ b/projetphp/src/Exceptions/InvalidViewException.php @@ -0,0 +1,13 @@ +data = $data; + $this->htmlCode = $code; + + $this->returnResponse(); + } + + public function returnResponse(): never { + + header( 'Content-type: application/json' ); + http_response_code( $this->htmlCode ); + + $this->data['_status'] = $this->htmlCode; + $json = json_encode( $this->data ); + echo $json; + die(); + } + + public static function sendSuccess( $data = [] ): self { + $data['success'] = true; + return new self( $data, 200 ); + } + + public static function sendError( $data = [] ): self { + $data['success'] = false; + return new self( $data, 400 ); + } + +} \ No newline at end of file diff --git a/projetphp/src/Http/Request.php b/projetphp/src/Http/Request.php new file mode 100644 index 0000000..7172602 --- /dev/null +++ b/projetphp/src/Http/Request.php @@ -0,0 +1,53 @@ +. + */ + public function __construct( + string $routeUrl, + string $routeName, + string $routeController, + string $routeAction, + array $routeMethods, + string $pageHeadTitle, + ){ + $this->routeUrl = $routeUrl; + $this->routeName = $routeName; + $this->routeController = $routeController; + $this->routeAction = $routeAction; + $this->routeMethods = $routeMethods; + $this->pageHeadTitle = $pageHeadTitle; + } + +} \ No newline at end of file diff --git a/projetphp/src/Http/Router.php b/projetphp/src/Http/Router.php new file mode 100644 index 0000000..907a5ff --- /dev/null +++ b/projetphp/src/Http/Router.php @@ -0,0 +1,199 @@ +routeMethods ) ){ + throw new InvalidRouteException( self::$clientRouteString ); + } else { + self::executeRouteAction(); + } + + } + + /** + * Permet de récupérer tous les controllers du dossier Domain. + * @return class-string[] + */ + private static function fetchControllers(): array { + + $classes = []; + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator( APP_ROOT . 'src/Domain', FilesystemIterator::SKIP_DOTS ), + ); + + foreach( $iterator as $file ){ + + if( $file->isFile() && $file->getExtension() === 'php' ){ + + $fileName = $file->getPathname(); + + // Transformation du chemin du fichier vers le nom complet de la classe. + $fileName = str_replace( APP_ROOT . 'src/', AutoLoader::PRIMARY_NAMESPACE, $fileName ); + $fileName = str_replace( '.php', '', $fileName ); + $fileName = str_replace( '/', '\\', $fileName ); + + + /* + * Vérifie que l'on est bien sur un Controller. + * Ignore Controller car Controller n'est pas une sous-classe de Controller. + */ + if( is_subclass_of( $fileName, Controller::class ) ){ + $classes[] = $fileName; + } + + } + + } + + return $classes; + + } + + /** + * Récupérer toutes les routes des controllers récupéré avant. + * @return Route[] + */ + private static function fetchRoutes(): array { + + $routes = []; + + foreach( self::$controllers as $controllerClassString ){ + $routes = array_merge( $routes, $controllerClassString::defineRoutes() ); + } + + return $routes; + } + + /** + * Permet de savoir si la route que le client veut existe. + * @return Route|bool Retourne la route de l'utilisateur ou false si inexistante. + */ + private static function clientRouteExist(): Route|bool { + + $clientRouteName = trim( self::$clientRouteString, '/' ); + + foreach( self::$routes as $route ){ + + $routeName = self::getRegexRoute( $route ); + if( preg_match( $routeName, $clientRouteName, $matches ) ){ + array_shift( $matches ); // On enlève la chaine complète. + self::$clientRouteArguments = $matches; + return $route; + } + } + + return false; + + } + + /** + * Permet de remplacer les expressions définies dans la configuration par leurs équivalents Regex. + * + * @param Route $route + * @return string + */ + private static function getRegexRoute( Route $route ): string { + $routeUrl = trim( $route->routeUrl, '/' ); + foreach ( Kernel::$configs['route_arguments'] as $key => $value ){ + $routeUrl = str_replace( $key, $value, $routeUrl ); + } + return "#^{$routeUrl}$#"; + } + + /** + * Va permettre d'exécuter la méthode du Controller→action() en parsant les arguments si existants. + * @return void + */ + private static function executeRouteAction(): void { + $controller = self::$clientRoute->routeController; + $method = self::$clientRoute->routeAction; + + new $controller()->$method( ...self::$clientRouteArguments); + } + + /** + * Permet d'obtenir l'adresse du site vers une route spécifique. + * + * @param string $routeName Le nom de la route. + * @param ...$args - Les arguments de la route, si existants. + * + * @return string Renvoie l'URL demandé ou bien la racine du site si la route n'existe pas. + */ + public static function getRouteURL( string $routeName, ...$args ): string { + foreach( self::$routes as $route ){ + if( $routeName === $route->routeName ){ + $i = 0; + $routeUrl = preg_replace_callback( '/\{([^}]+)}/', function( $match ) use ( $args, &$i ){ + return $args[$i++] ?? ""; + }, $route->routeUrl); + + return rtrim( Kernel::$configs['general']['website_url'] . $routeUrl, '/' ); + } + } + + return Kernel::$configs['general']['website_url']; + } + + public static function getAssetURL( string $assetPath ): string { + return Kernel::$configs['general']['website_url'] . 'assets/' . $assetPath; + } + +} \ No newline at end of file diff --git a/projetphp/src/Infrastructure/View.php b/projetphp/src/Infrastructure/View.php new file mode 100644 index 0000000..88ee12f --- /dev/null +++ b/projetphp/src/Infrastructure/View.php @@ -0,0 +1,220 @@ + + */ + public private(set) array $integratedContent; + + /** + * Garde la dernière instance de vue. + * Si il y a un squelette, ce sera l'instance du squelette. + * + * @var View + */ + private static self $lastInstance; + + /** + * Permet de construire les informations de la vue. + * + * @param string $viewName Le nom du fichier de vue. + * @param array $viewArgs Les arguments de la vue. + * @param bool $autoRender Si la méthode $this->render() est automatiquement exécuté. + * @param string|null $skeleton Le squelette du fichier, null correspond à aucun squelette, default correspond au + * squelette défini dans la configuration des vues. + * @param array $integratedContent Le contenu intégré dans le nouveau squelette. + */ + public function __construct( + string $viewName, + array $viewArgs = [], + bool $autoRender = true, + ?string $skeleton = 'default', + array $integratedContent = [] + ) { + + if( !str_ends_with( $viewName, '.php' ) ) { + $viewName .= '.php'; + } + + $this->viewName = $viewName; + $this->viewArgs = $viewArgs; + + $this->skeleton = $skeleton; + if( $this->skeleton === "default" ){ + $this->skeleton = Kernel::$configs['views']['base_view_skeleton']; + } + + $this->integratedContent = $integratedContent; + + try { + if (!file_exists(self::VIEW_PATH . $this->viewName)) { + throw new InvalidViewException($this->viewName); + } + + if ($autoRender) { + $this->render(); + } + } catch (InvalidViewException $e) { + die( $e->getMessage() ); + } + + } + + /** + * Permet de démarrer le rendu d'une vue. + * @return void + */ + public function startView(): void { + ob_start(); + } + + /** + * Permet de finir le rendu d'une vue. + * @return void + */ + public function endView(): void { + echo ob_get_clean(); + } + + /** + * Permet de faire le rendu proprement de la vue. + * @return void + */ + public function render(): void { + + self::$lastInstance = $this; + + if( $this->skeleton === null ){ + + // Si on a pas de squelette, on inclut la vue. + // pour accéder aux éléments de la vue, on utilise les méthodes statiques de cette classe. + $this->startView(); + require self::VIEW_PATH . $this->viewName; + $this->endView(); + + } else { + + // Si on a un squelette, on charge tout le contenu de la vue enfante. + $contentName = $this->viewName; + + // On démarre la vue du squelette. + $base = new View( $this->skeleton, $this->viewArgs, skeleton: null, integratedContent: [ 'content' => $contentName ] ); + + } + + + } + + /* + * VIEW TOOLS + */ + + /** + * Permet d'injecter un contenu sauvegardé dans $this->integratedContent. + * + * @param string $integratedContentName + * @return void N'affiche rien si le contenu n'existe pas. + */ + public static function inject( string $integratedContentName ): void { + if( isset( self::$lastInstance->integratedContent[ $integratedContentName ] ) ) + require self::VIEW_PATH . self::$lastInstance->integratedContent[ $integratedContentName ]; + } + + /** + * Permet de récupérer un argument passé à la vue. + * + * @param string $argumentName + * @return mixed null si l'argument n'existe pas. + */ + public static function arg( string $argumentName ): mixed { + if( isset( self::$lastInstance->viewArgs[ $argumentName ] ) ) + return self::$lastInstance->viewArgs[ $argumentName ]; + return null; + } + + /** + * Permet d'intégrer un bloc à la vue. + * + * @param string $partialName + * @return void N'affiche rien si le bloc n'existe pas. + */ + public static function partial( string $partialName ): void { + + if( !str_ends_with( $partialName, '.php' ) ) { + $partialName .= '.php'; + } + + if( file_exists( self::VIEW_PATH . 'partials/' . $partialName ) ) { + require self::VIEW_PATH . 'partials/' . $partialName; + } + } + + /** + * Permet d'obtenir le titre de la page à mettre dans + * @return string + */ + public static function getHeadTitle(): string { + $siteUrl = Kernel::$configs['general']['website_name']; + return Router::$clientRoute->pageHeadTitle . ' - ' . $siteUrl; + } + + /** + * Permet d'obtenir une route + * + * @param string $routeName Le nom de la route. + * @param ...$args - Les arguments de la route, si existant. + * + * @return void Affiche uniquement. + */ + public static function routeUrl( string $routeName, ...$args ): void { + echo Router::getRouteURL( $routeName, ...$args ); + } + + /** + * Permet d'obtenir l'URL vers un asset (CSS,JS,Images). + * @param string $assetPath + * + * @return void + */ + public static function assetUrl( string $assetPath ): void { + echo Router::getAssetURL( $assetPath ); + } + +} \ No newline at end of file diff --git a/projetphp/src/Kernel.php b/projetphp/src/Kernel.php new file mode 100644 index 0000000..83017c6 --- /dev/null +++ b/projetphp/src/Kernel.php @@ -0,0 +1,100 @@ + [ 'cle" → valeur ] ] + * + * @var array + */ + public private(set) static array $configs = []; + + /** + * Instance actuelle de l'application. + * @var Kernel|null + */ + private static ?self $instance = null; + + /** + * Méthode qui permet de démarrer le site. + * @return self + */ + public static function start(): self { + self::$instance = new self(); + self::$instance->init(); + return self::$instance; + } + + /** + * Permet d'obtenir l'instance actuelle du site. + * @return self + */ + public static function getInstance(): self { + return self::$instance; + } + + /** + * Constructeur. + */ + public function __construct() { + } + + /** + * Permet de préparer le démarrage du site. + * Lancé automatiquement par start(). + * + * @return void + * @see self::start() + */ + public function init(): void { + $this->buildAutoloader(); + $this->loadConfig(); + + Authentification::startSession(); + + try { + Router::routeTo(); + } catch ( InvalidRouteException $e ){ + die( $e->getMessage() ); + } + } + + /** + * Permet de mettre en place l'Autoloader. + * @return void + */ + private function buildAutoloader(): void { + require_once 'Helpers/AutoLoader.php'; + AutoLoader::register(); + } + + /** + * Permet de charger tous les fichiers de configuration du site. + * @return void + */ + private function loadConfig(): void { + try { + + self::$configs['general'] = ConfigFactory::loadConfigFile('general'); + self::$configs['route_arguments'] = ConfigFactory::loadConfigFile('route_arguments'); + self::$configs['views'] = ConfigFactory::loadConfigFile('views'); + + } catch( ConfigFailedLoadingException $e ){ + die( $e->getMessage() ); + } + } + +} \ No newline at end of file diff --git a/projetphp/views/base.php b/projetphp/views/base.php new file mode 100644 index 0000000..2a8631b --- /dev/null +++ b/projetphp/views/base.php @@ -0,0 +1,9 @@ + + + + +
+ +
+ + diff --git a/projetphp/views/home.php b/projetphp/views/home.php new file mode 100644 index 0000000..3cb93ff --- /dev/null +++ b/projetphp/views/home.php @@ -0,0 +1,2 @@ +

Coucou

+ \ No newline at end of file diff --git a/projetphp/views/partials/footer.php b/projetphp/views/partials/footer.php new file mode 100644 index 0000000..8015192 --- /dev/null +++ b/projetphp/views/partials/footer.php @@ -0,0 +1,14 @@ + + +
+ +
+ Front end : Bousquet Sébastien + --- Back end : Thorel Benjamin + --- Base de donné : Glaudis Jordan +
+
+ + diff --git a/projetphp/views/partials/header.php b/projetphp/views/partials/header.php new file mode 100644 index 0000000..792aadb --- /dev/null +++ b/projetphp/views/partials/header.php @@ -0,0 +1,23 @@ + + + + + + <?php echo V::getHeadTitle(); ?> + + + + + diff --git a/projetphp/views/recettes/index.php b/projetphp/views/recettes/index.php new file mode 100644 index 0000000..df1e1f3 --- /dev/null +++ b/projetphp/views/recettes/index.php @@ -0,0 +1,20 @@ + + + +
+
+ +
+ +
+
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+