Compare commits
5 Commits
Front-End-
...
Aco3
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c3de7ccf3 | |||
| 59b9829feb | |||
| 7b886fef86 | |||
| b1f3806983 | |||
| 455eee67f7 |
58
README.md
58
README.md
@@ -1 +1,57 @@
|
||||
# Les Recettes des Papis
|
||||
# Les recettes des Papis
|
||||
|
||||
## Installation
|
||||
- Décompressez l'archive du site.
|
||||
- Pointez votre domaine sur le dossier "public".
|
||||
- Modifiez les fichiers de configuration dans "config".
|
||||
|
||||
## Informations pratiques sur le PHP.
|
||||
|
||||
- Ce site utilise le principe des routes. Afin de ne pas avoir x fichiers publiques identiques pour chaque page.
|
||||
- Tout est géré par le fichier public/index.php.
|
||||
- index.php va charger le Kernel (Noyau) de l'application.
|
||||
- Le noyau fonctionne sur le principe d'élément unique. La méthode ``Kernel::getInstance()`` va permettre d'avoir l'unique instance de la classe Kernel.
|
||||
- Une fois ``Kernel::getInstance()->init()`` déclenché, le noyau enregistre l'autoloading des fichiers, puis enregistre la configuration.
|
||||
- La configuration sont des fichiers dans le dossier config/ qui vont renvoyer un tableau clé/valeur. Exemple
|
||||
|
||||
```php
|
||||
// Fichier config/test.php
|
||||
<?php return [
|
||||
'cle_1' => "Val1",
|
||||
'cle_2' => "Val2",
|
||||
// ...
|
||||
] ?>
|
||||
```
|
||||
|
||||
- Les fichiers de configuration sont chargés via la méthode ``ConfigFactory::loadConfigFile()`` qui va vérifier si le fichier de configuration existe et le charge en conséquence.
|
||||
- On enchaine ensuite sur le Router.
|
||||
- Le Router est une classe utilitaire qui va permettre de faire le lien entre la route saisie ``/test/...`` de l'utilisateur et les routes enregistrés par le site.
|
||||
- Le Router va d'abord commencer par récupérer la route voulue par l'utilisateur.
|
||||
- Puis, le router va rechercher tous les Controllers existants.
|
||||
- Un Controller est une classe qui va permettre de définir les routes et de dire, qu'est-ce qui se passe quand je prends cette route. Les routes du Controller sont définis par la methode ``Controller::defineRoutes()``
|
||||
- Pour définir les routes dans la méthode précédente, vous pouvez soit créer un objet ``Route`` en remplissant tous les champs, ou bien la méthode ``Controller::Route()`` qui va préremplir certains champs.
|
||||
- Exemple de Controller
|
||||
```php
|
||||
class TestController extends Controller {
|
||||
|
||||
public static function defineRoutes(): array {
|
||||
return [
|
||||
self::Route( routeUrl: '/test', routeName: "Test", routeAction: "test" ),
|
||||
new Route( routeUrl: "/test2", routeName: "Test2", routeController: "TestController", routeAction: "test2", routeMethods = [ 'GET' ] )
|
||||
];
|
||||
}
|
||||
|
||||
public function test(){
|
||||
echo "Je suis déclenché lorsque j'atteinds la route /test";
|
||||
}
|
||||
|
||||
public function test2(){
|
||||
echo "Je suis déclenché lorsque j'atteinds la route /test2 !!!";
|
||||
}
|
||||
}
|
||||
```
|
||||
- Retournons au routeur, pour chercher les controllers, il va scanner les fichiers du dossier src/Domain avec un Iterator Récursif. (Pour se simplifier un peu la tâche.)
|
||||
- Il va traiter le nom du chemin absolu pour obtenir le nom de la classe, puis va vérifier si cette classe est une sous-classe de Controller.
|
||||
- Après avoir trouvé tous les Controllers, il va récupérer toutes les routes de chaque controller.
|
||||
- Enfin, il va vérifier si la route utilisateur correspond à une des routes dans sa liste, si ça correspond, le Router va charger la méthode ``new {RouteController}()->{RouteAction}()``
|
||||
- Si par exemple, le router trouve la route ``/test`` voulu par l'utilisateur, il va déclencher la méthode ``new TestController()->test()``.
|
||||
57
acceuil.php
57
acceuil.php
@@ -1,57 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Statique</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include "header.php"?>
|
||||
|
||||
<div class="main-body">
|
||||
<div class="content">
|
||||
<div class="title-site">
|
||||
<h1 class="title">Les Recettes De Papis</h1>
|
||||
<hr class="line-acceuil">
|
||||
<h3>Proposition de recette :</h3>
|
||||
</div>
|
||||
<div class="random-proposition">
|
||||
|
||||
<a class="recette-icone" href="recette-en-dur.php">
|
||||
<img class="recette-preview-image" src="random-recette.jpg">
|
||||
<div class="recette-icone-content">
|
||||
<h3>Tarte au Pomme</h3>
|
||||
<ul>
|
||||
<li>Temps de préparation : 5ans</li>
|
||||
<li>Nombre d'ingrédient : 48548964</li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> <img class="recette-preview-image" src="random-recette.jpg">
|
||||
<div class="recette-icone-content">
|
||||
<h3>Tarte au Pomme</h3>
|
||||
<ul>
|
||||
<li>Temps de préparation : 5ans</li>
|
||||
<li>Nombre d'ingrédient : 48548964</li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> <img class="recette-preview-image" src="random-recette.jpg">
|
||||
<div class="recette-icone-content">
|
||||
<h3>Tarte au Pomme</h3>
|
||||
<ul>
|
||||
<li>Temps de préparation : 5ans</li>
|
||||
<li>Nombre d'ingrédient : 48548964</li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include "footer.php"?>
|
||||
</body>
|
||||
<script src="main.js"></script>
|
||||
</html>
|
||||
94
ajout.php
94
ajout.php
@@ -1,94 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Statique</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include "header.php"?>
|
||||
|
||||
<div class="main-body">
|
||||
<div class="content">
|
||||
<div class="recette-add-form-all">
|
||||
<form class="recette-form recette-form-add" action="/api/recettes/create.php" method="POST">
|
||||
<legend><h1>Nouvelle Recette : </h1></legend>
|
||||
<div class="recette-form-group form-group">
|
||||
<label for="nom">Titre : </label>
|
||||
<input type="texte" class="form-control" id="recette-form-nom" name="nom" placeholder="Titre de votre recette">
|
||||
</div>
|
||||
<div class="recette-form-group form-group">
|
||||
<label for="temps">Temps (en min) : </label>
|
||||
<input type="texte" class="form-control" id="recette-form-temps" name="temps" placeholder="Temps de préparation">
|
||||
</div>
|
||||
<div class="recette-form-group form-group">
|
||||
<label for="image">Photo du plat : </label>
|
||||
<input type="file" class="form-control" id="recette-form-photo" name="image" placeholder="ajouter votre image">
|
||||
</div>
|
||||
<div class="recette-form-group form-group" id="recette-form-div-ingr">
|
||||
<label for="ingr">Ingredients</label>
|
||||
<select multiple class="form-control" id="recette-form-ingr" name="ingr" placeholder="Ingredients">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="recette-form-group form-group" id="recette-form-div-tag">
|
||||
<label for="tag">Tag</label>
|
||||
<select multiple class="form-control" id="recette-form-tag" name="tag" placeholder="Tag">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="recette-form-group form-group" id="recette-form-div-desc">
|
||||
<label for="description">Description</label>
|
||||
<textarea type="texte" class="form-control" id="recette-form-description" name="description" placeholder="Description..."></textarea>
|
||||
</div>
|
||||
|
||||
<button type='submit' class='btn btn-primary'>Confirmer</button>
|
||||
</form>
|
||||
<div class="recette-form-add-additional">
|
||||
<form id="recette-form-ingr-add" class="recette-form recette-form-add-ingr" action="" method="POST">
|
||||
<legend><h2>Nouvelle Ingrédient :</h2></legend>
|
||||
<div class="recette-form-group form-group">
|
||||
<label for="name">Nom de l'Ingrédient</label>
|
||||
<input type="texte" class="form-control" id="recette-form-ingr-nom" name="name" placeholder="Nom de l'ingrédient" value="">
|
||||
</div>
|
||||
<div class="recette-form-group form-group">
|
||||
<label for="image">Photo de l'Ingrédient</label>
|
||||
<input type="file" class="form-control" id="recette-form-ingr-photo" name="image">
|
||||
</div>
|
||||
|
||||
<button id="submit-ingr" type='submit' class='btn btn-primary'>Confirmer</button>
|
||||
<div id="recette-form-div-err-ingr" class="err">
|
||||
Erreur
|
||||
</div>
|
||||
</form>
|
||||
<form id="recette-form-tag-add" class="recette-form recette-form-add-tag" action="" method="POST">
|
||||
<legend><h2>Nouveau Tag : </h2></legend>
|
||||
<div class="recette-form-group form-group">
|
||||
<label for="tag">Tags</label>
|
||||
<input type="texte" class="form-control" id="recette-form-tag" name="tag" placeholder="Nom de l'ingrédient">
|
||||
</div>
|
||||
|
||||
<button type='submit' class='btn btn-primary'>Confirmer</button>
|
||||
<div id="recette-form-div-err-tag" class="err">
|
||||
Erreur
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include "footer.php"?>
|
||||
</body>
|
||||
<script src="main.js"></script>
|
||||
</html>
|
||||
9
config/database.php
Normal file
9
config/database.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'host' => 'localhost',
|
||||
'port' => 3306,
|
||||
'user' => 'admin',
|
||||
'pass' => 'adminpass123',
|
||||
'name' => 'siterecette'
|
||||
];
|
||||
8
config/general.php
Normal file
8
config/general.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'website_url' => 'http://127.0.0.1:8080/',
|
||||
'website_name' => 'Les recettes des Papis',
|
||||
|
||||
'website_path' => APP_ROOT,
|
||||
];
|
||||
6
config/route_arguments.php
Normal file
6
config/route_arguments.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'{int}' => '([0-9]+)',
|
||||
'{chars}' => '([A-Za-z]+)',
|
||||
'{string}' => '([0-9A-Za-z]+)',
|
||||
];
|
||||
4
config/views.php
Normal file
4
config/views.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return [
|
||||
'base_view_skeleton' => 'base'
|
||||
];
|
||||
10
footer.php
10
footer.php
@@ -1,10 +0,0 @@
|
||||
<footer id="footer">
|
||||
<div class="src">
|
||||
<a href="https://code.romhackplaza.org/Benjamin/LesRecettesDePapis" target="_blank">Notre Gitea</a>
|
||||
</div>
|
||||
<div class="Contact">
|
||||
Front end : Bousquet Sébastien
|
||||
--- Back end : Thorel Benjamin
|
||||
--- Base de donné : Glaudis Jordan
|
||||
</div>
|
||||
</footer>
|
||||
18
header.php
18
header.php
@@ -1,18 +0,0 @@
|
||||
<header id="header">
|
||||
<div id="logo">
|
||||
<a href="index.php">
|
||||
<img src="Logo.jpg" class="logo">
|
||||
</a>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="nav-list">
|
||||
<li><a id="google" class="nav-element" href="index.php">Acceuil</a></li>
|
||||
<li><a id="unTest" class="nav-element" href="index.php">Liste des recettes</a></li>
|
||||
<li><a id="login" class= "nav-element" href="login.php">Login</a></li>
|
||||
<li><a id="addBouton" class="nav-element" href="ajout.php">Ajouter une Recette</a></li>
|
||||
<li><a id="ingrManage" class="nav-element" href="listeIngr.php">Liste des Ingrédients</a></li>
|
||||
<li><a id="tagManage" class="nav-element" href="listeTag.php">Manage Tag</a></li>
|
||||
</ul>
|
||||
<input type="text" class="search-form search-form-recette" placeholder="Rechercher une recette ...">
|
||||
</nav>
|
||||
</header>
|
||||
25
index.php
25
index.php
@@ -1,25 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Statique</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include "header.php"?>
|
||||
|
||||
<div class="main-body">
|
||||
<div class="sidebar">
|
||||
<?php include "sidebar.php" ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<?php include "recette.php" ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include "footer.php"?>
|
||||
</body>
|
||||
<script src="main.js"></script>
|
||||
</html>
|
||||
48
listeIngr.js
48
listeIngr.js
@@ -1,48 +0,0 @@
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
|
||||
|
||||
let modifier_button = document.getElementsByClassName("ingr-modifier");
|
||||
|
||||
console.log(modifier_button);
|
||||
|
||||
for (const element of modifier_button) {
|
||||
element.addEventListener('click', function () {
|
||||
let modif = element.parentElement.previousSibling.previousSibling;
|
||||
|
||||
let data = element.getAttribute("data-id");
|
||||
|
||||
let form = document.createElement("form");
|
||||
|
||||
form.setAttribute("method", "POST");
|
||||
form.setAttribute("action", "/ingredient/edit/" + data);
|
||||
form.setAttribute("class","liste-ingr-elem-text")
|
||||
|
||||
let input = document.createElement("input");
|
||||
|
||||
input.setAttribute("type", "text");
|
||||
input.setAttribute("class", "form-control");
|
||||
input.setAttribute("name","modif_ingr");
|
||||
input.setAttribute("placeholder", "modificiation");
|
||||
|
||||
let hidden = document.createElement("input");
|
||||
|
||||
hidden.setAttribute("type","hidden");
|
||||
hidden.setAttribute("name","id");
|
||||
hidden.setAttribute("value", data);
|
||||
|
||||
let button = document.createElement("button");
|
||||
|
||||
button.setAttribute("type", "submit");
|
||||
button.setAttribute("class", "btn btn-primary");
|
||||
button.innerText = "Submit Modification";
|
||||
|
||||
form.append(input)
|
||||
form.append(hidden)
|
||||
form.append(button)
|
||||
|
||||
modif.replaceWith(form);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
@@ -1,67 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Statique</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include "header.php"?>
|
||||
|
||||
<div class="main-body">
|
||||
<div class="content">
|
||||
<div class="liste-ingr-elem">
|
||||
<img class="ingr-image" src="pomme.jpeg">
|
||||
<div class="liste-ingr-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
<div class="liste-ingr-elem-modif">
|
||||
<a class="recette-button ingr-modifier" data-id="juh">Modifier</a>
|
||||
<a class="recette-button ingr-suppr">Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-ingr-elem">
|
||||
<img class="ingr-image" src="pomme.jpeg">
|
||||
<div class="liste-ingr-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-ingr-elem">
|
||||
<img class="ingr-image" src="pomme.jpeg">
|
||||
<div class="liste-ingr-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-ingr-elem">
|
||||
<img class="ingr-image" src="pomme.jpeg">
|
||||
<div class="liste-ingr-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-ingr-elem">
|
||||
<img class="ingr-image" src="pomme.jpeg">
|
||||
<div class="liste-ingr-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-ingr-elem">
|
||||
<img class="ingr-image" src="pomme.jpeg">
|
||||
<div class="liste-ingr-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-ingr-elem">
|
||||
<img class="ingr-image" src="pomme.jpeg">
|
||||
<div class="liste-ingr-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include "footer.php"?>
|
||||
</body>
|
||||
<script src="listeIngr.js"></script>
|
||||
</html>
|
||||
62
listeTag.js
62
listeTag.js
@@ -1,62 +0,0 @@
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
|
||||
|
||||
let modifier_button = document.getElementsByClassName("tag-modifier");
|
||||
|
||||
console.log(modifier_button);
|
||||
|
||||
for (const element of modifier_button) {
|
||||
element.addEventListener('click', function () {
|
||||
let modif = element.parentElement.previousSibling.previousSibling;
|
||||
|
||||
let data = element.getAttribute("data-id");
|
||||
|
||||
let form = document.createElement("form");
|
||||
|
||||
form.setAttribute("method", "POST");
|
||||
form.setAttribute("action", "/tag/edit/" + data);
|
||||
form.setAttribute("class","liste-tag-elem-text")
|
||||
|
||||
let input = document.createElement("input");
|
||||
|
||||
input.setAttribute("type", "text");
|
||||
input.setAttribute("class", "form-control");
|
||||
input.setAttribute("name","modif_tag");
|
||||
input.setAttribute("placeholder", "modificiation");
|
||||
|
||||
let hidden = document.createElement("input");
|
||||
|
||||
hidden.setAttribute("type","hidden");
|
||||
hidden.setAttribute("name","id");
|
||||
hidden.setAttribute("value", data);
|
||||
|
||||
let button = document.createElement("button");
|
||||
|
||||
button.setAttribute("type", "submit");
|
||||
button.setAttribute("class", "btn btn-primary");
|
||||
button.innerText = "Submit Modification";
|
||||
|
||||
form.append(input)
|
||||
form.append(hidden)
|
||||
form.append(button)
|
||||
|
||||
modif.replaceWith(form);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
let suppr = document.getElementsByClassName("tag-suppr");
|
||||
|
||||
for (const element of suppr) {
|
||||
element.addEventListener('click', function () {
|
||||
let confirm = window.confirm("Voulez vous supprimez ?");
|
||||
|
||||
let data = element.getAttribute("data-id");
|
||||
|
||||
if (confirm) {
|
||||
window.location.href = "/tag/delete/" + data;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
67
listeTag.php
67
listeTag.php
@@ -1,67 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Statique</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include "header.php"?>
|
||||
|
||||
<div class="main-body">
|
||||
<div class="content">
|
||||
<div class="liste-tag-elem">
|
||||
<div class="liste-tag-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
<div class="liste-tag-elem-modif">
|
||||
<a class="recette-button tag-modifier" data-id="juh">Modifier</a>
|
||||
<a class="recette-button tag-suppr" data-id="juh">Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-tag-elem">
|
||||
|
||||
<div class="liste-tag-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-tag-elem">
|
||||
|
||||
<div class="liste-tag-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-tag-elem">
|
||||
|
||||
<div class="liste-tag-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-tag-elem">
|
||||
|
||||
<div class="liste-tag-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-tag-elem">
|
||||
|
||||
<div class="liste-tag-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
<div class="liste-tag-elem">
|
||||
|
||||
<div class="liste-tag-elem-text">
|
||||
Un Element
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include "footer.php"?>
|
||||
</body>
|
||||
<script src="listeTag.js"></script>
|
||||
</html>
|
||||
36
login.php
36
login.php
@@ -1,36 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Statique</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include "header.php"?>
|
||||
|
||||
<div class="main-body">
|
||||
<div class=formcont>
|
||||
<form id='user-infos-01' action='".$action."' method='post'>
|
||||
|
||||
<div class='form-group'>
|
||||
<label for='logmsg'>Se Connecter</label>
|
||||
</div>
|
||||
<div class='form-group '>
|
||||
<input type='text' class='form-control' name='username' id='username' value='' placeholder="Pseudo">
|
||||
</div>
|
||||
|
||||
<div class='form-group '>
|
||||
<input type='text' class='form-control' name='password' id='password' value='' placeholder="Mot de Passe">
|
||||
</div>
|
||||
|
||||
<button type='submit' class='btn btn-primary'>Confirmer</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include "footer.php"?>
|
||||
</body>
|
||||
</html>
|
||||
96
main.js
96
main.js
@@ -1,96 +0,0 @@
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
|
||||
function formulaire_ingredient_update(event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
console.log(event);
|
||||
|
||||
let form = document.getElementById("recette-form-ingr-add");
|
||||
|
||||
let cible = document.getElementById("recette-form-ingr");
|
||||
|
||||
let ingr = form.nom;
|
||||
|
||||
let form_data = FormData(form);
|
||||
|
||||
fetch("/api/ingredients/create", {
|
||||
method : "POST",
|
||||
body: form_data
|
||||
}).then( reponse => {
|
||||
if (!reponse.ok) {
|
||||
let div_err = document.getElementById("recette-form-div-err-ingr");
|
||||
|
||||
div_err.style.display = "inherit";
|
||||
div_err.innerText = "Erreur de connection au serveur";
|
||||
|
||||
}
|
||||
else {
|
||||
reponse.json().then( data => {
|
||||
if (data.success) {
|
||||
let new_elem = document.createElement("option");
|
||||
|
||||
new_elem.setAttribute("value",ingr.value);
|
||||
new_elem.innerText = ingr.value;
|
||||
|
||||
cible.appendChild(new_elem);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function formulaire_tag_update(event){
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
let form = document.getElementById("recette-form-tag-add");
|
||||
|
||||
let cible = document.getElementById("recette-form-tag");
|
||||
|
||||
let tag = form.tag;
|
||||
|
||||
let form_data = FormData(form);
|
||||
|
||||
fetch("/api/tags/create", {
|
||||
method : "POST",
|
||||
body: form_data
|
||||
}).then( reponse => {
|
||||
if (!reponse.ok) {
|
||||
let div_err = document.getElementById("recette-form-div-err-tag");
|
||||
|
||||
div_err.style.display = "inherit";
|
||||
div_err.innerText = "Erreur de connection au serveur";
|
||||
|
||||
}
|
||||
else {
|
||||
reponse.json().then( data => {
|
||||
if (data.success) {
|
||||
let new_elem = document.createElement("option");
|
||||
|
||||
new_elem.setAttribute("value",tag.value);
|
||||
new_elem.innerText = tag.value;
|
||||
|
||||
cible.appendChild(new_elem);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
let ingr_form = document.getElementById("recette-form-ingr-add");
|
||||
|
||||
ingr_form.addEventListener('submit', formulaire_ingredient_update);
|
||||
|
||||
let tag_form = document.getElementById("recette-form-tag-add");
|
||||
|
||||
tag_form.addEventListener('submit', formulaire_tag_update);
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
BIN
pomme.jpeg
BIN
pomme.jpeg
Binary file not shown.
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -57,7 +57,7 @@ nav {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.nav-list{
|
||||
.nav-list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -67,20 +67,10 @@ nav {
|
||||
border-radius: 40px;
|
||||
}
|
||||
|
||||
.nav-list li {
|
||||
/*border: 1px solid black;*/
|
||||
border-radius: 2000px;
|
||||
}
|
||||
|
||||
.nav-list li:hover {
|
||||
background-color: #00d8a2;
|
||||
}
|
||||
|
||||
|
||||
.nav-element {
|
||||
line-height: 2.5;
|
||||
padding: 10px;
|
||||
margin: 4px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
@@ -94,13 +84,12 @@ nav {
|
||||
|
||||
#login {
|
||||
border: 1px solid black;
|
||||
border-radius: 2000px;
|
||||
border-radius: 6px;
|
||||
background-color: #ffc478;
|
||||
}
|
||||
|
||||
#login:hover {
|
||||
background-color: #0bbd90;
|
||||
box-shadow: 1px 1px 7px black;
|
||||
}
|
||||
|
||||
/*Body et son contenu */
|
||||
@@ -131,33 +120,6 @@ body {
|
||||
/*display:none;*/
|
||||
}
|
||||
|
||||
.title-site {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.random-proposition {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.recommandation img {
|
||||
max-width: calc( 7vh + 7vw );
|
||||
max-height: calc( 7vh + 7vw );
|
||||
}
|
||||
|
||||
.line-acceuil {
|
||||
color: #03be90;
|
||||
box-shadow: 20px 6px 8px #007054;
|
||||
}
|
||||
|
||||
.tag-selected-div {
|
||||
flex: 4;
|
||||
}
|
||||
@@ -180,8 +142,6 @@ body {
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-top: .5%;
|
||||
padding: .5% 0 .5% .5%;
|
||||
border: 1px solid black;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0px 0px 10px rgb(131, 131, 131);
|
||||
@@ -230,7 +190,6 @@ nav {
|
||||
background-clip: border-box;
|
||||
padding-right: 2%;
|
||||
padding-left: 2%;
|
||||
padding-bottom: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -333,8 +292,9 @@ ul {
|
||||
/*image*/
|
||||
.recette-image {
|
||||
background-color: red;
|
||||
max-height: calc( 20vh + 20vw );
|
||||
max-width: calc( 20vh + 20vw );
|
||||
aspect-ratio: 1/1;
|
||||
max-height: calc( 30vh + 30vw );
|
||||
max-width: calc( 30vh + 30vw );
|
||||
border: 1px solid white;
|
||||
border-radius: 30px;
|
||||
}
|
||||
@@ -344,35 +304,6 @@ ul {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.recette-liste-ingr-div {
|
||||
margin-left: 120px;
|
||||
padding-left: 100px;
|
||||
width: 75%;
|
||||
border: 1px solid black;
|
||||
border-radius: 200px;
|
||||
box-shadow: 7px 4px 7px black;
|
||||
}
|
||||
|
||||
.recette-liste-ingr-elem {
|
||||
display: flex;
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.ingr-image-prev {
|
||||
max-height: 50px;
|
||||
max-width: 50px;
|
||||
border: 1px solid black;
|
||||
border-radius: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.recette-liste-ingr-elem-nom {
|
||||
height: 100%;
|
||||
width: 15%;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.recette-div-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -487,23 +418,11 @@ ul {
|
||||
box-shadow: 2px 2px 0px #000000;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: .5% 2%;
|
||||
border: 2px solid black;
|
||||
border-radius: 10px;
|
||||
margin-top: .5%;
|
||||
margin-left: .5%;
|
||||
background-color: #ffd9a0;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #0bbd90;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
padding: 1%;
|
||||
border-radius: 10px;
|
||||
box-shadow: 3px 4px 5px #8c8c8c;
|
||||
}
|
||||
@@ -513,141 +432,30 @@ ul {
|
||||
}
|
||||
|
||||
.recette-form {
|
||||
height: 80%;
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid black;
|
||||
background: #fff9f1;
|
||||
border-radius: 20px;
|
||||
box-shadow: 2px 2px 3px #a06100;
|
||||
}
|
||||
|
||||
.recette-form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#recette-form-div-desc {
|
||||
height: 220px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#recette-form-description {
|
||||
height: 95%;
|
||||
}
|
||||
|
||||
.recette-add-form-all {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.recette-form-add {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.recette-form-add-ingr {
|
||||
height: 30%;
|
||||
}
|
||||
|
||||
.recette-form-add-tag {
|
||||
height: 30%;
|
||||
}
|
||||
|
||||
.recette-form-add-additional{
|
||||
flex: 1;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.err {
|
||||
color: red;
|
||||
display: none;
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.liste-ingr-elem {
|
||||
display: flex;
|
||||
height: 100px;
|
||||
padding:5px;
|
||||
background-color: #c6fdefe0;
|
||||
color: #000000;
|
||||
border: 1px solid black;
|
||||
border-radius: 10px;
|
||||
box-shadow: 1px 1px 1px black;
|
||||
margin-top: .25%;
|
||||
margin-bottom: .25%;
|
||||
}
|
||||
|
||||
.liste-ingr-elem:hover {
|
||||
border:3px solid #007054;
|
||||
box-shadow: 2px 2px 10px black;
|
||||
}
|
||||
|
||||
.liste-ingr-elem-text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex:12;
|
||||
padding-left: 20px;
|
||||
line-height: 100px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.liste-ingr-elem-modif {
|
||||
line-height: 100px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.liste-tag-elem {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
padding:5px;
|
||||
background-color: #c6fdefe0;
|
||||
color: #000000;
|
||||
border: 1px solid black;
|
||||
border-radius: 10px;
|
||||
box-shadow: 1px 1px 1px black;
|
||||
margin-top: .25%;
|
||||
margin-bottom: .25%;
|
||||
}
|
||||
|
||||
.liste-tag-elem:hover {
|
||||
border:3px solid #007054;
|
||||
box-shadow: 2px 2px 10px black;
|
||||
}
|
||||
|
||||
.liste-tag-elem-text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex:12;
|
||||
padding-left: 20px;
|
||||
line-height: 40px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.liste-tag-elem-modif {
|
||||
line-height: 40px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.ingr-image {
|
||||
flex: 1;
|
||||
border: 1px solid black;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
/*Footer et son contenue*/
|
||||
footer{
|
||||
padding: 25px;
|
||||
background: rgb(11, 189, 144);
|
||||
color: white;
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
66
public/assets/js/login.js
Normal file
66
public/assets/js/login.js
Normal file
@@ -0,0 +1,66 @@
|
||||
document.addEventListener( 'DOMContentLoaded', function(){
|
||||
const FORM = document.getElementById( 'login-form' );
|
||||
const FORM_ERROR = document.getElementById( 'login-errors' );
|
||||
if( !FORM )
|
||||
return;
|
||||
|
||||
/**
|
||||
* Permet d'afficher une erreur dans le formulaire.
|
||||
* @param message
|
||||
*/
|
||||
FORM.showError = function( message ){
|
||||
FORM_ERROR.style.display = 'block';
|
||||
FORM_ERROR.innerText = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de retirer l'erreur du formulaire.
|
||||
*/
|
||||
FORM.removeError = function(){
|
||||
FORM_ERROR.style.display = 'none';
|
||||
FORM_ERROR.innerText = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère l'envoi du formulaire.
|
||||
* @param e
|
||||
* @returns {boolean}
|
||||
*/
|
||||
FORM.onsubmit = function( e ){
|
||||
|
||||
e.preventDefault();
|
||||
FORM.removeError();
|
||||
|
||||
let userField = e.target.username;
|
||||
let passwordField = e.target.password;
|
||||
|
||||
if( !userField || !passwordField || userField.value === "" || passwordField.value === "" ){
|
||||
FORM.showError( "Le nom d'utilisateur ou le mot de passe ne doivent pas être vide.");
|
||||
return false;
|
||||
}
|
||||
|
||||
let formData = new FormData( FORM );
|
||||
let options = {
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
body: formData,
|
||||
}
|
||||
|
||||
fetch( FORM.action, options ).then( ( response ) => {
|
||||
if( response.ok ){
|
||||
response.json().then( ( responseJSON ) => {
|
||||
|
||||
if( responseJSON.success === true ){
|
||||
window.location.href = window.location.origin; // Redirection sur la page d'accueil si succès.
|
||||
} else {
|
||||
FORM.showError( responseJSON.message || "Mauvais nom d'utilisateur ou mauvais mot de passe." );
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
13
public/index.php
Normal file
13
public/index.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
use App\Kernel;
|
||||
|
||||
if( !defined( 'APP_ROOT' ) )
|
||||
|
||||
/**
|
||||
* Définit la racine de l'application en chemin absolue.
|
||||
*/
|
||||
define( 'APP_ROOT', realpath( __DIR__ . '/../') . '/' );
|
||||
|
||||
require_once '../src/Kernel.php';
|
||||
Kernel::start();
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
@@ -1,72 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Statique</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include "header.php"?>
|
||||
|
||||
<div class="main-body">
|
||||
<div class="content">
|
||||
<div class="recette-title">
|
||||
<h1>TARTE AU POMME</h1>
|
||||
</div>
|
||||
<div class="recette-content">
|
||||
<div class="recette-div-info">
|
||||
<div class="recette-div-image">
|
||||
<img class="recette-image" src="random-recette.jpg">
|
||||
</div>
|
||||
<div class="recette-div-liste-info">
|
||||
<ul class="recette-list-buttons">
|
||||
<li><a class="recette-button" href="UneLienSurUneAPI.php">Supprimer</a></li>
|
||||
<li><a class="recette-button" href="UnAutreLienSurUneAPI.php">Modifier</a></li>
|
||||
</ul>
|
||||
<ul class="recette-liste-info">
|
||||
<li class="recette-liste-info-elem">27 mars 2026</li>
|
||||
<li class="recette-liste-info-elem">5 ans</li>
|
||||
<li class="recette-liste-info-elem">458946</li>
|
||||
</ul>
|
||||
<ul class="recette-liste-tag">
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
<li class="tag tag-unselected">tag</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="recette-liste-ingr-div">
|
||||
<h3>Liste des ingrédients</h3>
|
||||
<ul class="recette-liste-ingr">
|
||||
<li class="recette-liste-ingr-elem"><img class="ingr-image-prev" src="pomme.jpeg"> <div class="recette-liste-ingr-elem-nom">Une pomme</div></li>
|
||||
<li class="recette-liste-ingr-elem"><img class="ingr-image-prev" src="pomme.jpeg"> <div class="recette-liste-ingr-elem-nom">Une pomme</div></li>
|
||||
<li class="recette-liste-ingr-elem"><img class="ingr-image-prev" src="pomme.jpeg"> <div class="recette-liste-ingr-elem-nom">Une pomme</div></li>
|
||||
<li class="recette-liste-ingr-elem"><img class="ingr-image-prev" src="pomme.jpeg"> <div class="recette-liste-ingr-elem-nom">Une pomme</div></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class = "recette-desc">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ultricies aliquet dignissim. Proin venenatis libero at erat rutrum consectetur. Morbi ut justo ex. Quisque ante mi, feugiat nec turpis ac, feugiat tristique magna. Nunc vitae aliquet mauris, consectetur convallis magna. Suspendisse rutrum sagittis tempor. Sed maximus vulputate vestibulum. Nam arcu enim, finibus id vestibulum in, cursus ac urna. Sed sodales, lacus hendrerit varius sollicitudin, dolor erat porttitor est, fringilla iaculis metus justo et sapien. Ut eget pharetra orci. Morbi at egestas tellus. Praesent porta elit non tempus rhoncus. Aliquam erat volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nunc vitae convallis eros, et tristique leo. Vestibulum vitae ultrices erat.
|
||||
Sed pellentesque tempor purus, eu ullamcorper risus lacinia in. Etiam quis rhoncus risus, quis lobortis sem. Quisque pulvinar faucibus lectus, ut gravida neque aliquam non. Pellentesque ornare blandit imperdiet. Proin sed elit quis dui pharetra volutpat et sagittis tortor. Quisque nec blandit lacus. Nulla varius neque metus, ut feugiat enim commodo a. Phasellus gravida fermentum tortor at lobortis. Nam tellus libero, interdum non mollis ut, faucibus id orci. Suspendisse laoreet lorem ex, vitae maximus tortor tincidunt id. Nullam id enim id lectus egestas commodo sit amet sed est. Phasellus enim est, ornare vitae feugiat at, ornare a sapien. Nullam interdum, lectus vitae commodo vulputate, metus ligula congue massa, quis lobortis lorem nisl quis augue. Nullam vestibulum et odio non luctus. Donec vel molestie nunc, ut ultrices ligula.
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ultricies aliquet dignissim. Proin venenatis libero at erat rutrum consectetur. Morbi ut justo ex. Quisque ante mi, feugiat nec turpis ac, feugiat tristique magna. Nunc vitae aliquet mauris, consectetur convallis magna. Suspendisse rutrum sagittis tempor. Sed maximus vulputate vestibulum. Nam arcu enim, finibus id vestibulum in, cursus ac urna. Sed sodales, lacus hendrerit varius sollicitudin, dolor erat porttitor est, fringilla iaculis metus justo et sapien. Ut eget pharetra orci. Morbi at egestas tellus. Praesent porta elit non tempus rhoncus. Aliquam erat volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nunc vitae convallis eros, et tristique leo. Vestibulum vitae ultrices erat.
|
||||
Sed pellentesque tempor purus, eu ullamcorper risus lacinia in. Etiam quis rhoncus risus, quis lobortis sem. Quisque pulvinar faucibus lectus, ut gravida neque aliquam non. Pellentesque ornare blandit imperdiet. Proin sed elit quis dui pharetra volutpat et sagittis tortor. Quisque nec blandit lacus. Nulla varius neque metus, ut feugiat enim commodo a. Phasellus gravida fermentum tortor at lobortis. Nam tellus libero, interdum non mollis ut, faucibus id orci. Suspendisse laoreet lorem ex, vitae maximus tortor tincidunt id. Nullam id enim id lectus egestas commodo sit amet sed est. Phasellus enim est, ornare vitae feugiat at, ornare a sapien. Nullam interdum, lectus vitae commodo vulputate, metus ligula congue massa, quis lobortis lorem nisl quis augue. Nullam vestibulum et odio non luctus. Donec vel molestie nunc, ut ultrices ligula.
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ultricies aliquet dignissim. Proin venenatis libero at erat rutrum consectetur. Morbi ut justo ex. Quisque ante mi, feugiat nec turpis ac, feugiat tristique magna. Nunc vitae aliquet mauris, consectetur convallis magna. Suspendisse rutrum sagittis tempor. Sed maximus vulputate vestibulum. Nam arcu enim, finibus id vestibulum in, cursus ac urna. Sed sodales, lacus hendrerit varius sollicitudin, dolor erat porttitor est, fringilla iaculis metus justo et sapien. Ut eget pharetra orci. Morbi at egestas tellus. Praesent porta elit non tempus rhoncus. Aliquam erat volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nunc vitae convallis eros, et tristique leo. Vestibulum vitae ultrices erat.
|
||||
Sed pellentesque tempor purus, eu ullamcorper risus lacinia in. Etiam quis rhoncus risus, quis lobortis sem. Quisque pulvinar faucibus lectus, ut gravida neque aliquam non. Pellentesque ornare blandit imperdiet. Proin sed elit quis dui pharetra volutpat et sagittis tortor. Quisque nec blandit lacus. Nulla varius neque metus, ut feugiat enim commodo a. Phasellus gravida fermentum tortor at lobortis. Nam tellus libero, interdum non mollis ut, faucibus id orci. Suspendisse laoreet lorem ex, vitae maximus tortor tincidunt id. Nullam id enim id lectus egestas commodo sit amet sed est. Phasellus enim est, ornare vitae feugiat at, ornare a sapien. Nullam interdum, lectus vitae commodo vulputate, metus ligula congue massa, quis lobortis lorem nisl quis augue. Nullam vestibulum et odio non luctus. Donec vel molestie nunc, ut ultrices ligula.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include "footer.php"?>
|
||||
</body>
|
||||
</html>
|
||||
22
recette.php
22
recette.php
@@ -1,22 +0,0 @@
|
||||
<div class="recettes">
|
||||
<a class="recette-icone" href="recette-en-dur.php">
|
||||
<img class="recette-preview-image" src="random-recette.jpg">
|
||||
<div class="recette-icone-content">
|
||||
<h3>Tarte au Pomme</h3>
|
||||
<ul>
|
||||
<li>Temps de préparation : 5ans</li>
|
||||
<li>Nombre d'ingrédient : 48548964</li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 2 </a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 3 </a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 4 </a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 5 </a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 6 </a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 7 </a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 8 </a>
|
||||
<a class="recette-icone" href="recette-en-dur.php"> 9 </a>
|
||||
</div>
|
||||
|
||||
|
||||
74
sidebar.php
74
sidebar.php
@@ -1,74 +0,0 @@
|
||||
<script>
|
||||
function test(){
|
||||
let rep = window.confirm("continuer ?");
|
||||
window.alert("rep " + rep);
|
||||
}
|
||||
</script>
|
||||
<div class="tag-cont">
|
||||
<h4>Tag</h4>
|
||||
<div class="tag-selected-div">
|
||||
<ul>
|
||||
<li class="tag tag-selected">Tag 1</li>
|
||||
<li class="tag tag-selected">Tag 2</li>
|
||||
</ul>
|
||||
</div>
|
||||
<form class="sidebar-search" action="none">
|
||||
<input type="text" class="search-form search-form-tag" name="search-tag" placeholder="Rechercher..." >
|
||||
</form>
|
||||
<div class="tag-unselected-div">
|
||||
<ul>
|
||||
<li class="tag tag-unselected" onclick="test()">Tag 1</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
<li class="tag tag-unselected">Tag 2</li>
|
||||
<li class="tag tag-unselected">Tag 3</li>
|
||||
<li class="tag tag-unselected">Tag 4</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr id="hr">
|
||||
<div class="ingr-cont">
|
||||
<h4>Ingrédient</h4>
|
||||
<div class="tag-selected-div">
|
||||
<ul>
|
||||
<li class="tag tag-selected">ingr 1</li>
|
||||
<li class="tag tag-selected">ingr 2</li>
|
||||
</ul>
|
||||
</div>
|
||||
<form class="sidebar-search" action="none">
|
||||
<input type="text" class="search-form search-form-tag" name="search-ingr" placeholder="Rechercher..." >
|
||||
</form>
|
||||
<div class="tag-unselected-div">
|
||||
<ul>
|
||||
<li class="tag tag-unselected" onclick="test()">ingr 1</li>
|
||||
<li class="tag tag-unselected">ingr 2</li>
|
||||
<li class="tag tag-unselected">ingr 3</li>
|
||||
<li class="tag tag-unselected">ingr 4</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
41
src/Domain/Controller.php
Normal file
41
src/Domain/Controller.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain;
|
||||
|
||||
use App\Http\Route;
|
||||
|
||||
/**
|
||||
* Classe abstraite des controllers qui vont pouvoir permettre de gérer les routes et leur retour.
|
||||
*/
|
||||
abstract class Controller {
|
||||
|
||||
/**
|
||||
* Va permettre de créer une route en préremplissant certains arguments.
|
||||
* Pour les paramètres, veuillez voir le constructeur d'une Route.
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @return Route
|
||||
*
|
||||
* @see Route::__construct()
|
||||
*/
|
||||
public static function Route( ...$args ): Route {
|
||||
$defaults = [
|
||||
'routeController' => 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;
|
||||
|
||||
}
|
||||
33
src/Domain/Ingredients/Ingredient.php
Normal file
33
src/Domain/Ingredients/Ingredient.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Ingredients;
|
||||
|
||||
use App\Domain\Model;
|
||||
|
||||
/**
|
||||
* Classe qui va gérer les ingrédients.
|
||||
*/
|
||||
class Ingredient extends Model {
|
||||
|
||||
/**
|
||||
* Le numéro de l'ingrédient.
|
||||
* @var int
|
||||
*/
|
||||
public int $num_ingredient;
|
||||
|
||||
/**
|
||||
* Le nom de l'ingrédient
|
||||
* @var string
|
||||
*/
|
||||
public string $nom_ingredient;
|
||||
|
||||
/**
|
||||
* Retourne le numéro de l'ingrédient.
|
||||
* @return int
|
||||
*/
|
||||
public function getID(): int
|
||||
{
|
||||
return $this->num_ingredient;
|
||||
}
|
||||
|
||||
}
|
||||
175
src/Domain/Ingredients/IngredientRepository.php
Normal file
175
src/Domain/Ingredients/IngredientRepository.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Ingredients;
|
||||
|
||||
use App\Domain\LinkableInterface;
|
||||
use App\Domain\Recettes\Recette;
|
||||
use App\Domain\Repository;
|
||||
use App\Domain\Model;
|
||||
use App\Domain\Tags\Tag;
|
||||
use App\Kernel;
|
||||
|
||||
/**
|
||||
* Classe qui va permettre de gérer les requêtes BDD en lien avec les ingrédients.
|
||||
* Les ingrédients sont liables à d'autres entités comme les Recettes.
|
||||
*/
|
||||
class IngredientRepository extends Repository implements LinkableInterface {
|
||||
|
||||
/**
|
||||
* Permet d'avoir le nom de l'entité dont dépend ce repo.
|
||||
* @return string
|
||||
*/
|
||||
public static function getEntity(): string
|
||||
{
|
||||
return Ingredient::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* La structure de notre table dans la BDD.
|
||||
* Un champ link_recettes a été ajouté pour donner le nom de la table de lien entre nos recettes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getStructure(): array
|
||||
{
|
||||
return [
|
||||
'table' => 'Ingredient',
|
||||
'columns' => [
|
||||
'num_ingredient', 'nom_ingredient'
|
||||
],
|
||||
'link_recettes' => 'Listeingredient'
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir une liste de tous les ingrédients objet Ingreident.
|
||||
*
|
||||
* @return Ingredient[]|null
|
||||
*/
|
||||
public function getAll(): ?array {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName};";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir un ingrédient spécifique par son ID.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return Ingredient|null
|
||||
*/
|
||||
public function getByID( int $id ): ?Ingredient {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_ingredient = {$id}";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null || count( $results ) > 1 )
|
||||
return null;
|
||||
return $results[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir, sous forme de liste, toutes les entrées qui lient des ingrédients.
|
||||
*
|
||||
* @param string $linkedTo La table qui permet de faire la liaison des ingrédients avec une autre entité.
|
||||
* @param string $linkingField Le champ qui permet de faire la liaison des ingrédients avec une autre entité.
|
||||
* @param Model $linkedEntity L'autre entité.
|
||||
*
|
||||
* @see LinkableInterface
|
||||
* @return array|null
|
||||
*/
|
||||
public function getIdLinkedTo( string $linkedTo, string $linkingField, Model $linkedEntity ): ?array {
|
||||
|
||||
$linkName = 'link_' . $linkedTo;
|
||||
if( !isset( $this->globalStructure[$linkName]))
|
||||
return null;
|
||||
|
||||
$sqlQuery = "SELECT * FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()};";
|
||||
$results = $this->selectGetAll($sqlQuery, true);
|
||||
if( $results === null )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'ajouter un lien entre un ingrédient et une autre entité comme Recette.
|
||||
*
|
||||
* @param string $linkedTo
|
||||
* @param string $linkingField
|
||||
* @param Model $linkedEntity
|
||||
* @param Model $ingredientEntity
|
||||
*
|
||||
* @see LinkableInterface
|
||||
* @return bool
|
||||
*/
|
||||
public function addLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $ingredientEntity ): bool {
|
||||
|
||||
$linkName = 'link_' . $linkedTo;
|
||||
if( !isset( $this->globalStructure[$linkName]))
|
||||
return false;
|
||||
|
||||
$query = "INSERT INTO {$this->globalStructure[$linkName]} ({$linkingField},num_ingredient) VALUES ({$linkedEntity->getId()}, {$ingredientEntity->getID()});";
|
||||
$statement = Kernel::$DB->pdo->prepare( $query );
|
||||
|
||||
return $statement->execute();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire un lien dans la BDD entre un ingrédient et une autre entité.
|
||||
*
|
||||
* @param string $linkedTo
|
||||
* @param string $linkingField
|
||||
* @param Model $linkedEntity
|
||||
* @param Model $ingredientEntity
|
||||
*
|
||||
* @see LinkableInterface
|
||||
* @return bool
|
||||
*/
|
||||
public function removeLinkBetween(string $linkedTo, string $linkingField, Model $linkedEntity, Model $ingredientEntity ): bool
|
||||
{
|
||||
$linkName = 'link_' . $linkedTo;
|
||||
if( !isset( $this->globalStructure[$linkName]))
|
||||
return false;
|
||||
|
||||
$query = "DELETE FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()} AND num_ingredient = {$ingredientEntity->getId()};";
|
||||
$statement = Kernel::$DB->pdo->prepare( $query );
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajouter un ingrédient dans la BDD.
|
||||
*
|
||||
* @param Model $ingredient
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function add( Model $ingredient ): bool {
|
||||
return $this->addEntity( $ingredient );
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un ingrédient dans la BDD.
|
||||
*
|
||||
* @param Model $ingredient
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function update( Model $ingredient ): bool {
|
||||
return $this->updateEntity( $ingredient, 'num_ingredient' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un ingrédient de la BDD.
|
||||
*
|
||||
* @param Model $ingredient
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete( Model $ingredient ): bool {
|
||||
return $this->deleteEntity( $ingredient, 'num_ingredient' );
|
||||
}
|
||||
|
||||
}
|
||||
38
src/Domain/Ingredients/UseIngredientsInterface.php
Normal file
38
src/Domain/Ingredients/UseIngredientsInterface.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Ingredients;
|
||||
|
||||
use App\Domain\Model;
|
||||
|
||||
/**
|
||||
* Interface utilisée par tous les repository qui sont en lien avec les ingrédients.
|
||||
*/
|
||||
interface UseIngredientsInterface {
|
||||
|
||||
/**
|
||||
* Permet de récupérer tous les ingrédients liés à ce Modèle.
|
||||
* @return array|null
|
||||
*/
|
||||
public function getAllLinkedIngredients( Model $entity ): ?array;
|
||||
|
||||
/**
|
||||
* Permet d'ajouter un ingrédient en lien avec notre autre entité.
|
||||
*
|
||||
* @param Ingredient $ingredient
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function addAnIngredient( Ingredient $ingredient, Model $entity ): bool;
|
||||
|
||||
/**
|
||||
* Permet de retirer un lien entre un ingrédient et une autre entité.
|
||||
*
|
||||
* @param Ingredient $ingredient
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeAnIngredient( Ingredient $ingredient, Model $entity ): bool;
|
||||
|
||||
}
|
||||
51
src/Domain/LinkableInterface.php
Normal file
51
src/Domain/LinkableInterface.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain;
|
||||
|
||||
/**
|
||||
* Interface pour dire qu'un objet peut avoir un lien avec un autre objet par une table.
|
||||
* Il s'agit d'une interface qui s'utilise sur des repositories de meta-données (Tag, Ingrédients)
|
||||
*
|
||||
* Les champs de toutes les méthodes se présentent de la même façon.
|
||||
* $linkedTo : Champ qui va désigner le nom de la table de liens que vous avez dans getStructure().
|
||||
* $linkingField : Champ dans la BDD qui va permettre de lier l'élément courant et l'étranger.
|
||||
* $linkedEntity : Une instance de l'entité étrangère que l'on veut lier.
|
||||
*/
|
||||
interface LinkableInterface {
|
||||
|
||||
/**
|
||||
* Permet de récupérer tous les liens entre notre entité implémentée et notre entité étrangère.
|
||||
*
|
||||
* @param string $linkedTo
|
||||
* @param string $linkingField
|
||||
* @param Model $linkedEntity
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getIdLinkedTo( string $linkedTo, string $linkingField, Model $linkedEntity ): ?array;
|
||||
|
||||
/**
|
||||
* Permet d'ajouter un lien entre notre entité implémentée et notre entité étrangère.
|
||||
*
|
||||
* @param string $linkedTo
|
||||
* @param string $linkingField
|
||||
* @param Model $linkedEntity
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function addLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $entity ): bool;
|
||||
|
||||
/**
|
||||
* Permet de retirer un lien entre notre entité implémentée et notre entité étrangère.
|
||||
*
|
||||
* @param string $linkedTo
|
||||
* @param string $linkingField
|
||||
* @param Model $linkedEntity
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $entity ): bool;
|
||||
|
||||
}
|
||||
16
src/Domain/Model.php
Normal file
16
src/Domain/Model.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain;
|
||||
|
||||
/**
|
||||
* Classe abstraite pour gérer l'implémentation des entités de données.
|
||||
*/
|
||||
abstract class Model {
|
||||
|
||||
/**
|
||||
* Getter de l'ID primaire du model spécifique.
|
||||
* @return int
|
||||
*/
|
||||
abstract public function getID(): int;
|
||||
|
||||
}
|
||||
34
src/Domain/Pages/PagesController.php
Normal file
34
src/Domain/Pages/PagesController.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Pages;
|
||||
|
||||
use App\Domain\Controller;
|
||||
use App\Infrastructure\View;
|
||||
|
||||
/**
|
||||
* Controller pour les pages sans lien avec un contenu spécifique.
|
||||
* Exemple : Page d'accueil.
|
||||
*/
|
||||
class PagesController extends Controller {
|
||||
|
||||
/**
|
||||
* Définit les routes globales d'acceuil.
|
||||
* @return array|\App\Http\Route[]
|
||||
*/
|
||||
public static function defineRoutes(): array
|
||||
{
|
||||
return [
|
||||
self::Route( routeUrl: '/', routeName: 'home', routeAction: 'index', pageHeadTitle: "Home Page" ),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Route de la page d'accueil.
|
||||
* @return void
|
||||
*/
|
||||
public function index(): View {
|
||||
return new View( 'home', [ 'ok' => 'bla' ] );
|
||||
}
|
||||
|
||||
}
|
||||
52
src/Domain/Recettes/Recette.php
Normal file
52
src/Domain/Recettes/Recette.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Recettes;
|
||||
|
||||
use App\Domain\Ingredients\Ingredient;
|
||||
use App\Domain\Ingredients\IngredientRepository;
|
||||
use App\Domain\Ingredients\UseIngredientsInterface;
|
||||
use App\Domain\Model;
|
||||
use App\Helpers\Markdown;
|
||||
|
||||
/**
|
||||
* Classe qui va permettre de gérer tous les objets recette.
|
||||
*/
|
||||
class Recette extends Model {
|
||||
|
||||
public int $num_recette;
|
||||
public string $titre_recette;
|
||||
public string $slug;
|
||||
public string $description_recette;
|
||||
public string $photo;
|
||||
public string $publication_date;
|
||||
public int $temps_de_preparation;
|
||||
|
||||
public function getID(): int
|
||||
{
|
||||
return $this->num_recette;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit la description de Markdown à HTML.
|
||||
* @return string
|
||||
*/
|
||||
public function getHTMLDescription(): string {
|
||||
return Markdown::convertToHTML( $this->description_recette );
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une liste de tous les ingrédients liés à la recette.
|
||||
* @return Ingredient[]|null
|
||||
*/
|
||||
public function getAllLinkedIngredients(): ?array
|
||||
{
|
||||
return new RecetteRepository()->getAllLinkedIngredients( $this );
|
||||
}
|
||||
|
||||
public function getNumberOfIngredients(): int {
|
||||
|
||||
$response = $this->getAllLinkedIngredients();
|
||||
return $response !== null ? count( $response ) : 0;
|
||||
}
|
||||
|
||||
}
|
||||
162
src/Domain/Recettes/RecetteRepository.php
Normal file
162
src/Domain/Recettes/RecetteRepository.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Recettes;
|
||||
|
||||
use App\Domain\Ingredients\Ingredient;
|
||||
use App\Domain\Ingredients\IngredientRepository;
|
||||
use App\Domain\Ingredients\UseIngredientsInterface;
|
||||
use App\Domain\Model;
|
||||
use App\Domain\Repository;
|
||||
use App\Domain\Tags\Tag;
|
||||
use App\Domain\Tags\TagRepository;
|
||||
use App\Domain\Tags\UseTagsInterface;
|
||||
|
||||
/**
|
||||
* Classe qui permet de faire le lien entre la BDD et le site pour les recettes.
|
||||
* Les recettes sont en lien avec les ingrédients.
|
||||
*/
|
||||
class RecetteRepository extends Repository implements UseIngredientsInterface, UseTagsInterface {
|
||||
|
||||
public static function getEntity(): string
|
||||
{
|
||||
return Recette::class;
|
||||
}
|
||||
|
||||
public static function getStructure(): array
|
||||
{
|
||||
return [
|
||||
'table' => 'Recette',
|
||||
'columns' => [
|
||||
'num_recette', 'titre_recette', 'slug', 'description_recette', 'photo', 'publication_date', 'temps_de_preparation'
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir une liste de toutes les recettes objet Recette.
|
||||
*
|
||||
* @return Recette[]|null
|
||||
*/
|
||||
public function getAll(): ?array {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName};";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir toutes les recettes paginées.
|
||||
* Mise par défaut dans l'ordre croissant du titre et avec une pagination de 15 éléments.
|
||||
*
|
||||
* @param int $page
|
||||
* @param int $pagination
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getAllRecettesBrowse( int $page = 1, int $pagination = 15 ){
|
||||
|
||||
if( $page <= 0 )
|
||||
$page = 1;
|
||||
|
||||
$offset = ( $page - 1 ) * $pagination;
|
||||
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName} ORDER BY titre_recette ASC LIMIT {$pagination} OFFSET {$offset};";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'avoir une recette par un ID.
|
||||
*
|
||||
* @param int $id
|
||||
* @return Recette|null
|
||||
*/
|
||||
public function getByID( int $id ): ?Recette {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_recette = {$id}";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null || count( $results ) > 1 )
|
||||
return null;
|
||||
return $results[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'avoir une recette par un slug.
|
||||
*
|
||||
* @param string $slug
|
||||
* @return Recette|null
|
||||
*/
|
||||
public function getBySlug( string $slug ): ?Recette {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE slug = {$slug}";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null || count( $results ) > 1 )
|
||||
return null;
|
||||
return $results[0];
|
||||
}
|
||||
|
||||
public function add( Model $recette ): bool {
|
||||
return $this->addEntity( $recette );
|
||||
}
|
||||
|
||||
public function update( Model $recette ): bool {
|
||||
return $this->updateEntity( $recette, 'num_recette' );
|
||||
}
|
||||
|
||||
public function delete( Model $recette ): bool {
|
||||
return $this->deleteEntity( $recette, 'num_recette' );
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getAllLinkedIngredients(Model $entity): ?array
|
||||
{
|
||||
$ingredientRepo = new IngredientRepository();
|
||||
$response = $ingredientRepo->getIdLinkedTo( 'recettes', 'num_recette', $entity );
|
||||
if( $response === null )
|
||||
return null;
|
||||
|
||||
return array_map( function($arr) use($ingredientRepo) {
|
||||
return $ingredientRepo->getByID( $arr['num_ingredient'] );
|
||||
}, $response );
|
||||
}
|
||||
|
||||
public function addAnIngredient(Ingredient $ingredient, Model $entity): bool
|
||||
{
|
||||
$ingredientRepo = new IngredientRepository();
|
||||
return $ingredientRepo->addLinkBetween( 'recettes', 'num_recette', $entity, $ingredient );
|
||||
}
|
||||
|
||||
public function removeAnIngredient(Ingredient $ingredient, Model $entity): bool
|
||||
{
|
||||
$ingredientRepo = new IngredientRepository();
|
||||
return $ingredientRepo->removeLinkBetween( 'recettes', 'num_recette', $entity, $ingredient );
|
||||
}
|
||||
|
||||
public function getAllLinkedTags(Model $entity): ?array
|
||||
{
|
||||
$tagRepo = new TagRepository();
|
||||
$response = $tagRepo->getIdLinkedTo( 'recettes', 'num_recette', $entity );
|
||||
if( $response === null )
|
||||
return null;
|
||||
|
||||
return array_map( function($arr) use($tagRepo) {
|
||||
return $tagRepo->getByID( $arr['num_tag'] );
|
||||
}, $response );
|
||||
}
|
||||
|
||||
public function addATag(Tag $tag, Model $entity): bool
|
||||
{
|
||||
$tagRepo = new TagRepository();
|
||||
return $tagRepo->addLinkBetween( 'recettes', 'num_recette', $entity, $tag );
|
||||
}
|
||||
|
||||
public function removeATag(Tag $tag, Model $entity): bool
|
||||
{
|
||||
$tagRepo = new TagRepository();
|
||||
return $tagRepo->removeLinkBetween( 'recettes', 'num_recette', $entity, $tag );
|
||||
}
|
||||
|
||||
}
|
||||
16
src/Domain/Recettes/RecettesAPIController.php
Normal file
16
src/Domain/Recettes/RecettesAPIController.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Recettes;
|
||||
|
||||
use App\Domain\Controller;
|
||||
|
||||
class RecettesAPIController extends Controller {
|
||||
|
||||
public static function defineRoutes(): array
|
||||
{
|
||||
return [
|
||||
self::Route( routeUrl: '/api/recettes/list', routeName: 'api->recettes->list', routeAction: 'list', routeMethods: ['POST'] ),
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
36
src/Domain/Recettes/RecettesController.php
Normal file
36
src/Domain/Recettes/RecettesController.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
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 {
|
||||
|
||||
public static function defineRoutes(): array
|
||||
{
|
||||
return [
|
||||
self::Route( routeUrl: '/recettes', routeName: 'recettes->index', routeAction: 'index', pageHeadTitle: 'Liste des recettes' ),
|
||||
self::Route( routeUrl: '/recettes/{string}', routeName: 'recettes->show', routeAction: 'show', pageHeadTitle: 'Recette' ),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
public function index(): View {
|
||||
|
||||
$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 ),
|
||||
] );
|
||||
}
|
||||
|
||||
}
|
||||
17
src/Domain/Recettes/RecettesManagementController.php
Normal file
17
src/Domain/Recettes/RecettesManagementController.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Recettes;
|
||||
|
||||
use App\Domain\Controller;
|
||||
|
||||
class RecettesManagementController extends Controller {
|
||||
|
||||
public static function defineRoutes(): array
|
||||
{
|
||||
return [
|
||||
self::Route( routeUrl: '/recettes/create', routeName: 'recettes->create', routeAction: 'create' ),
|
||||
self::Route( routeUrl: '/recettes/edit/{int}', routeName: 'recettes->edit', routeAction: 'edit' ),
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
170
src/Domain/Repository.php
Normal file
170
src/Domain/Repository.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain;
|
||||
|
||||
use App\Domain\Recettes\Recette;
|
||||
use App\Kernel;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Classe abstraite avec des méthodes pour pouvoir gérer les liens entre le site et la base de données.
|
||||
*/
|
||||
abstract class Repository {
|
||||
|
||||
/**
|
||||
* Instancie des éléments de cette classe par rapport au repository.
|
||||
* @return class-string
|
||||
*/
|
||||
abstract public static function getEntity(): string;
|
||||
|
||||
/**
|
||||
* Doit retourner une liste du type :
|
||||
* ['table' => '', 'columns' => [ ...Liste des colonnes dans la BDD ] ].
|
||||
* @return array
|
||||
*/
|
||||
abstract public static function getStructure(): array;
|
||||
|
||||
/**
|
||||
* Contient le nom de la table principale du repo.
|
||||
* @var string|mixed
|
||||
*/
|
||||
final public string $tableName;
|
||||
|
||||
/**
|
||||
* Contient le nom des colonnes de la table du repo.
|
||||
* @var array|mixed
|
||||
*/
|
||||
final public array $tableColumns;
|
||||
|
||||
/**
|
||||
* Contient les mêmes données que getStructure().
|
||||
* @var array
|
||||
*/
|
||||
public private(set) array $globalStructure;
|
||||
|
||||
/**
|
||||
* Constructeur.
|
||||
* Reprend les informations de getStructure et les met dans des attributs.
|
||||
*/
|
||||
public function __construct(){
|
||||
$structure = static::getStructure();
|
||||
|
||||
$this->tableName = $structure['table'];
|
||||
$this->tableColumns = $structure['columns'];
|
||||
$this->globalStructure = $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'avoir tous les éléments correspondant à la requête passée en paramètre.
|
||||
*
|
||||
* @param string $sqlQuery
|
||||
* @param bool $asArray Permet de savoir si on veut que le retour soit une array ou bien un tableau de Model.
|
||||
* @return array|null
|
||||
*/
|
||||
public function selectGetAll( string $sqlQuery, bool $asArray = false ): ?array {
|
||||
$statement = Kernel::$DB->pdo->prepare( $sqlQuery );
|
||||
if( !$statement->execute() )
|
||||
return null;
|
||||
|
||||
if( $asArray )
|
||||
$results = $statement->fetchAll( PDO::FETCH_ASSOC );
|
||||
else
|
||||
$results = $statement->fetchAll( PDO::FETCH_CLASS, static::getEntity() );
|
||||
if( empty( $results ) )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'ajouter une entité à la base de données.
|
||||
*
|
||||
* @param Model $entity
|
||||
* @return bool
|
||||
*/
|
||||
public function addEntity( Model $entity ): bool {
|
||||
|
||||
$query = "INSERT INTO {$this->tableName} (";
|
||||
$values = "(";
|
||||
foreach( $this->tableColumns as $column ) {
|
||||
$query .= "`{$column}`,";
|
||||
$values .= ":{$column},";
|
||||
}
|
||||
$query = substr( $query, 0, -1 ) . ")";
|
||||
$values = substr( $values, 0, -1 ) . ")";
|
||||
$query .= " VALUES " . $values . ";";
|
||||
|
||||
$statement = Kernel::$DB->pdo->prepare( $query );
|
||||
foreach( $this->tableColumns as $column ) {
|
||||
$statement->bindValue(":{$column}", $entity->{$column} );
|
||||
}
|
||||
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de mettre à jour les informations de l'entité dans la base de données.
|
||||
*
|
||||
* @param Model $entity
|
||||
* @param string $identifier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateEntity( Model $entity, string $identifier ): bool {
|
||||
$query = "UPDATE {$this->tableName} SET ";
|
||||
foreach( $this->tableColumns as $column ) {
|
||||
$query .= "`{$column}` = :{$column},";
|
||||
}
|
||||
$query = substr( $query, 0, -1 ) . " WHERE {$identifier} = :{$identifier};";
|
||||
|
||||
$statement = Kernel::$DB->pdo->prepare( $query );
|
||||
foreach( $this->tableColumns as $column ) {
|
||||
$statement->bindValue(":{$column}", $entity->{$column} );
|
||||
}
|
||||
$statement->bindValue( ":{$identifier}", $entity->{$identifier} );
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de supprimer une entrée d'entité dans la base de données.
|
||||
*
|
||||
* @param Model $entity
|
||||
* @param string $identifier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteEntity( Model $entity, string $identifier ): bool {
|
||||
$query = "DELETE FROM {$this->tableName} WHERE {$identifier} = :{$identifier};";
|
||||
$statement = Kernel::$DB->pdo->prepare( $query );
|
||||
$statement->bindValue( ":{$identifier}", $entity->{$identifier} );
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction raccourcie pour préparer les champs nécessaires pour addEntity.
|
||||
*
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function add( Model $entity ): bool;
|
||||
|
||||
/**
|
||||
* Fonction raccourcie pour préparer les champs nécessaires pour updateEntity
|
||||
*
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function update( Model $entity ): bool;
|
||||
|
||||
/**
|
||||
* Fonction raccourcie pour préparer les champs nécessaires pour deleteEntity
|
||||
*
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function delete( Model $entity ): bool;
|
||||
|
||||
|
||||
}
|
||||
33
src/Domain/Tags/Tag.php
Normal file
33
src/Domain/Tags/Tag.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Tags;
|
||||
|
||||
use App\Domain\Model;
|
||||
|
||||
/**
|
||||
* Classe qui va gérer les tags.
|
||||
*/
|
||||
class Tag extends Model {
|
||||
|
||||
/**
|
||||
* Le numéro du tag.
|
||||
* @var int
|
||||
*/
|
||||
public int $num_tag;
|
||||
|
||||
/**
|
||||
* Le nom du tag
|
||||
* @var string
|
||||
*/
|
||||
public string $nom_tag;
|
||||
|
||||
/**
|
||||
* Retourne le numéro du tag.
|
||||
* @return int
|
||||
*/
|
||||
public function getID(): int
|
||||
{
|
||||
return $this->num_tag;
|
||||
}
|
||||
|
||||
}
|
||||
174
src/Domain/Tags/TagRepository.php
Normal file
174
src/Domain/Tags/TagRepository.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Tags;
|
||||
|
||||
use App\Domain\LinkableInterface;
|
||||
use App\Domain\Recettes\Recette;
|
||||
use App\Domain\Repository;
|
||||
use App\Domain\Model;
|
||||
use App\Kernel;
|
||||
|
||||
/**
|
||||
* Classe qui va permettre de gérer les requêtes BDD en lien avec les tags.
|
||||
* Les tags sont liables à d'autres entités comme les Recettes.
|
||||
*/
|
||||
class TagRepository extends Repository implements LinkableInterface {
|
||||
|
||||
/**
|
||||
* Permet d'avoir le nom de l'entité dont dépend ce repo.
|
||||
* @return string
|
||||
*/
|
||||
public static function getEntity(): string
|
||||
{
|
||||
return Tag::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* La structure de notre table dans la BDD.
|
||||
* Un champ link_recettes a été ajouté pour donner le nom de la table de lien entre nos recettes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getStructure(): array
|
||||
{
|
||||
return [
|
||||
'table' => 'Tag',
|
||||
'columns' => [
|
||||
'num_tag', 'nom_tag'
|
||||
],
|
||||
'link_recettes' => 'Referencetag'
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir une liste de tous les tags objet Tag.
|
||||
*
|
||||
* @return Tag[]|null
|
||||
*/
|
||||
public function getAll(): ?array {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName};";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir un tag spécifique par son ID.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return Tag|null
|
||||
*/
|
||||
public function getByID( int $id ): ?Tag {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_tag = {$id}";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null || count( $results ) > 1 )
|
||||
return null;
|
||||
return $results[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir, sous forme de liste, toutes les entrées qui lient des tags.
|
||||
*
|
||||
* @param string $linkedTo La table qui permet de faire la liaison des tags avec une autre entité.
|
||||
* @param string $linkingField Le champ qui permet de faire la liaison des tags avec une autre entité.
|
||||
* @param Model $linkedEntity L'autre entité.
|
||||
*
|
||||
* @see LinkableInterface
|
||||
* @return array|null
|
||||
*/
|
||||
public function getIdLinkedTo( string $linkedTo, string $linkingField, Model $linkedEntity ): ?array {
|
||||
|
||||
$linkName = 'link_' . $linkedTo;
|
||||
if( !isset( $this->globalStructure[$linkName]))
|
||||
return null;
|
||||
|
||||
$sqlQuery = "SELECT * FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()};";
|
||||
$results = $this->selectGetAll($sqlQuery, true);
|
||||
if( $results === null )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'ajouter un lien entre un tag et une autre entité comme Recette.
|
||||
*
|
||||
* @param string $linkedTo
|
||||
* @param string $linkingField
|
||||
* @param Model $linkedEntity
|
||||
* @param Model $tagEntity
|
||||
*
|
||||
* @see LinkableInterface
|
||||
* @return bool
|
||||
*/
|
||||
public function addLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $tagEntity ): bool {
|
||||
|
||||
$linkName = 'link_' . $linkedTo;
|
||||
if( !isset( $this->globalStructure[$linkName]))
|
||||
return false;
|
||||
|
||||
$query = "INSERT INTO {$this->globalStructure[$linkName]} ({$linkingField},num_tag) VALUES ({$linkedEntity->getId()}, {$tagEntity->getID()});";
|
||||
$statement = Kernel::$DB->pdo->prepare( $query );
|
||||
|
||||
return $statement->execute();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire un lien dans la BDD entre un tag et une autre entité.
|
||||
*
|
||||
* @param string $linkedTo
|
||||
* @param string $linkingField
|
||||
* @param Model $linkedEntity
|
||||
* @param Model $tagEntity
|
||||
*
|
||||
* @see LinkableInterface
|
||||
* @return bool
|
||||
*/
|
||||
public function removeLinkBetween(string $linkedTo, string $linkingField, Model $linkedEntity, Model $tagEntity ): bool
|
||||
{
|
||||
$linkName = 'link_' . $linkedTo;
|
||||
if( !isset( $this->globalStructure[$linkName]))
|
||||
return false;
|
||||
|
||||
$query = "DELETE FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()} AND num_tag = {$tagEntity->getId()};";
|
||||
$statement = Kernel::$DB->pdo->prepare( $query );
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajouter un tag dans la BDD.
|
||||
*
|
||||
* @param Model $ingredient
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function add( Model $tag ): bool {
|
||||
return $this->addEntity( $tag );
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un tag dans la BDD.
|
||||
*
|
||||
* @param Model $tag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function update( Model $tag ): bool {
|
||||
return $this->updateEntity( $tag, 'num_tag' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un tag de la BDD.
|
||||
*
|
||||
* @param Model $tag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete( Model $tag ): bool {
|
||||
return $this->deleteEntity( $tag, 'num_tag' );
|
||||
}
|
||||
|
||||
}
|
||||
38
src/Domain/Tags/UseTagsInterface.php
Normal file
38
src/Domain/Tags/UseTagsInterface.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Tags;
|
||||
|
||||
use App\Domain\Model;
|
||||
|
||||
/**
|
||||
* Interface utilisée par tous les repository qui sont en lien avec les tags.
|
||||
*/
|
||||
interface UseTagsInterface {
|
||||
|
||||
/**
|
||||
* Permet de récupérer tous les tags liés à ce Modèle.
|
||||
* @return array|null
|
||||
*/
|
||||
public function getAllLinkedTags( Model $entity ): ?array;
|
||||
|
||||
/**
|
||||
* Permet d'ajouter un tag en lien avec notre autre entité.
|
||||
*
|
||||
* @param Tag $tag
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function addATag( Tag $tag, Model $entity ): bool;
|
||||
|
||||
/**
|
||||
* Permet de retirer un lien entre un tag et une autre entité.
|
||||
*
|
||||
* @param Tag $tag
|
||||
* @param Model $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeATag( Tag $tag, Model $entity ): bool;
|
||||
|
||||
}
|
||||
72
src/Domain/Utilisateurs/AuthentificationController.php
Normal file
72
src/Domain/Utilisateurs/AuthentificationController.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Utilisateurs;
|
||||
|
||||
use App\Domain\Controller;
|
||||
use App\Helpers\Authentification;
|
||||
use App\Http\JSONResponse;
|
||||
use App\Http\Request;
|
||||
use App\Infrastructure\View;
|
||||
|
||||
class AuthentificationController extends Controller {
|
||||
|
||||
public static function defineRoutes(): array {
|
||||
|
||||
return [
|
||||
|
||||
// Public routes.
|
||||
self::Route( routeUrl: '/login', routeName: 'login', routeAction: 'loginForm', pageHeadTitle: 'Connexion' ),
|
||||
self::Route( routeUrl: '/logout', routeName: 'logout', routeAction: 'logoutPage', pageHeadTitle: 'Déconnexion' ),
|
||||
|
||||
// API Routes.
|
||||
self::Route( routeUrl: '/api/auth', routeName: 'api->auth', routeAction: 'auth', routeMethods: ['POST'] ),
|
||||
// self::Route( routeUrl: '/api/auth/logout', routeName: 'api->auth->logout', routeAction: 'logout', routeMethods: ['POST'] ),
|
||||
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
$username = Request::get( 'username' );
|
||||
$password = Request::get( 'password' );
|
||||
|
||||
$success = new \App\Domain\Utilisateurs\UtilisateursRepository()->login($username,$password);
|
||||
if(!$success) return JSONResponse::sendError( [ 'user_id' => $userId ] );
|
||||
|
||||
$userId = $success;
|
||||
Authentification::loginUser( $userId );
|
||||
return JSONResponse::sendSuccess( [ 'user_id' => $userId ] );
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function logout(): JSONResponse {
|
||||
|
||||
if( !Authentification::isLoggedIn() ) {
|
||||
return JSONResponse::sendError( [ 'message' => 'Already disconnected' ] );
|
||||
}
|
||||
|
||||
Authentification::destroySession();
|
||||
return JSONResponse::sendSuccess( [ 'message' => 'Logged out' ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
23
src/Domain/Utilisateurs/Utilisateurs.php
Normal file
23
src/Domain/Utilisateurs/Utilisateurs.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Utilisateurs;
|
||||
|
||||
use App\Domain\Model;
|
||||
//use App\Helpers\Markdown;
|
||||
|
||||
/**
|
||||
* Classe qui va permettre de gérer tous les objets recette.
|
||||
*/
|
||||
class Utilisateurs extends Model {
|
||||
|
||||
public int $num_user;
|
||||
public string $username;
|
||||
public string $userpassword;
|
||||
|
||||
|
||||
public function getID(): int
|
||||
{
|
||||
return $this->num_user;
|
||||
}
|
||||
|
||||
}
|
||||
89
src/Domain/Utilisateurs/UtilisateursRepository.php
Normal file
89
src/Domain/Utilisateurs/UtilisateursRepository.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Utilisateurs;
|
||||
|
||||
use App\Domain\Model;
|
||||
use App\Domain\Repository;
|
||||
|
||||
|
||||
/**
|
||||
* Classe qui permet de faire le lien entre la BDD et le site pour les recettes.
|
||||
* Les recettes sont en lien avec les ingrédients.
|
||||
*/
|
||||
class UtilisateursRepository extends Repository {
|
||||
|
||||
public static function getEntity(): string
|
||||
{
|
||||
return Utilisateurs::class;
|
||||
}
|
||||
|
||||
public static function getStructure(): array
|
||||
{
|
||||
return [
|
||||
'table' => 'User',
|
||||
'columns' => [
|
||||
'num_user', 'username', 'userpassword'
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir une liste de toutes les recettes objet Recette.
|
||||
*
|
||||
* @return Recette[]|null
|
||||
*/
|
||||
public function getAll(): ?array {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName};";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null )
|
||||
return null;
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Permet d'avoir une recette par un ID.
|
||||
*
|
||||
* @param int $id
|
||||
* @return Recette|null
|
||||
*/
|
||||
public function getByID( int $id ): ?Utilisateurs {
|
||||
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_user = {$id}";
|
||||
$results = $this->selectGetAll($sqlQuery);
|
||||
if( $results === null || count( $results ) > 1 )
|
||||
return null;
|
||||
return $results[0];
|
||||
}
|
||||
|
||||
|
||||
public function login(string $name, string $pass): int|bool{
|
||||
|
||||
$users = new UtilisateursRepository()->getAll();
|
||||
echo "\n\n";
|
||||
var_dump($users);
|
||||
foreach($users as $user){
|
||||
if((strcmp($name,$user->username)==0) and (strcmp($pass,$user->userpassword)==0))return $user->num_user;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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' );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
28
src/Exceptions/ConfigFailedLoadingException.php
Normal file
28
src/Exceptions/ConfigFailedLoadingException.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception qui se déclenche lorsqu'un fichier de configuration n'a pas pu être chargé.
|
||||
*
|
||||
* - Peut se déclencher si un fichier de configuration non voulu.
|
||||
*/
|
||||
class ConfigFailedLoadingException extends \Exception {
|
||||
|
||||
/**
|
||||
* Chemin vers le fichier de configuration manquant.
|
||||
* @var string
|
||||
*/
|
||||
public private(set) string $configFilePath;
|
||||
|
||||
/**
|
||||
* Constructeur.
|
||||
* @param string $configFilePath Le fichier de configuration manquant.
|
||||
*/
|
||||
public function __construct( string $configFilePath ) {
|
||||
$this->configFilePath = $configFilePath;
|
||||
$this->message = "Failed to load configuration file '{$configFilePath}'.";
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
}
|
||||
15
src/Exceptions/InvalidRouteException.php
Normal file
15
src/Exceptions/InvalidRouteException.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
class InvalidRouteException extends \Exception {
|
||||
|
||||
private string $clientRoute;
|
||||
|
||||
public function __construct(string $clientRoute)
|
||||
{
|
||||
$this->clientRoute = $clientRoute;
|
||||
$message = "{$clientRoute} doesn't exist";
|
||||
parent::__construct($message, 404);
|
||||
}
|
||||
|
||||
}
|
||||
13
src/Exceptions/InvalidViewException.php
Normal file
13
src/Exceptions/InvalidViewException.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
use Throwable;
|
||||
|
||||
class InvalidViewException extends \Exception {
|
||||
|
||||
public function __construct( string $viewName )
|
||||
{
|
||||
parent::__construct( "View {$viewName} does not exist.", 500 );
|
||||
}
|
||||
|
||||
}
|
||||
49
src/Helpers/Authentification.php
Normal file
49
src/Helpers/Authentification.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class Authentification {
|
||||
|
||||
/**
|
||||
* Permet de démarrer la variable Session.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function startSession(): void {
|
||||
session_start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de supprimer la session.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function destroySession(): void {
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de connecter un utilisateur.
|
||||
*
|
||||
* @param int $userId
|
||||
* @return void
|
||||
*/
|
||||
public static function loginUser( int $userId ){
|
||||
$_SESSION['user'] = $userId;
|
||||
}
|
||||
|
||||
// TODO : Complete when user.
|
||||
public static function getCurrentUser() {
|
||||
return $_SESSION['user'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de savoir si un utilisateur est connecté ou pas.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isLoggedIn(): bool {
|
||||
return self::getCurrentUser() !== false;
|
||||
}
|
||||
|
||||
}
|
||||
38
src/Helpers/AutoLoader.php
Normal file
38
src/Helpers/AutoLoader.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace App\Helpers;
|
||||
|
||||
/**
|
||||
* Classe utilitaire contenant des méthodes pour charger automatiquement les classes.
|
||||
*/
|
||||
class AutoLoader {
|
||||
|
||||
/**
|
||||
* Namespace principal de l'application.
|
||||
*/
|
||||
public const string PRIMARY_NAMESPACE = 'App\\';
|
||||
|
||||
/**
|
||||
* Permet d'enregistrer la méthode _autoload_ en tant que méthode pour charger des classes.
|
||||
* @return void
|
||||
*/
|
||||
public static function register(): void {
|
||||
spl_autoload_register( [ self::class, '_autoload_' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de charger une classe.
|
||||
*
|
||||
* @param string $className
|
||||
* @return void
|
||||
*/
|
||||
public static function _autoload_( string $className ): void {
|
||||
if( !str_starts_with( $className, self::PRIMARY_NAMESPACE ) ) // Ne s'occupe que des classes à charger de cette application.
|
||||
return;
|
||||
|
||||
$className = str_replace( self::PRIMARY_NAMESPACE, '', $className );
|
||||
$className = str_replace( '\\', '/', $className );
|
||||
|
||||
require APP_ROOT . 'src/' . $className . '.php';
|
||||
}
|
||||
|
||||
}
|
||||
42
src/Helpers/ConfigFactory.php
Normal file
42
src/Helpers/ConfigFactory.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Exceptions\ConfigFailedLoadingException;
|
||||
|
||||
/**
|
||||
* Classe outillage qui permet de charger les fichiers de configuration.
|
||||
*/
|
||||
class ConfigFactory {
|
||||
|
||||
/**
|
||||
* Chemin relatif à partir de APP_ROOT du dossier contenant les fichiers de configuration.
|
||||
*
|
||||
* @see APP_ROOT
|
||||
*/
|
||||
const string CONFIG_RELATIVE_FOLDER = "config/";
|
||||
|
||||
/**
|
||||
* Permet de charger un fichier de configuration.
|
||||
*
|
||||
* @param string $fileName Le nom du fichier de configuration, avec ou sans l'extension .php.
|
||||
* @return array
|
||||
*
|
||||
* @throws ConfigFailedLoadingException
|
||||
*/
|
||||
public static function loadConfigFile( string $fileName ): array {
|
||||
if( !str_ends_with( $fileName, ".php" ) ) {
|
||||
$fileName .= ".php";
|
||||
}
|
||||
|
||||
$filePath = APP_ROOT . self::CONFIG_RELATIVE_FOLDER . $fileName;
|
||||
|
||||
if( !file_exists( $filePath ) ){
|
||||
throw new ConfigFailedLoadingException( "Config file '$fileName' does not exist" );
|
||||
}
|
||||
|
||||
$configArray = require $filePath;
|
||||
return $configArray;
|
||||
}
|
||||
|
||||
}
|
||||
29
src/Helpers/Markdown.php
Normal file
29
src/Helpers/Markdown.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class Markdown {
|
||||
|
||||
public static function getMarkdownEntities(): array {
|
||||
return [
|
||||
|
||||
// Gras & Italique
|
||||
'/\*\*(.*?)\*\*/' => '<b>$1</b>',
|
||||
'/\*(.*?)\*/' => '<i>$1</i>',
|
||||
|
||||
// Titres
|
||||
'/^## (.*?)$/m' => '<h3>$1</h3>',
|
||||
'/^# (.*?)$/m' => '<h2>$1</h2>',
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
public static function convertToHTML( string $markdown ): string {
|
||||
$safeMD = htmlspecialchars( $markdown, ENT_QUOTES );
|
||||
foreach( Markdown::getMarkdownEntities() as $key => $value ) {
|
||||
$safeMD = preg_replace( $key, $value, $safeMD );
|
||||
}
|
||||
|
||||
return nl2br( $safeMD );
|
||||
}
|
||||
}
|
||||
27
src/Helpers/SanitizeTrait.php
Normal file
27
src/Helpers/SanitizeTrait.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
/**
|
||||
* Trait qui permet de désinfecter une variable.
|
||||
*/
|
||||
trait SanitizeTrait {
|
||||
|
||||
/**
|
||||
* Permet de désinfecter une variable
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
public static function sanitize( mixed $data ): mixed {
|
||||
|
||||
if( is_string( $data ) ) {
|
||||
return htmlspecialchars( $data, ENT_QUOTES );
|
||||
} else if( is_integer( $data ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
}
|
||||
51
src/Http/JSONResponse.php
Normal file
51
src/Http/JSONResponse.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
/**
|
||||
* Permet de renvoyer une réponse de route au format JSON.
|
||||
*/
|
||||
class JSONResponse {
|
||||
|
||||
/**
|
||||
* Les données ajoutés au fichier JSON.
|
||||
* @var array|mixed
|
||||
*/
|
||||
public private(set) array $data;
|
||||
|
||||
/**
|
||||
* Le code HTML de la réponse.
|
||||
* @var int|mixed
|
||||
*/
|
||||
public private(set) int $htmlCode;
|
||||
|
||||
public function __construct( $data = [], $code = 200 ){
|
||||
|
||||
$this->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 );
|
||||
}
|
||||
|
||||
}
|
||||
69
src/Http/Request.php
Normal file
69
src/Http/Request.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
use App\Helpers\SanitizeTrait;
|
||||
use App\Kernel;
|
||||
|
||||
/**
|
||||
* Classe utilitaire ayant plusieurs méthodes pour gérer la requête actuelle.
|
||||
*/
|
||||
class Request {
|
||||
|
||||
use SanitizeTrait;
|
||||
|
||||
/**
|
||||
* Bloquer les CORS venant d'autres sites.
|
||||
* @return void
|
||||
*/
|
||||
public static function setCORS(): void {
|
||||
$siteUrl = Kernel::$configs['general']['website_url'];
|
||||
header("Access-Control-Allow-Origin: {$siteUrl}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir une variable GET et nettoyé.
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get( string $name ): mixed {
|
||||
|
||||
if( !isset( $_GET[$name] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::sanitize( $_GET[$name] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'obtenir une variable POST et nettoyé.
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public static function post( string $name ): mixed {
|
||||
|
||||
if( !isset( $_POST[$name] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
42
src/Http/Route.php
Normal file
42
src/Http/Route.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
/**
|
||||
* Permet de définir une route précise.
|
||||
*/
|
||||
final class Route {
|
||||
|
||||
|
||||
public private(set) string $routeUrl;
|
||||
public private(set) string $routeName;
|
||||
public private(set) string $routeController;
|
||||
public private(set) string $routeAction;
|
||||
public private(set) array $routeMethods;
|
||||
public private(set) string $pageHeadTitle;
|
||||
|
||||
/**
|
||||
* @param string $routeUrl Le chemin vers la route.
|
||||
* @param string $routeName Le nom de la route.
|
||||
* @param string $routeController Le controller qui va interpréter cette route.
|
||||
* @param string $routeAction L'action du controller qui va interpréter cette route.
|
||||
* @param array $routeMethods Une liste de methodes GET, POST que la page accepte.
|
||||
* @param string $pageHeadTitle Le nom de la page qui sera affiché dans <title></title>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
202
src/Http/Router.php
Normal file
202
src/Http/Router.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use App\Domain\Controller;
|
||||
use App\Exceptions\InvalidRouteException;
|
||||
use App\Helpers\AutoLoader;
|
||||
use App\Kernel;
|
||||
use FilesystemIterator;
|
||||
|
||||
final class Router {
|
||||
|
||||
/**
|
||||
* Route voulue par le client.
|
||||
* @var string
|
||||
*/
|
||||
public private(set) static string $clientRouteString;
|
||||
// private(set)
|
||||
/**
|
||||
* Informations de la route du client.
|
||||
* @var Route
|
||||
*/
|
||||
public static Route $clientRoute;
|
||||
// private(set)
|
||||
/**
|
||||
* Informations sur les arguments passés à la route.
|
||||
* @var array
|
||||
*/
|
||||
public static array $clientRouteArguments;
|
||||
// private(set)
|
||||
/**
|
||||
* Liste des controllers sorti du cache ou récupéré via fetchControllers.
|
||||
* @var array
|
||||
*/
|
||||
private static array $controllers;
|
||||
|
||||
/**
|
||||
* Liste des routes tirées des controllers. Rempli par la méthode fetchRoutes.
|
||||
* @var Route[]
|
||||
*/
|
||||
private static array $routes;
|
||||
|
||||
/**
|
||||
* Fonction principale qui va router le contenu vers la bonne méthode du bon Controller.
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidRouteException Si la route voulue n'existe pas ou n'est pas bien formaté.
|
||||
*/
|
||||
public static function routeTo(): void {
|
||||
|
||||
self::$clientRouteString = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
|
||||
self::$controllers = self::fetchControllers();
|
||||
self::$routes = self::fetchRoutes();
|
||||
|
||||
$clientRoute = self::clientRouteExist();
|
||||
if( !$clientRoute ){
|
||||
throw new InvalidRouteException( self::$clientRouteString );
|
||||
}
|
||||
self::$clientRoute = $clientRoute;
|
||||
|
||||
if( !in_array( $_SERVER['REQUEST_METHOD'], self::$clientRoute->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);
|
||||
//correction controler()->method to controler().method
|
||||
// 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( 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;
|
||||
}
|
||||
|
||||
}
|
||||
45
src/Infrastructure/Database.php
Normal file
45
src/Infrastructure/Database.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Infrastructure;
|
||||
|
||||
/**
|
||||
* Permet de se connecter à la base de données et de faire le pont
|
||||
* entre la classe PDO et nos actions.
|
||||
*/
|
||||
final class Database {
|
||||
|
||||
/**
|
||||
* Contient les informations de connexion à notre BDD.
|
||||
* @var array
|
||||
*/
|
||||
private array $databaseInfos;
|
||||
|
||||
/**
|
||||
* Instance PDO.
|
||||
* @var \PDO
|
||||
*/
|
||||
public private(set) \PDO $pdo;
|
||||
|
||||
/**
|
||||
* Constructeur, lance la connexion.
|
||||
* @param array $loginInfos ['name','host','port','user','pass']
|
||||
*/
|
||||
public function __construct( array $loginInfos ){
|
||||
$this->databaseInfos = $loginInfos;
|
||||
$this->tryLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet d'essayer un login à la base de données.
|
||||
* @return void - never si la connexion échoue.
|
||||
*/
|
||||
private function tryLogin(): void {
|
||||
try {
|
||||
$dsn = 'mysql:dbname=' . $this->databaseInfos['name'] . ';host=' . $this->databaseInfos['host'] . ';port=' . $this->databaseInfos['port'];
|
||||
$this->pdo = new \PDO( $dsn, $this->databaseInfos['user'], $this->databaseInfos['pass'] );
|
||||
} catch ( \PDOException $e ) {
|
||||
die( $e->getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
220
src/Infrastructure/View.php
Normal file
220
src/Infrastructure/View.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace App\Infrastructure;
|
||||
|
||||
use App\Exceptions\InvalidViewException;
|
||||
use App\Http\Router;
|
||||
use App\Kernel;
|
||||
|
||||
/**
|
||||
* Permet de rendre une vue / contenu de la page.
|
||||
* /!\ : Une seule vue peut-être appellé par page, hormis celle en plus pour le squelette.
|
||||
*/
|
||||
final class View {
|
||||
|
||||
/**
|
||||
* Répertoire où se trouvent les vues.
|
||||
*/
|
||||
const string VIEW_PATH = APP_ROOT . 'views/';
|
||||
|
||||
/**
|
||||
* Le nom du fichier de vue.
|
||||
* @var string
|
||||
*/
|
||||
public private(set) string $viewName;
|
||||
|
||||
/**
|
||||
* Les arguments passés à la vue.
|
||||
* @var array
|
||||
*/
|
||||
public private(set) array $viewArgs;
|
||||
|
||||
/**
|
||||
* La base de la vue. (Pour ne pas répéter header/footer à chaque fois).
|
||||
* null correspond à pas de squelette.
|
||||
*
|
||||
* @var string|mixed|null
|
||||
*/
|
||||
public private(set) ?string $skeleton;
|
||||
|
||||
/**
|
||||
* Utilisé par les squelettes, permet d'injecter des contenus comme le contenu de son enfant.
|
||||
* @var array<string,string>
|
||||
*/
|
||||
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<string,string> $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 <title></title>
|
||||
* @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 );
|
||||
}
|
||||
|
||||
}
|
||||
119
src/Kernel.php
Normal file
119
src/Kernel.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
use App\Exceptions\ConfigFailedLoadingException;
|
||||
use App\Exceptions\InvalidRouteException;
|
||||
use App\Helpers\Authentification;
|
||||
use App\Helpers\AutoLoader;
|
||||
use App\Helpers\ConfigFactory;
|
||||
use App\Http\Router;
|
||||
use App\Infrastructure\Database;
|
||||
|
||||
/**
|
||||
* Classe primaire du site.
|
||||
* Permet de lancer l'application.
|
||||
*/
|
||||
final class Kernel {
|
||||
|
||||
/**
|
||||
* Liste des fichiers de configuration sous le format suivant :
|
||||
* [ 'nom_de_la_config' -> [ 'cle" → valeur ] ]
|
||||
*
|
||||
* @var array<string,array>
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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();
|
||||
$this->loadDatabase();
|
||||
|
||||
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['database'] = ConfigFactory::loadConfigFile('database');
|
||||
self::$configs['route_arguments'] = ConfigFactory::loadConfigFile('route_arguments');
|
||||
self::$configs['views'] = ConfigFactory::loadConfigFile('views');
|
||||
|
||||
} catch( ConfigFailedLoadingException $e ){
|
||||
die( $e->getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de se connecter à la base de données principale.
|
||||
* @return void
|
||||
*/
|
||||
private function loadDatabase(): void {
|
||||
self::$DB = new Database( self::$configs['database'] );
|
||||
}
|
||||
|
||||
}
|
||||
9
views/base.php
Normal file
9
views/base.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php use App\Infrastructure\View as V; ?>
|
||||
|
||||
<?php V::partial( 'header' ); ?>
|
||||
|
||||
<div class="main-body">
|
||||
<?php V::inject( 'content'); ?>
|
||||
</div>
|
||||
|
||||
<?php V::partial( 'footer' ); ?>
|
||||
36
views/home.php
Normal file
36
views/home.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<h1>Coucou</h1>
|
||||
<?php $str = "Markdown **text**\n# SOLONG\n*text*."; $md = \App\Helpers\Markdown::convertToHTML( $str ); echo "<p>$md</p>"; ?>
|
||||
<?php use App\Infrastructure\View as V; use App\Http\Router; ?>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
|
||||
$recette = new \App\Domain\Recettes\RecetteRepository()->getByID( 2 );
|
||||
$modelingredient = new \App\Domain\Ingredients\Ingredient();
|
||||
$modelingredient->num_ingredient=0;
|
||||
$modelingredient->nom_ingredient='nomdingredientaupif';
|
||||
var_dump($modelingredient);
|
||||
//$ingredient = new \App\Domain\Ingredients\IngredientRepository()->add($modelingredient);
|
||||
//pas de add si numing existe déjà use autoincrement
|
||||
$ingredient = new \App\Domain\Ingredients\IngredientRepository()->getByID( 1 );
|
||||
var_dump( $recette );
|
||||
var_dump( $ingredient );
|
||||
// new \App\Domain\Recettes\RecetteRepository()->removeAnIngredient( $ingredient, $recette );
|
||||
var_dump( $recette->getAllLinkedIngredients() );
|
||||
echo "\n\n";
|
||||
$modelUtilisateurs = new \App\Domain\Utilisateurs\Utilisateurs();
|
||||
$modelUtilisateurs->num_user=0;
|
||||
$modelUtilisateurs->username="usnmaemaee";
|
||||
$modelUtilisateurs->userpassword="pawssorddd";
|
||||
//$user = new \App\Domain\Utilisateurs\UtilisateursRepository()->add($modelUtilisateurs);
|
||||
$user = new \App\Domain\Utilisateurs\UtilisateursRepository()->getByID( 2 );
|
||||
var_dump($user);
|
||||
|
||||
$success = new \App\Domain\Utilisateurs\UtilisateursRepository()->login("usnmaemaee","pawssorddd");
|
||||
echo "\n\n";
|
||||
var_dump($success);
|
||||
|
||||
?>
|
||||
23
views/login.php
Normal file
23
views/login.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php use App\Infrastructure\View as V; use App\Http\Router; ?>
|
||||
|
||||
<div class=formcont>
|
||||
<form id='login-form' action='<?php echo Router::getRouteURL( 'api->auth' ); ?>' method='post'>
|
||||
|
||||
<div class='form-group'>
|
||||
<label for='logmsg'>Se Connecter</label>
|
||||
</div>
|
||||
<div id="login-errors" class="form-error" style="display:none;color:red;">
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<input type='text' class='form-control' name='username' id='username' value='' placeholder="Pseudo">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<input type='text' class='form-control' name='password' id='password' value='' placeholder="Mot de Passe">
|
||||
</div>
|
||||
|
||||
<button type='submit' class='btn btn-primary'>Confirmer</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<script src="<?php echo Router::getAssetURL( 'js/login.js' ); ?>" defer></script>
|
||||
14
views/partials/footer.php
Normal file
14
views/partials/footer.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php use App\Infrastructure\View as V; ?>
|
||||
|
||||
<footer id="footer">
|
||||
<div class="src">
|
||||
<a href="google.com">Une source</a>
|
||||
</div>
|
||||
<div class="Contact">
|
||||
Front end : Bousquet Sébastien
|
||||
--- Back end : Thorel Benjamin
|
||||
--- Base de donné : Glaudis Jordan
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
30
views/partials/header.php
Normal file
30
views/partials/header.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php use App\Infrastructure\View as V; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><?php echo V::getHeadTitle(); ?></title>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="stylesheet" href="<?php V::assetUrl( 'css/style.css' ); ?>" />
|
||||
</head>
|
||||
<body>
|
||||
<header id="header">
|
||||
<div id="logo">
|
||||
<a href="index.php">
|
||||
<img src="<?php V::assetUrl( 'images/Logo.jpg'); ?>" class="logo">
|
||||
</a>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="nav-list">
|
||||
<li><a id="google" class="nav-element" href="<?php V::routeUrl( 'home'); ?>">Acceuil</a></li>
|
||||
<li><a id="unTest" class="nav-element" href="<?php V::routeUrl( 'recettes->index'); ?>">Liste des recettes</a></li>
|
||||
<?php if( \App\Helpers\Authentification::isLoggedIn() ): ?>
|
||||
<li><a id="login" class= "nav-element" href="<?php V::routeUrl( 'logout'); ?>">Logout</a></li>
|
||||
<li><a id="addBouton" class="nav-element" href="<?php V::routeUrl( 'recettes->create'); ?>">Ajouter une Recette</a></li>
|
||||
<?php else: ?>
|
||||
<li><a id="login" class= "nav-element" href="<?php V::routeUrl( 'login'); ?>">Login</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<input type="text" class="search-form search-form-recette" placeholder="Rechercher une recette ...">
|
||||
</nav>
|
||||
</header>
|
||||
39
views/partials/tag-sidebar.php
Normal file
39
views/partials/tag-sidebar.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php use App\Infrastructure\View as V; ?>
|
||||
|
||||
<div class="sidebar">
|
||||
<div class="tag-cont">
|
||||
<h4>Tag</h4>
|
||||
<div class="tag-selected-div">
|
||||
<ul>
|
||||
</ul>
|
||||
</div>
|
||||
<form class="sidebar-search" action="none">
|
||||
<input type="text" class="search-form search-form-tag" name="search-tag" placeholder="Rechercher..." >
|
||||
</form>
|
||||
<div class="tag-unselected-div">
|
||||
<ul>
|
||||
<?php foreach( V::arg( 'tagsList') as $tag ): ?>
|
||||
<li class="tag tag-unselected" onclick="test()"><?php echo $tag->nom_tag; ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr id="hr">
|
||||
<div class="ingr-cont">
|
||||
<h4>Ingrédient</h4>
|
||||
<div class="tag-selected-div">
|
||||
<ul>
|
||||
</ul>
|
||||
</div>
|
||||
<form class="sidebar-search" action="none">
|
||||
<input type="text" class="search-form search-form-tag" name="search-ingr" placeholder="Rechercher..." >
|
||||
</form>
|
||||
<div class="tag-unselected-div">
|
||||
<ul>
|
||||
<?php foreach( V::arg( 'ingredientsList') as $tag ): ?>
|
||||
<li class="tag tag-unselected" onclick="test()"><?php echo $tag->nom_ingredient; ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
19
views/recettes/index.php
Normal file
19
views/recettes/index.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php use App\Infrastructure\View as V; ?>
|
||||
|
||||
<?php V::partial( 'tag-sidebar' ); ?>
|
||||
<div class="content">
|
||||
<div class="recettes">
|
||||
<?php if( V::arg( 'recettesList' ) != null ) foreach( V::arg( 'recettesList' ) as $recette ): ?>
|
||||
<a class="recette-icone" href="<?php V::routeUrl( 'recettes->show', $recette->slug ); ?>">
|
||||
<img class="recette-preview-image" src="random-recette.jpg">
|
||||
<div class="recette-icone-content">
|
||||
<h3><?php echo $recette->titre_recette; ?></h3>
|
||||
<ul>
|
||||
<li>Temps de préparation : <?php echo $recette->temps_de_preparation; ?></li>
|
||||
<li>Nombre d'ingrédients : <?php echo $recette->getNumberOfIngredients(); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user