33 Commits

Author SHA1 Message Date
159ef1eec7 prend ce design la 2026-04-09 17:32:11 +02:00
0ceb856272 jijza 2026-04-03 15:53:32 +02:00
23b73512e6 Source et léger paufinement 2026-04-03 14:58:27 +02:00
06c606105c Liste Ingre css base 2026-04-03 14:46:12 +02:00
5e8f23877d JS 2026-04-03 14:29:26 +02:00
8712caac33 bi 2026-04-03 13:43:57 +02:00
22d80b8a07 commit ListeIngr 2026-04-03 12:00:50 +02:00
553c0e9689 jhuyg 2026-04-03 11:37:06 +02:00
4d34d0c8a0 danj 2026-04-03 11:17:24 +02:00
6349bd4d90 Basic JS 2026-04-03 10:47:36 +02:00
a9f310a37f ajout et base du formulaire 2026-04-03 10:23:45 +02:00
0704caa03f un mesage 2026-04-02 17:21:25 +02:00
bf441546ed un msg 2026-03-27 16:00:23 +01:00
f81725cbe0 Présentation mostly done 2026-03-27 14:47:40 +01:00
aa9337b558 commit ofr now 2026-03-27 14:33:23 +01:00
f18d95766a on demande Je push 2026-03-27 13:52:40 +01:00
6f84d81e1c etc 2026-03-27 13:25:33 +01:00
fce15dda39 un message 2026-03-27 12:02:17 +01:00
ea1149ba48 j 2026-03-27 11:10:05 +01:00
97286b6f6a commit matin 2026-03-27 10:12:59 +01:00
0c958b0c59 dj 2026-03-20 16:00:45 +01:00
867134a3e5 commit 2026-03-20 15:11:41 +01:00
b45ef1281a des changements 2026-03-20 14:37:33 +01:00
1fe166e95b yes 2026-03-20 14:13:09 +01:00
898b297eee bouh 2026-03-20 13:51:30 +01:00
286c7b8c30 bouh 2026-03-20 12:01:14 +01:00
e1470927ea modification 2026-03-20 11:45:25 +01:00
c70b4b0dde plus d'amélioration 2026-03-20 11:08:33 +01:00
4e45014b47 A few modification 2026-03-20 09:34:02 +01:00
760ab677ee commit 2026-03-13 12:00:18 +01:00
c9300a2ff3 Commit for now 2026-03-13 11:24:44 +01:00
f998085291 test 2026-03-13 11:03:09 +01:00
155a4dde42 basicNecessity 2026-03-13 10:54:13 +01:00
77 changed files with 717 additions and 3747 deletions

0
public/assets/images/Logo.jpg → Logo.jpg Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

@@ -1,57 +1 @@
# 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()``.
# Les Recettes des Papis

57
acceuil.php Normal file
View File

@@ -0,0 +1,57 @@
<!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 Normal file
View File

@@ -0,0 +1,94 @@
<!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>

View File

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

View File

@@ -1,8 +0,0 @@
<?php
return [
'website_url' => 'http://127.0.0.1:8080/',
'website_name' => 'Les recettes des Papis',
'website_path' => APP_ROOT,
];

View File

@@ -1,6 +0,0 @@
<?php
return [
'{int}' => '([0-9]+)',
'{chars}' => '([A-Za-z]+)',
'{string}' => '([0-9A-Za-z]+)',
];

View File

@@ -1,4 +0,0 @@
<?php
return [
'base_view_skeleton' => 'base'
];

10
footer.php Normal file
View File

@@ -0,0 +1,10 @@
<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 Normal file
View File

@@ -0,0 +1,18 @@
<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 Normal file
View File

@@ -0,0 +1,25 @@
<!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>

View File

@@ -6,15 +6,15 @@ document.addEventListener("DOMContentLoaded", function(){
console.log(modifier_button);
for (const element of modifier_button) {
element.addEventListener('click', function ( e ) {
element.addEventListener('click', function () {
let modif = element.parentElement.previousSibling.previousSibling;
let data = e.target.getAttribute("data-id");
let data = element.getAttribute("data-id");
let form = document.createElement("form");
form.setAttribute("method", "POST");
form.setAttribute("action", "/ingredients/edit/" + data);
form.setAttribute("action", "/ingredient/edit/" + data);
form.setAttribute("class","liste-ingr-elem-text")
let input = document.createElement("input");
@@ -45,18 +45,4 @@ document.addEventListener("DOMContentLoaded", function(){
});
};
let suppr = document.getElementsByClassName("ingr-suppr");
for (const element of suppr) {
element.addEventListener('click', function ( e ) {
let confirm = window.confirm("Voulez vous supprimez ?");
let data = e.target.getAttribute("data-id");
if (confirm) {
window.location.href = "/ingredients/delete/" + data;
}
})
}
});

67
listeIngr.php Normal file
View File

@@ -0,0 +1,67 @@
<!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>

View File

@@ -6,15 +6,15 @@ document.addEventListener("DOMContentLoaded", function(){
console.log(modifier_button);
for (const element of modifier_button) {
element.addEventListener('click', function ( e ) {
element.addEventListener('click', function () {
let modif = element.parentElement.previousSibling.previousSibling;
let data = e.target.getAttribute("data-id");
let data = element.getAttribute("data-id");
let form = document.createElement("form");
form.setAttribute("method", "POST");
form.setAttribute("action", "/tags/edit/" + data);
form.setAttribute("action", "/tag/edit/" + data);
form.setAttribute("class","liste-tag-elem-text")
let input = document.createElement("input");
@@ -48,13 +48,13 @@ document.addEventListener("DOMContentLoaded", function(){
let suppr = document.getElementsByClassName("tag-suppr");
for (const element of suppr) {
element.addEventListener('click', function ( e ) {
element.addEventListener('click', function () {
let confirm = window.confirm("Voulez vous supprimez ?");
let data = e.target.getAttribute("data-id");
let data = element.getAttribute("data-id");
if (confirm) {
window.location.href = "/tags/delete/" + data;
window.location.href = "/tag/delete/" + data;
}
})
}

67
listeTag.php Normal file
View File

@@ -0,0 +1,67 @@
<!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 Normal file
View File

@@ -0,0 +1,36 @@
<!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>

68
public/assets/css/style.css → main.css Executable file → Normal file
View File

@@ -57,7 +57,7 @@ nav {
padding: 10px;
}
.nav-list {
.nav-list{
list-style-type: none;
margin: 0;
padding: 0;
@@ -67,6 +67,15 @@ 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;
@@ -85,12 +94,13 @@ nav {
#login {
border: 1px solid black;
border-radius: 6px;
border-radius: 2000px;
background-color: #ffc478;
}
#login:hover {
background-color: #0bbd90;
box-shadow: 1px 1px 7px black;
}
/*Body et son contenu */
@@ -121,6 +131,33 @@ 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;
}
@@ -143,6 +180,8 @@ 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);
@@ -191,6 +230,7 @@ nav {
background-clip: border-box;
padding-right: 2%;
padding-left: 2%;
padding-bottom: 5px;
overflow: hidden;
}
@@ -447,11 +487,23 @@ 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;
}
@@ -466,8 +518,9 @@ ul {
margin: 10px;
padding: 10px;
border: 1px solid black;
background: white;
background: #fff9f1;
border-radius: 20px;
box-shadow: 2px 2px 3px #a06100;
}
.recette-form-group {
@@ -477,7 +530,7 @@ ul {
}
#recette-form-div-desc {
height: 300px;
height: 220px;
}
#recette-form-description {
@@ -523,6 +576,8 @@ ul {
border: 1px solid black;
border-radius: 10px;
box-shadow: 1px 1px 1px black;
margin-top: .25%;
margin-bottom: .25%;
}
.liste-ingr-elem:hover {
@@ -553,6 +608,8 @@ ul {
border: 1px solid black;
border-radius: 10px;
box-shadow: 1px 1px 1px black;
margin-top: .25%;
margin-bottom: .25%;
}
.liste-tag-elem:hover {
@@ -592,3 +649,6 @@ footer{
color: white;
border-top: 1px solid black;
}

96
main.js Normal file
View File

@@ -0,0 +1,96 @@
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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -1,110 +0,0 @@
document.addEventListener('DOMContentLoaded', function(){
let searchInput = "";
let tagsId = [];
let ingredientsId = [];
function buildCards( data ){
if( Array.isArray( data ) ){
data = Object.assign({}, data );
}
document.getElementById( "recetteList" ).innerHTML = '';
for( const [key, recette] of Object.entries( data ) ) {
let HTML_CONTENT = `<a class="recette-icone" href="${recette.url}">
<img class="recette-preview-image" src="${recette.photo}">
<div class="recette-icone-content">
<h3>${recette.titre_recette}</h3>
<ul>
<li>Temps de préparation : ${recette.temps_de_preparation}</li>
<li>Nombre d'ingrédients : ${recette.nb_ingredients}</li>
</ul>
</div>
</a>`
document.getElementById("recetteList").innerHTML += HTML_CONTENT;
}
}
function advancedSearch(){
let form = new FormData();
form.append( "title", searchInput );
form.append( "tagsId", tagsId );
form.append( "ingredientsId", ingredientsId );
const XML = new XMLHttpRequest();
XML.open( "POST", '/api/recettes/list' );
XML.onreadystatechange = function ( e ) {
if( XML.status === 200 && XML.readyState === XMLHttpRequest.DONE ) {
try {
console.log( XML.response );
let response = JSON.parse(XML.response);
console.log( "Reponse parsé" );
buildCards( response.data );
} catch( e ) {
console.log( "Ce n'est pas un fichier JSON" );
}
}
}
XML.send( form );
}
const INGREDIENTSLIST = document.getElementById( 'ingredientsList' );
for( let item of INGREDIENTSLIST.getElementsByTagName( 'li' ) ){
item.addEventListener( 'click', function(){
let ingredientId = Number( item.getAttribute( 'data-ingredient-id' ) );
let index = ingredientsId.indexOf( ingredientId );
if( index > -1 ) {
ingredientsId.splice( index, 1 );
item.classList.add( 'tag-unselected' );
item.classList.remove( 'tag-selected' );
} else {
ingredientsId.push( ingredientId );
item.classList.add( 'tag-selected' );
item.classList.remove( 'tag-unselected' );
}
advancedSearch();
});
}
const TAGSLIST = document.getElementById( 'tagsList' );
for( let item of TAGSLIST.getElementsByTagName( 'li' ) ){
item.addEventListener( 'click', function(){
let tagId = Number( item.getAttribute( 'data-tag-id' ) );
let index = tagsId.indexOf( tagId );
if( index > -1 ) {
tagsId.splice( index, 1 );
item.classList.add( 'tag-unselected' );
item.classList.remove( 'tag-selected' );
} else {
tagsId.push( tagId );
item.classList.add( 'tag-selected' );
item.classList.remove( 'tag-unselected' );
}
advancedSearch();
});
}
const SEARCHBAR = document.getElementById( 'searchForm' );
searchInput = new URLSearchParams(document.location.search).get("s") || searchInput;
if( searchInput !== undefined && searchInput != "" ) {
advancedSearch();
}
SEARCHBAR.onsubmit = function( e ){
e.preventDefault();
searchInput = document.getElementById( 'searchFormField' ).value;
advancedSearch();
}
});

View File

@@ -1,169 +0,0 @@
document.addEventListener("DOMContentLoaded", function(){
let compl_form = document.getElementById("recette-form-complete");
console.log(compl_form);
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 nomIngredient = document.getElementById("recette-form-ingr-nom");
let image = document.getElementById("recette-form-ingr-photo");
if( nomIngredient.value === "" || image.value === "" ){
alert( "Un champ requis est manquant." );
return;
}
let ingr = form.nom;
let form_data = new 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",document.getElementById('recette-form-ingr-nom').value);
new_elem.innerText = document.getElementById('recette-form-ingr-nom').value;
cible.appendChild(new_elem);
document.getElementById('recette-form-ingr-nom').value = "";
}
})
}
})
}
function formulaire_tag_update(event){
event.preventDefault();
let form = document.getElementById("recette-form-tag-add");
let cible = document.getElementById("recette-form-tag");
let nomTag = document.getElementById("recette-form-tag-nom");
if( nomTag.value === "" ){
alert( "Un champ requis est manquant." );
return;
}
let tag = form.tag;
let form_data = new 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",nomTag.value);
new_elem.innerText = nomTag.value;
cible.appendChild(new_elem);
nomTag.value = "";
}
})
}
})
}
function formulaire_traitement(event){
event.preventDefault();
console.log( event );
let nom = document.getElementById('recette-form-nom');
let temps = document.getElementsByName('recette-form-nom');
let photo = document.getElementById('recette-form-photo');
let ingredients = document.getElementById('recette-form-ingr');
let tags = document.getElementById('recette-form-tag');
let description = document.getElementById('recette-form-description');
if( nom.value === "" || temps.value == "" || ingredients.value === "" || tags.value === ""){
console.log( "Another field" );
alert( "Un champ requis est manquant." );
return;
}
if( !IS_EDIT && photo.value === ""){
console.log( "Photo field" );
alert( "Un champ requis est manquant." );
return;
}
if( temps <= 0 ){
alert( "Le temps doit être positif ou supérieur à 0." );
return;
}
let form_data = new FormData(compl_form);
form_data.append( "ingredients", form_data.getAll("ingr").join(",") );
form_data.append( "tags", form_data.getAll( "tag").join(",") );
if( RECETTE_ID !== 0 )
form_data.append( 'id', RECETTE_ID );
let endpoint = IS_EDIT ? "/api/recettes/edit" : "/api/recettes/create";
fetch( endpoint, {
method : "POST",
body: form_data
}).then( reponse => {
if (!reponse.ok) {
alert( "Erreur survenie" );
}
else {
reponse.json().then( data => {
if (data.success) {
alert( "Envoyé" );
} else {
alert( data.error || "" );
}
})
}
})
}
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);
compl_form.addEventListener('submit', formulaire_traitement);
})

View File

@@ -1,66 +0,0 @@
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." );
}
})
}
})
}
})

View File

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

View File

BIN
random-recette.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

72
recette-en-dur.php Normal file
View File

@@ -0,0 +1,72 @@
<!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 Normal file
View File

@@ -0,0 +1,22 @@
<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 Normal file
View File

@@ -0,0 +1,74 @@
<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>

View File

@@ -1,41 +0,0 @@
<?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;
}

View File

@@ -1,39 +0,0 @@
<?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;
/**
* L'URL vers l'image de l'ingrédient.
* @var string
*/
public string $photo_ingredient;
/**
* Retourne le numéro de l'ingrédient.
* @return int
*/
public function getID(): int
{
return $this->num_ingredient;
}
}

View File

@@ -1,70 +0,0 @@
<?php
namespace App\Domain\Ingredients;
use App\Domain\Controller;
use App\Domain\Ingredients\IngredientRepository;
use App\Domain\Tags\TagRepository;
use App\Helpers\Authentification;
use App\Http\JSONResponse;
use App\Http\Request;
use App\Infrastructure\View;
class IngredientManagementController extends Controller {
public static function defineRoutes(): array
{
return [
self::Route( routeUrl: '/manage/ingredients', routeName: 'manage->ingredients', routeAction: 'index' ),
self::Route( routeUrl: '/ingredients/edit/{int}', routeName: 'ingredients->edit', routeAction: 'edit', routeMethods: [ 'POST' ] ),
self::Route( routeUrl: '/ingredients/delete/{int}', routeName: 'ingredients->delete', routeAction: 'delete' ),
];
}
public function index(){
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$tags = new IngredientRepository()->getAll();
return new View( 'ingredients/index', [
'ingredients' => $tags,
]);
}
public function edit( int $idIngredient ): never {
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$ing = new IngredientRepository()->getByID( $idIngredient );
if( !$ing )
die( "Ingredient not found" );
$name = Request::post( 'modif_ingr' );
if( !$name || $name == "" )
JSONResponse::sendError( [ 'error' => 'Name not defined' ] );
$ing->nom_ingredient = $name;
if( !new IngredientRepository()->update( $ing ) )
JSONResponse::sendError( [ 'error' => 'An error occured while updating ingredient' ] );
Request::redirectTo( 'manage->ingredients');
}
public function delete( int $idIngredient ): never {
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$ing = new IngredientRepository()->getByID( $idIngredient );
if( !$ing )
die( "Tag not found" );
if( !new IngredientRepository()->delete( $ing ) )
JSONResponse::sendError( [ 'error' => 'An error occured while deleting ingredient' ] );
Request::redirectTo( 'manage->ingredients' );
}
}

View File

@@ -1,175 +0,0 @@
<?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', 'photo_ingredient',
],
'link_recettes' => 'Listeingredient'
];
}
/**
* Permet d'obtenir une liste de tous les ingrédients objet Ingreident.
*
* @return Ingredient[]|null
*/
public function getAll(): ?array {
$sqlQuery = "SELECT * FROM {$this->tableName};";
$results = $this->selectGetAll($sqlQuery);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'obtenir un ingrédient spécifique par son ID.
*
* @param int $id
*
* @return Ingredient|null
*/
public function getByID( int $id ): ?Ingredient {
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_ingredient = {$id}";
$results = $this->selectGetAll($sqlQuery);
if( $results === null || count( $results ) > 1 )
return null;
return $results[0];
}
/**
* Permet d'obtenir, sous forme de liste, toutes les entrées qui lient des ingrédients.
*
* @param string $linkedTo La table qui permet de faire la liaison des ingrédients avec une autre entité.
* @param string $linkingField Le champ qui permet de faire la liaison des ingrédients avec une autre entité.
* @param Model $linkedEntity L'autre entité.
*
* @see LinkableInterface
* @return array|null
*/
public function getIdLinkedTo( string $linkedTo, string $linkingField, Model $linkedEntity ): ?array {
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return null;
$sqlQuery = "SELECT * FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()};";
$results = $this->selectGetAll($sqlQuery, true);
if( $results === null )
return null;
return $results;
}
/**
* Permet d'ajouter un lien entre un ingrédient et une autre entité comme Recette.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $ingredientEntity
*
* @see LinkableInterface
* @return bool
*/
public function addLinkBetween( string $linkedTo, string $linkingField, Model $linkedEntity, Model $ingredientEntity ): bool {
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return false;
$query = "INSERT INTO {$this->globalStructure[$linkName]} ({$linkingField},num_ingredient) VALUES ({$linkedEntity->getId()}, {$ingredientEntity->getID()});";
$statement = Kernel::$DB->pdo->prepare( $query );
return $statement->execute();
}
/**
* Retire un lien dans la BDD entre un ingrédient et une autre entité.
*
* @param string $linkedTo
* @param string $linkingField
* @param Model $linkedEntity
* @param Model $ingredientEntity
*
* @see LinkableInterface
* @return bool
*/
public function removeLinkBetween(string $linkedTo, string $linkingField, Model $linkedEntity, Model $ingredientEntity ): bool
{
$linkName = 'link_' . $linkedTo;
if( !isset( $this->globalStructure[$linkName]))
return false;
$query = "DELETE FROM {$this->globalStructure[$linkName]} WHERE {$linkingField} = {$linkedEntity->getId()} AND num_ingredient = {$ingredientEntity->getId()};";
$statement = Kernel::$DB->pdo->prepare( $query );
return $statement->execute();
}
/**
* Ajouter un ingrédient dans la BDD.
*
* @param Model $ingredient
*
* @return bool
*/
public function add( Model $ingredient ): bool {
return $this->addEntity( $ingredient );
}
/**
* Met à jour un ingrédient dans la BDD.
*
* @param Model $ingredient
*
* @return bool
*/
public function update( Model $ingredient ): bool {
return $this->updateEntity( $ingredient, 'num_ingredient' );
}
/**
* Supprime un ingrédient de la BDD.
*
* @param Model $ingredient
*
* @return bool
*/
public function delete( Model $ingredient ): bool {
return $this->deleteEntity( $ingredient, 'num_ingredient' );
}
}

View File

@@ -1,70 +0,0 @@
<?php
namespace App\Domain\Ingredients;
use App\Domain\Controller;
use App\Helpers\UploadFiles;
use App\Http\JSONResponse;
use App\Http\Request;
class IngredientsAPIController extends Controller {
public static function defineRoutes(): array
{
return [
self::Route( routeUrl: '/api/ingredients/create', routeName: 'api->ingredients->create', routeAction: 'create', routeMethods: ['POST'] ),
self::Route( routeUrl: '/api/ingredients/edit', routeName: 'api->ingredients->edit', routeAction: 'edit', routeMethods: ['POST'] ),
];
}
public function create(){
$name = Request::post( 'name' );
$fileNameField = "image";
if( !$name || $name == "" )
JSONResponse::sendError( [ 'error' => 'Name not defined' ] );
$urlOrError = UploadFiles::uploadFile( $fileNameField );
if( is_int( $urlOrError ) ){
JSONResponse::sendError( [ 'error' => $urlOrError ] );
}
$ingredient = new Ingredient();
$ingredient->num_ingredient = 0;
$ingredient->nom_ingredient = $name;
$ingredient->photo_ingredient = $urlOrError;
if( !new IngredientRepository()->add( $ingredient ) )
JSONResponse::sendError( [ 'error' => 'An error occured while adding ingredient' ] );
JSONResponse::sendSuccess( [ 'image_url' => $urlOrError ] );
}
public function edit(){
$id = Request::post( 'id' ) ?? 0;
$ingredient = new IngredientRepository()->getByID( $id );
if( !$ingredient )
JSONResponse::sendError( [ 'error' => 'Ingredient not found' ] );
$name = Request::post( 'name' );
$fileNameField = "image";
if( $name ) {
$ingredient->nom_ingredient = $name;
}
$urlOrError = UploadFiles::uploadFile( $fileNameField );
if( !is_int( $urlOrError ) ){
$ingredient->photo_ingredient = $urlOrError;
}
if( !new IngredientRepository()->update( $ingredient ) )
JSONResponse::sendError( [ 'error' => 'An error occured while updating ingredient' ] );
JSONResponse::sendSuccess( [] );
}
}

View File

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

View File

@@ -1,51 +0,0 @@
<?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;
}

View File

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

View File

@@ -1,34 +0,0 @@
<?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' ] );
}
}

View File

@@ -1,63 +0,0 @@
<?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\Tags\Tag;
use App\Helpers\Markdown;
/**
* Classe qui va permettre de gérer tous les objets recette.
*/
#[\AllowDynamicProperties]
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 );
}
/**
* Récupère une liste de tous les tags liés à la recette.
* @return Tag[]|null
*/
public function getAllLinkedTags(): ?array
{
return new RecetteRepository()->getAllLinkedTags( $this );
}
public function getNumberOfIngredients(): int {
$response = $this->getAllLinkedIngredients();
return $response !== null ? count( $response ) : 0;
}
}

View File

@@ -1,196 +0,0 @@
<?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 advancedRecetteSearch( string $title = "", array $tagsId= [], array $ingredientsId= [] ): ?array {
$tableLinkIngredients = IngredientRepository::getStructure()['link_recettes'];
$tableLinkTags = TagRepository::getStructure()['link_recettes'];
$sqlQuery = "SELECT ({$this->tableName}.num_recette) FROM {$this->tableName} LEFT JOIN {$tableLinkIngredients} ON {$this->tableName}.num_recette = {$tableLinkIngredients}.num_recette LEFT JOIN {$tableLinkTags} ON {$this->tableName}.num_recette = {$tableLinkTags}.num_recette";
if( $title != "" || $tagsId !== [] || $ingredientsId !== [] ) {
$sqlQuery .= " WHERE ";
if ($title != "")
$sqlQuery .= " titre_recette LIKE '%{$title}%' AND";
if ($tagsId !== [])
$sqlQuery .= " num_tag IN (" . implode(",", $tagsId) . ") AND";
if ($ingredientsId !== [])
$sqlQuery .= " num_ingredient IN (" . implode(",", $ingredientsId) . ") AND";
$sqlQuery = substr($sqlQuery, 0, -3);
}
$sqlQuery .= ";";
$results = $this->selectGetAll($sqlQuery, true);
if( $results === null )
return null;
$alreadyGettedId = [];
for( $i = 0; $i < count( $results ); $i++ ){
if( in_array( $results[$i]['num_recette'], $alreadyGettedId ) )
unset( $results[$i] );
else
$alreadyGettedId[] = $results[$i]['num_recette'];
}
return $results;
}
public function add( Model $recette ): bool {
return $this->addEntity( $recette );
}
public function update( Model $recette ): bool {
return $this->updateEntity( $recette, 'num_recette' );
}
public function delete( Model $recette ): bool {
return $this->deleteEntity( $recette, 'num_recette' );
}
public function getAllLinkedIngredients(Model $entity): ?array
{
$ingredientRepo = new IngredientRepository();
$response = $ingredientRepo->getIdLinkedTo( 'recettes', 'num_recette', $entity );
if( $response === null )
return null;
return array_map( function($arr) use($ingredientRepo) {
return $ingredientRepo->getByID( $arr['num_ingredient'] );
}, $response );
}
public function addAnIngredient(Ingredient $ingredient, Model $entity): bool
{
$ingredientRepo = new IngredientRepository();
return $ingredientRepo->addLinkBetween( 'recettes', 'num_recette', $entity, $ingredient );
}
public function removeAnIngredient(Ingredient $ingredient, Model $entity): bool
{
$ingredientRepo = new IngredientRepository();
return $ingredientRepo->removeLinkBetween( 'recettes', 'num_recette', $entity, $ingredient );
}
public function getAllLinkedTags(Model $entity): ?array
{
$tagRepo = new TagRepository();
$response = $tagRepo->getIdLinkedTo( 'recettes', 'num_recette', $entity );
if( $response === null )
return null;
return array_map( function($arr) use($tagRepo) {
return $tagRepo->getByID( $arr['num_tag'] );
}, $response );
}
public function addATag(Tag $tag, Model $entity): bool
{
$tagRepo = new TagRepository();
return $tagRepo->addLinkBetween( 'recettes', 'num_recette', $entity, $tag );
}
public function removeATag(Tag $tag, Model $entity): bool
{
$tagRepo = new TagRepository();
return $tagRepo->removeLinkBetween( 'recettes', 'num_recette', $entity, $tag );
}
}

View File

@@ -1,301 +0,0 @@
<?php
namespace App\Domain\Recettes;
use App\Domain\Controller;
use App\Domain\Ingredients\IngredientRepository;
use App\Domain\Tags\TagRepository;
use App\Helpers\UploadFiles;
use App\Http\JSONResponse;
use App\Http\Request;
use App\Http\Router;
class RecettesAPIController extends Controller {
public static function defineRoutes(): array
{
return [
self::Route( routeUrl: '/api/recettes/list', routeName: 'api->recettes->list', routeAction: 'list', routeMethods: ['POST'] ),
self::Route( routeUrl: '/api/recettes/create', routeName: 'api->recettes->create', routeAction: 'create', routeMethods: ['POST'] ),
self::Route( routeUrl: '/api/recettes/edit', routeName: 'api->recettes->edit', routeAction: 'edit', routeMethods: ['POST'] ),
self::Route( routeUrl: '/api/recettes/delete', routeName: 'api->recettes->delete', routeAction: 'delete', routeMethods: ['POST'] ),
];
}
public function list(){
$title = Request::post( 'title' ) ?? "";
$tagsId = explode( ",", Request::post( 'tagsId' ) ) ?? [];
$ingredientsId = explode( ",", Request::post( 'ingredientsId' ) ) ?? [];
if( $tagsId == [ "" ] )
$tagsId = [];
if( $ingredientsId == [ "" ] )
$ingredientsId = [];
$tagsId = array_map( 'intval', $tagsId );
$ingredientsId = array_map( 'intval', $ingredientsId );
$recetteRepo = new RecetteRepository();
$resp = $recetteRepo->advancedRecetteSearch( $title, $tagsId, $ingredientsId );
$objResp = [];
foreach( $resp as $r ){
$r = $recetteRepo->getByID( $r['num_recette'] );
$r->url = Router::getRouteURL( 'recettes->show', $r->slug );
$r->nb_ingredients = $r->getNumberOfIngredients();
if( !in_array( $r, $objResp ) ){
$objResp[] = $r;
}
}
JSONResponse::sendSuccess( [ 'data' => $objResp ] );
}
public function create(){
// Récupération des champs.
$name = Request::post( 'nom' ) ?? "";
$temps = Request::post( 'temps' ) ?? "";
$fileField = "image";
$tagsId = explode( ",", Request::post( 'tags' ) ) ?? [];
$ingredientsId = explode( ",", Request::post( 'ingredients' ) ) ?? [];
if( $tagsId == [ "" ] )
$tagsId = [];
if( $ingredientsId == [ "" ] )
$ingredientsId = [];
$tagsId = array_map( 'intval', $tagsId );
$ingredientsId = array_map( 'intval', $ingredientsId );
$description = Request::post( 'description' );
// Vérification.
if( $name == "" || $temps == "" || $ingredientsId == [] || $description == "" )
JSONResponse::sendError( [ 'error' => "One required fields is missing" ] );
// Upload & Vérification de l'image.
$urlOrError = UploadFiles::uploadFile( $fileField );
if( is_int( $urlOrError ) ){
JSONResponse::sendError( [ 'error' => $urlOrError ] );
}
// Vérification d'une entrée qui existerait déjà.
$slug = strtolower( $name );
$slug = str_replace( ' ', '', $slug );
$recetteRepo = new RecetteRepository();
if( $recetteRepo->getBySlug( $slug ) ){
JSONResponse::sendError( [ 'error' => "Recette already exists" ] );
}
if( in_array( $slug, [ 'create', 'edit', 'delete' ] ) ){
JSONResponse::sendError( [ 'error' => "Recette already exists" ] );
}
// Création de la recette.
$recette = new Recette();
$recette->num_recette = 0;
$recette->titre_recette = $name;
$recette->slug = $slug;
$recette->temps_de_preparation = intval( $temps <= 0 ? 1 : $temps );
$recette->photo = $urlOrError;
$recette->description_recette = $description;
$recette->publication_date = date("Y-m-d");
// Importation de la recette.
if( !$recetteRepo->add( $recette ) ){
JSONResponse::sendError( [ 'error' => "Error adding recette" ] );
}
// Pour actualiser l'ID.
$recette = $recetteRepo->getBySlug( $slug );
// Ajout du lien ingrédients recettes.
foreach( $ingredientsId as $ingredientId ){
$ingredient = new IngredientRepository()->getByID( $ingredientId );
if( $ingredient == null )
continue;
$recetteRepo->addAnIngredient( $ingredient, $recette );
}
// Ajout du lien Recettes tags.
if( $tagsId != [] ){
foreach( $tagsId as $tagId ){
$tag = new TagRepository()->getByID( $tagId );
if( $tag == null )
continue;
$recetteRepo->addATag( $tag, $recette );
}
}
JSONResponse::sendSuccess( [ 'recette' => $recette ] );
}
public function edit(){
// Récupération des champs.
$id = Request::post( 'id' );
$name = Request::post( 'nom' ) ?? "";
$temps = Request::post( 'temps' ) ?? "";
$fileField = "image";
$tagsId = explode( ",", Request::post( 'tags' ) ) ?? [];
$ingredientsId = explode( ",", Request::post( 'ingredients' ) ) ?? [];
if( $tagsId == [ "" ] )
$tagsId = [];
if( $ingredientsId == [ "" ] )
$ingredientsId = [];
$tagsId = array_map( 'intval', $tagsId );
$ingredientsId = array_map( 'intval', $ingredientsId );
$description = Request::post( 'description' );
// Vérification.
if( $id == "" || $name == "" || $temps == "" || $ingredientsId == [] || $description == "" )
JSONResponse::sendError( [ 'error' => "One required fields is missing" ] );
$recetteRepo = new RecetteRepository();
$recette = $recetteRepo->getByID( $id );
if( !$recette ){
JSONResponse::sendError( [ 'error' => "Recette not found" ] );
}
// Upload & Vérification de l'image.
$urlOrError = UploadFiles::uploadFile( $fileField );
if( is_int( $urlOrError ) ){
// Ingore image.
} else {
$recette->photo = $urlOrError;
}
// Vérification d'une entrée qui existerait déjà.
$slug = strtolower( $name );
$slug = str_replace( ' ', '', $slug );
$recette->titre_recette = $name;
$recette->slug = $slug;
$recette->temps_de_preparation = intval( $temps <= 0 ? 1 : $temps );
$recette->description_recette = $description;
// Importation de la recette.
if( !$recetteRepo->update( $recette ) ){
JSONResponse::sendError( [ 'error' => "Error updating recette" ] );
}
// Actualisation des ingrédients.
$ingrObjArray = $recette->getAllLinkedIngredients();
if( !$ingrObjArray || $ingrObjArray === [] ){ // Si il n'y a pas déjà d'autres ingrédients, on ajoute tous ceux présents ici.
foreach( $ingredientsId as $ingredientId ){
$ingredient = new IngredientRepository()->getByID( $ingredientId );
if( $ingredient == null )
continue;
$recetteRepo->addAnIngredient( $ingredient, $recette );
}
} else {
$ingrIntArray = array_map( function($ingr){
return intval( $ingr->getID() );
}, $ingrObjArray );
foreach( $ingrIntArray as $ingrId ){
// Si un ingrédient n'est pas dans la nouvelle liste d'ingrédients, il faut retirer le lien.
if( !in_array( $ingrId, $ingredientsId ) ){
$ingredient = new IngredientRepository()->getByID( $ingrId );
if( $ingredient == null )
continue;
$recetteRepo->removeAnIngredient( $ingredient, $recette );
}
}
foreach( $ingredientsId as $ingrId ){
// Si un ingrédient n'est pas dans l'ancienne liste, il faut ajouter un lien.
if( !in_array( $ingrId, $ingrIntArray ) ){
$ingredient = new IngredientRepository()->getByID( $ingrId );
if( $ingredient == null )
continue;
$recetteRepo->addAnIngredient( $ingredient, $recette );
}
}
}
// Actualisation des tags.
if( $tagsId != [] ) {
$tagObjArray = $recette->getAllLinkedTags();
if (!$tagObjArray || $tagObjArray === []) { // Si il n'y a pas déjà d'autres tags, on ajoute tous ceux présents ici.
foreach ($tagsId as $tagId) {
$tag = new TagRepository()->getByID($tagId);
if ($tag == null)
continue;
$recetteRepo->addATag($tag, $recette);
}
} else {
$tagIntArray = array_map(function ($ingr) {
return intval( $ingr->getID() );
}, $tagObjArray);
foreach ($tagIntArray as $tagId) {
// Si un ingrédient n'est pas dans la nouvelle liste d'ingrédients, il faut retirer le lien.
if (!in_array($tagId, $tagsId)) {
$tag = new TagRepository()->getByID($tagId);
if ($tag == null)
continue;
$recetteRepo->removeATag($tag, $recette);
}
}
foreach ($tagsId as $tagId) {
// Si un ingrédient n'est pas dans l'ancienne liste, il faut ajouter un lien.
if (!in_array($tagId, $tagIntArray)) {
$tag = new TagRepository()->getByID($tagId);
if ($tag == null)
continue;
$recetteRepo->addATag($tag, $recette);
}
}
}
}
JSONResponse::sendSuccess( [ 'recette' => $recette ] );
}
public function delete(){
$id = Request::post( 'id' );
$recette = new RecetteRepository()->getByID( $id );
if( !$recette )
JSONResponse::sendError( [ 'error' => 'Recette not found' ] );
$recetteRepo = new RecetteRepository();
// Retirer tous les liens Ingrédients/Tags.
foreach( ( $recette->getAllLinkedIngredients() ?? [] ) as $ingr ){
$recetteRepo->removeAnIngredient( $ingr, $recette );
}
foreach( ( $recette->getAllLinkedTags() ?? [] ) as $tag ){
$recetteRepo->removeATag( $tag, $recette );
}
if( !$recetteRepo->delete( $recette ) )
JSONResponse::sendError( [ 'error' => 'An error occured while deleting recette' ] );
JSONResponse::sendSuccess();
}
}

View File

@@ -1,47 +0,0 @@
<?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 ),
] );
}
public function show( string $slug ): View {
$recette = new RecetteRepository()->getBySlug( $slug );
if( !$recette )
die( "Recette not found" );
return new View( 'recettes/show', [
'recette' => $recette,
]);
}
}

View File

@@ -1,85 +0,0 @@
<?php
namespace App\Domain\Recettes;
use App\Domain\Controller;
use App\Domain\Ingredients\IngredientRepository;
use App\Domain\Tags\TagRepository;
use App\Helpers\Authentification;
use App\Http\JSONResponse;
use App\Http\Request;
use App\Infrastructure\View;
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' ),
self::Route( routeUrl: '/recettes/delete/{int}', routeName: 'recettes->delete', routeAction: 'delete' ),
];
}
public function create(): View {
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
return new View( 'recettes/create', [
'tags' => new TagRepository()->getAll(),
'ingredients' => new IngredientRepository()->getAll(),
] );
}
public function edit( int $idRecette ): View {
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$recette = new RecetteRepository()->getByID( $idRecette );
if( !$recette )
die( "Recette not found" );
$tagsID = array_map( function($tag) {
return $tag->getId();
}
, $recette->getAllLinkedTags() ?? [] );
$ingredientsID = array_map( function($tag) {
return $tag->getId();
}
, $recette->getAllLinkedIngredients() ?? [] );
return new View( 'recettes/edit', [
'recette' => $recette,
'recetteTags' => $tagsID,
'recetteIngr' => $ingredientsID,
'tags' => new TagRepository()->getAll(),
'ingredients' => new IngredientRepository()->getAll(),
]);
}
public function delete( int $idRecette ): never {
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$recette = new RecetteRepository()->getByID( $idRecette );
if( !$recette )
die( "Recette not found" );
$recetteRepo = new RecetteRepository();
// Retirer tous les liens Ingrédients/Tags.
foreach( ( $recette->getAllLinkedIngredients() ?? [] ) as $ingr ){
$recetteRepo->removeAnIngredient( $ingr, $recette );
}
foreach( ( $recette->getAllLinkedTags() ?? [] ) as $tag ){
$recetteRepo->removeATag( $tag, $recette );
}
if( !$recetteRepo->delete( $recette ) )
JSONResponse::sendError( [ 'error' => 'An error occured while deleting recette' ] );
Request::redirectTo( 'recettes->index' );
}
}

View File

@@ -1,170 +0,0 @@
<?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;
}

View File

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

View File

@@ -1,69 +0,0 @@
<?php
namespace App\Domain\Tags;
use App\Domain\Controller;
use App\Domain\Ingredients\IngredientRepository;
use App\Domain\Tags\TagRepository;
use App\Helpers\Authentification;
use App\Http\JSONResponse;
use App\Http\Request;
use App\Infrastructure\View;
class TagManagementController extends Controller {
public static function defineRoutes(): array
{
return [
self::Route( routeUrl: '/manage/tags', routeName: 'manage->tags', routeAction: 'index' ),
self::Route( routeUrl: '/tags/edit/{int}', routeName: 'tags->edit', routeAction: 'edit', routeMethods: [ 'POST' ] ),
self::Route( routeUrl: '/tags/delete/{int}', routeName: 'tags->delete', routeAction: 'delete' ),
];
}
public function index(){
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$tags = new TagRepository()->getAll();
return new View( 'tags/index', [
'tags' => $tags,
]);
}
public function edit( int $idTag ): never {
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$tag = new TagRepository()->getByID( $idTag );
if( !$tag )
die( "Tag not found" );
$name = Request::post( 'modif_tag' );
if( !$name || $name == "" )
JSONResponse::sendError( [ 'error' => 'Name not defined' ] );
$tag->nom_tag = $name;
if( !new TagRepository()->update( $tag ) )
JSONResponse::sendError( [ 'error' => 'An error occured while updating tag' ] );
Request::redirectTo( 'manage->tags');
}
public function delete( int $idTag ): never {
if( !Authentification::isLoggedIn() )
die( "Not logged in" );
$tag = new \App\Domain\Tags\TagRepository()->getByID( $idTag );
if( !$tag )
die( "Tag not found" );
if( !new TagRepository()->delete( $tag ) )
JSONResponse::sendError( [ 'error' => 'An error occured while deleting tag' ] );
Request::redirectTo( 'manage->tags' );
}
}

View File

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

View File

@@ -1,72 +0,0 @@
<?php
namespace App\Domain\Tags;
use App\Domain\Controller;
use App\Helpers\UploadFiles;
use App\Http\JSONResponse;
use App\Http\Request;
class TagsAPIController extends Controller {
public static function defineRoutes(): array
{
return [
self::Route( routeUrl: '/api/tags/create', routeName: 'api->tags->create', routeAction: 'create', routeMethods: ['POST'] ),
self::Route( routeUrl: '/api/tags/edit', routeName: 'api->tags->edit', routeAction: 'edit', routeMethods: ['POST'] ),
self::Route( routeUrl: '/api/tags/delete', routeName: 'api->tags->delete', routeAction: 'delete', routeMethods: ['POST'] ),
];
}
public function create(){
$name = Request::post( 'name' );
if( !$name || $name == "" )
JSONResponse::sendError( [ 'error' => 'Name not defined' ] );
$tag = new Tag();
$tag->num_tag = 0;
$tag->nom_tag = $name;
if( !new TagRepository()->add( $tag ) )
JSONResponse::sendError( [ 'error' => 'An error occured while adding tag' ] );
JSONResponse::sendSuccess();
}
public function edit(){
$id = Request::post( 'id' ) ?? 0;
$tag = new TagRepository()->getByID( $id );
if( !$tag )
JSONResponse::sendError( [ 'error' => 'Tag not found' ] );
$name = Request::post( 'name' );
if( !$name || $name == "" )
JSONResponse::sendError( [ 'error' => 'Name not defined' ] );
$tag->nom_tag = $name;
if( !new TagRepository()->update( $tag ) )
JSONResponse::sendError( [ 'error' => 'An error occured while updating tag' ] );
JSONResponse::sendSuccess();
}
public function delete(){
$id = Request::post( 'id' ) ?? 0;
$tag = new TagRepository()->getByID( $id );
if( !$tag )
JSONResponse::sendError( [ 'error' => 'Tag not found' ] );
if( !new TagRepository()->delete( $tag ) )
JSONResponse::sendError( [ 'error' => 'An error occured while deleting tag' ] );
JSONResponse::sendSuccess();
}
}

View File

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

View File

@@ -1,68 +0,0 @@
<?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' );
// TODO : Récupération de l'utilisateur et verify_password.
$userId = 1;
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' ] );
}
}

View File

@@ -1,28 +0,0 @@
<?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();
}
}

View File

@@ -1,15 +0,0 @@
<?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);
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace App\Exceptions;
use Throwable;
class InvalidViewException extends \Exception {
public function __construct( string $viewName )
{
parent::__construct( "View {$viewName} does not exist.", 500 );
}
}

View File

@@ -1,49 +0,0 @@
<?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;
}
}

View File

@@ -1,38 +0,0 @@
<?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';
}
}

View File

@@ -1,42 +0,0 @@
<?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;
}
}

View File

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

View File

@@ -1,27 +0,0 @@
<?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;
}
}

View File

@@ -1,51 +0,0 @@
<?php
namespace App\Helpers;
use App\Http\Router;
use App\Kernel;
class UploadFiles {
public static function uploadFolderPath(){
return APP_ROOT . 'public/uploads/';
}
public static function uploadFolderUri(){
return Kernel::$configs['general']['website_url'] . 'uploads/';
}
/**
* @param string $fileName
*
* @return string|int
*
* 1: Pas de fichier $fileArg
* 2: Erreur dans le fichier
* 3: Déjà existant.
* 4: Erreur dans le déplacement.
*/
public static function uploadFile( string $fileArg ): string|int {
if( !isset( $_FILES[ $fileArg ] ) )
return 1;
$file = $_FILES[ $fileArg ];
if( $file['error'] != 0 )
return 2;
$tempFileName = $file['tmp_name'];
$fileName = $file['name'];
$full_name = self::uploadFolderPath() . $fileName;
$full_uri = self::uploadFolderUri() . $fileName;
if( file_exists( $full_name ) )
return 3;
if( !move_uploaded_file($tempFileName, $full_name) )
return 4;
return $full_uri;
}
}

View File

@@ -1,51 +0,0 @@
<?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 );
}
}

View File

@@ -1,69 +0,0 @@
<?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();
}
}

View File

@@ -1,42 +0,0 @@
<?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;
}
}

View File

@@ -1,199 +0,0 @@
<?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;
/**
* Informations de la route du client.
* @var Route
*/
public private(set) static Route $clientRoute;
/**
* Informations sur les arguments passés à la route.
* @var array
*/
public private(set) static array $clientRouteArguments;
/**
* 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);
}
/**
* 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;
}
}

View File

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

View File

@@ -1,220 +0,0 @@
<?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 );
}
}

View File

@@ -1,119 +0,0 @@
<?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'] );
}
}

View File

@@ -1,9 +0,0 @@
<?php use App\Infrastructure\View as V; ?>
<?php V::partial( 'header' ); ?>
<div class="main-body">
<?php V::inject( 'content'); ?>
</div>
<?php V::partial( 'footer' ); ?>

View File

@@ -1,2 +0,0 @@
<h1>Coucou</h1>
<?php $str = "Markdown **text**\n# SOLONG\n*text*."; $md = \App\Helpers\Markdown::convertToHTML( $str ); echo "<p>$md</p>"; ?>

View File

@@ -1,18 +0,0 @@
<?php use App\Domain\Tags\Tag;
use App\Infrastructure\View as V; ?>
<div class="content">
<?php foreach (V::arg( 'ingredients') as $ing): ?>
<div class="liste-ingr-elem">
<img class="ingr-image" src="<?php echo $ing->photo_ingredient; ?>">
<div class="liste-ingr-elem-text">
<?php echo $ing->nom_ingredient; ?>
</div>
<div class="liste-ingr-elem-modif">
<a class="recette-button ingr-modifier" data-id="<?php echo $ing->getId(); ?>">Modifier</a>
<a class="recette-button ingr-suppr" data-id="<?php echo $ing->getId(); ?>">Supprimer</a>
</div>
</div>
<?php endforeach; ?>
</div>
<script src="<?php V::assetUrl( 'js/manage-ingredients.js' ); ?>" ></script>

View File

@@ -1,23 +0,0 @@
<?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>

View File

@@ -1,14 +0,0 @@
<?php use App\Infrastructure\View as V; ?>
<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ées : Glaudis Jordan
</div>
</footer>
</body>
</html>

View File

@@ -1,35 +0,0 @@
<?php use App\Infrastructure\View as V; ?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo V::getHeadTitle(); ?></title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="<?php V::assetUrl( 'css/style.css' ); ?>" />
</head>
<body>
<header id="header">
<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>
<li><a id="ingrManage" class="nav-element" href="<?php V::routeUrl( 'manage->ingredients'); ?>">Gérer les ingrédients</a></li>
<li><a id="tagManage" class="nav-element" href="<?php V::routeUrl( 'manage->tags'); ?>">Gérer les tags</a></li>
<?php else: ?>
<li><a id="login" class= "nav-element" href="<?php V::routeUrl( 'login'); ?>">Login</a></li>
<?php endif; ?>
</ul>
<form id="searchForm" method="get" action="<?php V::routeUrl( 'recettes->index'); ?>">
<input name="s" id="searchFormField" type="text" class="search-form search-form-recette" placeholder="Rechercher une recette ..." value="<?php echo \App\Http\Request::get( 's' ); ?>">
</form>
</nav>
</header>

View File

@@ -1,40 +0,0 @@
<?php use App\Infrastructure\View as V; use App\Http\Router;?>
<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 id="tagsList">
<?php foreach( V::arg( 'tagsList') as $tag ): ?>
<li class="tag tag-unselected" data-tag-id="<?php echo $tag->num_tag;?>"><?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="ing-unselected-div">
<ul id="ingredientsList">
<?php foreach( V::arg( 'ingredientsList') as $tag ): ?>
<li class="tag tag-unselected" data-ingredient-id="<?php echo $tag->num_ingredient;?>"><?php echo $tag->nom_ingredient; ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
<script src="<?php echo Router::getAssetURL( 'js/advanced-search.js' ); ?>" defer></script>

View File

@@ -1,77 +0,0 @@
<?php use App\Domain\Recettes\Recette;
use App\Infrastructure\View as V; ?>
<div class="main-body">
<div class="content">
<div class="recette-add-form-all">
<form id="recette-form-complete" class="recette-form recette-form-add" action="/api/recettes/create" method="POST">
<div class="recette-form-group form-group">
<label for="nom">Titre : </label>
<input type="text" 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="number" 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">
<?php foreach( V::arg( 'ingredients') as $ingr ): ?>
<option value="<?php echo $ingr->getID(); ?>"><?php echo $ingr->nom_ingredient; ?></option>
<?php endforeach; ?>
</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">
<?php foreach( V::arg( 'tags') as $tag ): ?>
<option value="<?php echo $tag->getID(); ?>"><?php echo $tag->nom_tag; ?></option>
<?php endforeach; ?>
</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">
<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">
<div class="recette-form-group form-group">
<label for="tag">Tags</label>
<input type="text" class="form-control" id="recette-form-tag-nom" name="name" 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>
<script>
const IS_EDIT = false; const RECETTE_ID = 0;
</script>
<script src="<?php V::assetUrl( 'js/form.js' ); ?>" defer></script>

View File

@@ -1,77 +0,0 @@
<?php use App\Domain\Recettes\Recette;
use App\Infrastructure\View as V; ?>
<?php /** @var Recette $R */$R = V::arg( 'recette'); ?>
<div class="main-body">
<div class="content">
<div class="recette-add-form-all">
<form id="recette-form-complete" class="recette-form recette-form-add" action="/api/recettes/edit" method="POST">
<div class="recette-form-group form-group">
<label for="nom">Titre : </label>
<input type="text" class="form-control" id="recette-form-nom" name="nom" placeholder="Titre de votre recette" value="<?php echo $R->titre_recette; ?>">
</div>
<div class="recette-form-group form-group">
<label for="temps">Temps (en min) : </label>
<input type="number" class="form-control" id="recette-form-temps" name="temps" placeholder="Temps de préparation" value="<?php echo $R->temps_de_preparation; ?>">
</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">
<?php foreach( V::arg( 'ingredients') as $ingr ): ?>
<option value="<?php echo $ingr->getID(); ?>" <?php echo in_array( $ingr->getId(), V::arg( 'recetteIngr') ) ? "selected" : ""; ?> ><?php echo $ingr->nom_ingredient; ?></option>
<?php endforeach; ?>
</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">
<?php foreach( V::arg( 'tags') as $tag ): ?>
<option value="<?php echo $tag->getID(); ?>" <?php echo in_array( $tag->getId(), V::arg( 'recetteTags' ) ) ? "selected" : ""; ?> ><?php echo $tag->nom_tag; ?></option>
<?php endforeach; ?>
</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..."><?php echo $R->description_recette; ?></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">
<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">
<div class="recette-form-group form-group">
<label for="tag">Tags</label>
<input type="text" class="form-control" id="recette-form-tag-nom" name="name" 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>
<script>
const IS_EDIT = true; const RECETTE_ID = <?php echo $R->getId(); ?>;
</script>
<script src="<?php V::assetUrl( 'js/form.js' ); ?>" defer></script>

View File

@@ -1,19 +0,0 @@
<?php use App\Infrastructure\View as V; ?>
<?php V::partial( 'tag-sidebar' ); ?>
<div class="content">
<div id="recetteList" 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="<?php echo $recette->photo; ?>">
<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>

View File

@@ -1,55 +0,0 @@
<?php use App\Domain\Recettes\Recette;
use App\Infrastructure\View as V; ?>
<?php /** @var Recette $R */ $R = V::arg( 'recette' ); ?>
<div class="content">
<div class="recette-title">
<h1><?php echo $R->titre_recette; ?></h1>
</div>
<div class="recette-content">
<div class="recette-div-info">
<div class="recette-div-image">
<img class="recette-image" src="<?php echo $R->photo; ?>">
</div>
<div class="recette-div-liste-info">
<?php if( \App\Helpers\Authentification::isLoggedIn() ): ?>
<ul class="recette-list-buttons">
<li><a class="recette-button" href="#" id="delete" data-id="<?php echo $R->num_recette; ?>">Supprimer</a></li>
<li><a class="recette-button" href="<?php V::routeUrl( 'recettes->edit', $R->getID() ); ?>">Modifier</a></li>
</ul>
<?php endif; ?>
<ul class="recette-liste-info">
<li class="recette-liste-info-elem"><?php echo $R->publication_date; ?></li>
<li class="recette-liste-info-elem"><?php echo $R->temps_de_preparation; ?> minutes</li>
<li class="recette-liste-info-elem"><?php echo $R->getNumberOfIngredients(); ?> ingrédients</li>
</ul>
<ul class="recette-liste-tag">
<?php foreach( $R->getAllLinkedTags() as $tag ): ?>
<li class="tag tag-unselected"><?php echo $tag->nom_tag; ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
<div class="recette-liste-ingr-div">
<h3>Liste des ingrédients</h3>
<ul class="recette-liste-ingr">
<?php foreach( $R->getAllLinkedIngredients() as $ingr ): ?>
<li class="recette-liste-ingr-elem"><img class="ingr-image-prev" src="<?php echo $ingr->photo_ingredient; ?>"> <div class="recette-liste-ingr-elem-nom"><?php echo $ingr->nom_ingredient; ?></div></li>
<?php endforeach; ?>
</ul>
</div>
<div class = "recette-desc">
<div id="description">
<?php echo $R->getHTMLDescription(); ?>
</div>
</div>
</div>
</div>
<script>
document.getElementById( 'delete' ).addEventListener( 'click', e => {
if( window.confirm( "Êtes-vous sur de vouloir supprimer cette recette ? ") ){
let id = document.getElementById( 'delete' ).getAttribute( 'data-id' );
window.location.href = "/recettes/delete/" + id;
}
})
</script>

View File

@@ -1,17 +0,0 @@
<?php use App\Domain\Tags\Tag;
use App\Infrastructure\View as V; ?>
<div class="content">
<?php foreach( V::arg( 'tags' ) as $tag ): ?>
<div class="liste-tag-elem">
<div class="liste-tag-elem-text">
<?php echo $tag->nom_tag; ?>
</div>
<div class="liste-tag-elem-modif">
<a class="recette-button tag-modifier" data-id="<?php echo $tag->num_tag; ?>">Modifier</a>
<a class="recette-button tag-suppr" data-id="<?php echo $tag->num_tag; ?>">Supprimer</a>
</div>
</div>
<?php endforeach; ?>
</div>
<script src="<?php V::assetUrl( 'js/manage-tags.js' ); ?>" ></script>