Lundi #4
9
config/database.php
Normal file
9
config/database.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 3306,
|
||||||
|
'user' => 'benjamin',
|
||||||
|
'pass' => '011235813',
|
||||||
|
'name' => 'siterecette'
|
||||||
|
];
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap');
|
||||||
/* Sommaire :
|
/* Sommaire :
|
||||||
- body et html
|
- body et html
|
||||||
- Header et ses contenues
|
- Header et ses contenues
|
||||||
@@ -14,6 +15,7 @@ html, body{
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/*font-size: 98%;*/
|
/*font-size: 98%;*/
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -43,6 +45,11 @@ html, body{
|
|||||||
box-shadow: 1px 1px 1px black;
|
box-shadow: 1px 1px 1px black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo:hover{
|
||||||
|
box-shadow: 1px 1px 2px #0bbd90;
|
||||||
|
border: 1px solid #0bbd90;
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
flex: 9;
|
flex: 9;
|
||||||
text-align: initial;
|
text-align: initial;
|
||||||
@@ -64,6 +71,25 @@ nav {
|
|||||||
.nav-element {
|
.nav-element {
|
||||||
line-height: 2.5;
|
line-height: 2.5;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-list{
|
||||||
|
li:nth-of-type(3){
|
||||||
|
position: fixed;
|
||||||
|
right: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#login {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #ffc478;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login:hover {
|
||||||
|
background-color: #0bbd90;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Body et son contenu */
|
/*Body et son contenu */
|
||||||
@@ -77,21 +103,93 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
background-color: aquamarine;
|
background-color: #caecd6;
|
||||||
border: 1px solid rgba(0,0,0,.125);
|
border: 1px solid rgba(0,0,0,.125);
|
||||||
border-radius: 10px;
|
border-radius: 6px;
|
||||||
width: 128px;
|
width: 128px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tag-cont {
|
||||||
|
/*display:none;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.ingr-cont {
|
||||||
|
/*display:none;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-selected-div {
|
||||||
|
flex: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-unselected-div {
|
||||||
|
flex: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-search {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form-tag {
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form-recette {
|
||||||
|
width: 100%;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0px 0px 10px rgb(131, 131, 131);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 95%;
|
||||||
|
box-shadow: 1px 1px 1px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-selected {
|
||||||
|
background-color: #ffa04d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-selected:hover {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-unselected {
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
}
|
||||||
|
.tag-unselected:hover {
|
||||||
|
background-color: #ffa04d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hr {
|
||||||
|
border: 1px solid #0bbd90;
|
||||||
|
width: 93%;
|
||||||
|
box-shadow: 1px 3px 5px #0bbd90;
|
||||||
|
}
|
||||||
|
|
||||||
.main-body {
|
.main-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
overflow: auto;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-clip: border-box;
|
background-clip: border-box;
|
||||||
padding-right: 5%;
|
padding-right: 2%;
|
||||||
padding-left: 5%;
|
padding-left: 2%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* content : le cadre de base du centre de notre site
|
/* content : le cadre de base du centre de notre site
|
||||||
@@ -103,37 +201,77 @@ body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border: 1px solid rgba(0,0,0,.125);
|
border: 1px solid rgba(0,0,0,.125);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
overflow: auto;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Recettes :
|
/*Recettes :
|
||||||
représente le contenue centrale de la page.
|
représente le contenue centrale de la page.
|
||||||
*/
|
*/
|
||||||
.recettes {
|
.recettes {
|
||||||
--UneVariable: calc( 100vw / 500 );
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(var(--UneVariable), 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
grid-gap: 10px;
|
grid-gap: 10px;
|
||||||
grid-auto-rows: minmax(300px, auto);
|
/* grid-auto-rows: minmax(300px, auto);
|
||||||
grid-auto-columns: auto;
|
grid-auto-columns: auto;*/
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width : 1200px) {
|
||||||
|
.recettes {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width : 800px) {
|
||||||
|
.recettes {
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width : 600px) {
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin-left: 0px;
|
||||||
|
text-align: center;
|
||||||
|
padding : 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Classe recette icone :
|
/* Classe recette icone :
|
||||||
C'est la classe de chaques éléments de notre liste de recette
|
C'est la classe de chaques éléments de notre liste de recette
|
||||||
*/
|
*/
|
||||||
.recette-icone{
|
.recette-icone{
|
||||||
|
display: flex;
|
||||||
border: 3px solid rgba(0,0,0,.125);
|
border: 3px solid rgba(0,0,0,.125);
|
||||||
border-radius: 40px;
|
border-radius: 40px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
box-shadow: 3px 5px 5px black;
|
box-shadow: 3px 5px 5px black;
|
||||||
width: 500px;
|
max-width: 400px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recette-icone:hover{
|
||||||
|
border-color: #0bbd90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recette-icone-content{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recette-preview-image{
|
.recette-preview-image{
|
||||||
max-width: calc( 10vh + 10vw );
|
max-width: calc( 5vh + 5vw );
|
||||||
max-height: calc( 10vh + 10vw );
|
max-height: calc( 5vh + 5vw );
|
||||||
border: 1px solid rgb(252, 223, 57);
|
border: 1px solid rgb(252, 223, 57);
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
}
|
}
|
||||||
@@ -165,11 +303,102 @@ body {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*description*/
|
.recette-div-info {
|
||||||
.recette-desc {
|
display: flex;
|
||||||
padding : 10px;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recette-div-liste-info{
|
||||||
|
padding: 50px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recette-liste-info-elem {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recette-liste-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recette-liste-tag {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*description*/
|
||||||
|
.recette-desc {
|
||||||
|
padding : 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Login form*/
|
||||||
|
.formcont {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formcont form {
|
||||||
|
width: 50vmin;
|
||||||
|
height: 40vmin;
|
||||||
|
border: 2px solid rgb(0, 0, 0);
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5vmin;
|
||||||
|
background:white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 5px 5px 30px #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formcont form .form-group {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1.2vmin;
|
||||||
|
padding-left:0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formcont form .form-group label {
|
||||||
|
width: 95%;
|
||||||
|
text-align: center;
|
||||||
|
color:black;
|
||||||
|
font-size:5vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formcont form .form-group input {
|
||||||
|
width: 95%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size:3vmin;
|
||||||
|
padding:1vmin;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.formcont form button {
|
||||||
|
width: 48%;
|
||||||
|
height: 19%;
|
||||||
|
padding:0.5vmin;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #ffa04d;
|
||||||
|
color: black;
|
||||||
|
font-size: 5vmin;
|
||||||
|
border-radius: 7px;
|
||||||
|
box-shadow: 2px 2px 0px #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #0bbd90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 3px 4px 5px #8c8c8c;
|
||||||
|
}
|
||||||
|
|
||||||
/*Footer et son contenue*/
|
/*Footer et son contenue*/
|
||||||
footer{
|
footer{
|
||||||
|
|||||||
66
public/assets/js/login.js
Normal file
66
public/assets/js/login.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
document.addEventListener( 'DOMContentLoaded', function(){
|
||||||
|
const FORM = document.getElementById( 'login-form' );
|
||||||
|
const FORM_ERROR = document.getElementById( 'login-errors' );
|
||||||
|
if( !FORM )
|
||||||
|
return;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'afficher une erreur dans le formulaire.
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
FORM.showError = function( message ){
|
||||||
|
FORM_ERROR.style.display = 'block';
|
||||||
|
FORM_ERROR.innerText = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de retirer l'erreur du formulaire.
|
||||||
|
*/
|
||||||
|
FORM.removeError = function(){
|
||||||
|
FORM_ERROR.style.display = 'none';
|
||||||
|
FORM_ERROR.innerText = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gère l'envoi du formulaire.
|
||||||
|
* @param e
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
FORM.onsubmit = function( e ){
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
FORM.removeError();
|
||||||
|
|
||||||
|
let userField = e.target.username;
|
||||||
|
let passwordField = e.target.password;
|
||||||
|
|
||||||
|
if( !userField || !passwordField || userField.value === "" || passwordField.value === "" ){
|
||||||
|
FORM.showError( "Le nom d'utilisateur ou le mot de passe ne doivent pas être vide.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let formData = new FormData( FORM );
|
||||||
|
let options = {
|
||||||
|
method: "POST",
|
||||||
|
contentType: "application/json",
|
||||||
|
body: formData,
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch( FORM.action, options ).then( ( response ) => {
|
||||||
|
if( response.ok ){
|
||||||
|
response.json().then( ( responseJSON ) => {
|
||||||
|
|
||||||
|
if( responseJSON.success === true ){
|
||||||
|
window.location.href = window.location.origin; // Redirection sur la page d'accueil si succès.
|
||||||
|
} else {
|
||||||
|
FORM.showError( responseJSON.message || "Mauvais nom d'utilisateur ou mauvais mot de passe." );
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
13
src/Domain/Ingredients/Ingredient.php
Normal file
13
src/Domain/Ingredients/Ingredient.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain\Ingredients;
|
||||||
|
|
||||||
|
use App\Domain\Model;
|
||||||
|
|
||||||
|
class Ingredient extends Model {
|
||||||
|
|
||||||
|
public int $num_ingredient;
|
||||||
|
public string $nom_ingredient;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
29
src/Domain/Ingredients/IngredientRepository.php
Normal file
29
src/Domain/Ingredients/IngredientRepository.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain\Ingredients;
|
||||||
|
|
||||||
|
use App\Domain\Repository;
|
||||||
|
use App\Domain\Model;
|
||||||
|
|
||||||
|
class IngredientRepository extends Repository {
|
||||||
|
|
||||||
|
public static function getEntity(): string
|
||||||
|
{
|
||||||
|
return Ingredient::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getStructure(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'table' => 'Ingredient',
|
||||||
|
'columns' => [
|
||||||
|
'num_ingredient', 'nom_ingredient'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add( Model $ingredient ): bool {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/Domain/Model.php
Normal file
8
src/Domain/Model.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain;
|
||||||
|
|
||||||
|
abstract class Model {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
22
src/Domain/Recettes/Recette.php
Normal file
22
src/Domain/Recettes/Recette.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain\Recettes;
|
||||||
|
|
||||||
|
use App\Domain\Model;
|
||||||
|
use App\Helpers\Markdown;
|
||||||
|
|
||||||
|
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 getHTMLDescription(): string {
|
||||||
|
return Markdown::convertToHTML( $this->description_recette );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
66
src/Domain/Recettes/RecetteRepository.php
Normal file
66
src/Domain/Recettes/RecetteRepository.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain\Recettes;
|
||||||
|
|
||||||
|
use App\Domain\Model;
|
||||||
|
use App\Domain\Repository;
|
||||||
|
|
||||||
|
class RecetteRepository extends Repository {
|
||||||
|
|
||||||
|
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'avoir une recette par un ID.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return Recette|null
|
||||||
|
*/
|
||||||
|
public function getByID( int $id ): ?Recette {
|
||||||
|
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE num_recette = {$id}";
|
||||||
|
$results = $this->selectGetAll($sqlQuery);
|
||||||
|
if( $results === null || count( $results ) > 1 )
|
||||||
|
return null;
|
||||||
|
return $results[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'avoir une recette par un slug.
|
||||||
|
*
|
||||||
|
* @param string $slug
|
||||||
|
* @return Recette|null
|
||||||
|
*/
|
||||||
|
public function getBySlug( string $slug ): ?Recette {
|
||||||
|
$sqlQuery = "SELECT * FROM {$this->tableName} WHERE slug = {$slug}";
|
||||||
|
$results = $this->selectGetAll($sqlQuery);
|
||||||
|
if( $results === null || count( $results ) > 1 )
|
||||||
|
return null;
|
||||||
|
return $results[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add( Model $recette ): bool {
|
||||||
|
return $this->addEntity( $recette );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update( Model $recette ): bool {
|
||||||
|
return $this->updateEntity( $recette, 'num_recette' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete( Model $recette ): bool {
|
||||||
|
return $this->deleteEntity( $recette, 'num_recette' );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
99
src/Domain/Repository.php
Normal file
99
src/Domain/Repository.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain;
|
||||||
|
|
||||||
|
use App\Domain\Recettes\Recette;
|
||||||
|
use App\Kernel;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
final public string $tableName;
|
||||||
|
final public array $tableColumns;
|
||||||
|
|
||||||
|
public function __construct(){
|
||||||
|
$structure = static::getStructure();
|
||||||
|
|
||||||
|
$this->tableName = $structure['table'];
|
||||||
|
$this->tableColumns = $structure['columns'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'avoir tous les éléments correspondant à la requête passée en paramètre.
|
||||||
|
*
|
||||||
|
* @param string $sqlQuery
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function selectGetAll( string $sqlQuery ): ?array {
|
||||||
|
$statement = Kernel::$DB->pdo->prepare( $sqlQuery );
|
||||||
|
if( !$statement->execute() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$results = $statement->fetchAll( PDO::FETCH_CLASS, static::getEntity() );
|
||||||
|
if( empty( $results ) )
|
||||||
|
return null;
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function add( Model $entity ): bool;
|
||||||
|
abstract public function update( Model $entity ): bool;
|
||||||
|
abstract public function delete( Model $entity ): bool;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use App\Domain\Controller;
|
|||||||
use App\Helpers\Authentification;
|
use App\Helpers\Authentification;
|
||||||
use App\Http\JSONResponse;
|
use App\Http\JSONResponse;
|
||||||
use App\Http\Request;
|
use App\Http\Request;
|
||||||
|
use App\Infrastructure\View;
|
||||||
|
|
||||||
class AuthentificationController extends Controller {
|
class AuthentificationController extends Controller {
|
||||||
|
|
||||||
@@ -15,19 +16,29 @@ class AuthentificationController extends Controller {
|
|||||||
|
|
||||||
// Public routes.
|
// Public routes.
|
||||||
self::Route( routeUrl: '/login', routeName: 'login', routeAction: 'loginForm', pageHeadTitle: 'Connexion' ),
|
self::Route( routeUrl: '/login', routeName: 'login', routeAction: 'loginForm', pageHeadTitle: 'Connexion' ),
|
||||||
|
self::Route( routeUrl: '/logout', routeName: 'logout', routeAction: 'logoutPage', pageHeadTitle: 'Déconnexion' ),
|
||||||
|
|
||||||
// API Routes.
|
// API Routes.
|
||||||
self::Route( routeUrl: '/api/auth', routeName: 'api->auth', routeAction: 'auth', routeMethods: ['POST'] ),
|
self::Route( routeUrl: '/api/auth', routeName: 'api->auth', routeAction: 'auth', routeMethods: ['POST'] ),
|
||||||
self::Route( routeUrl: '/api/auth/logout', routeName: 'api->auth->logout', routeAction: 'logout', routeMethods: ['POST'] ),
|
// self::Route( routeUrl: '/api/auth/logout', routeName: 'api->auth->logout', routeAction: 'logout', routeMethods: ['POST'] ),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(): View {
|
public function loginForm(): View {
|
||||||
return new View( 'login' );
|
return new View( 'login' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function logoutPage(){
|
||||||
|
if( !Authentification::isLoggedIn() ) {
|
||||||
|
Request::redirectTo( 'home' );
|
||||||
|
}
|
||||||
|
|
||||||
|
Authentification::destroySession();
|
||||||
|
Request::redirectTo( 'home' );
|
||||||
|
}
|
||||||
|
|
||||||
public function auth(): JSONResponse {
|
public function auth(): JSONResponse {
|
||||||
|
|
||||||
Request::setCORS();
|
Request::setCORS();
|
||||||
@@ -39,14 +50,14 @@ class AuthentificationController extends Controller {
|
|||||||
|
|
||||||
$userId = 1;
|
$userId = 1;
|
||||||
Authentification::loginUser( $userId );
|
Authentification::loginUser( $userId );
|
||||||
JSONResponse::sendSuccess( [ 'user_id' => $userId ] );
|
return JSONResponse::sendSuccess( [ 'user_id' => $userId ] );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logout(): JSONResponse {
|
public function logout(): JSONResponse {
|
||||||
|
|
||||||
if( !Authentification::isLoggedIn() ) {
|
if( !Authentification::isLoggedIn() ) {
|
||||||
return JSONResponse::sendError( [ 'message' => 'Alrady disconnected' ] );
|
return JSONResponse::sendError( [ 'message' => 'Already disconnected' ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
Authentification::destroySession();
|
Authentification::destroySession();
|
||||||
|
|||||||
29
src/Helpers/Markdown.php
Normal file
29
src/Helpers/Markdown.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
class Markdown {
|
||||||
|
|
||||||
|
public static function getMarkdownEntities(): array {
|
||||||
|
return [
|
||||||
|
|
||||||
|
// Gras & Italique
|
||||||
|
'/\*\*(.*?)\*\*/' => '<b>$1</b>',
|
||||||
|
'/\*(.*?)\*/' => '<i>$1</i>',
|
||||||
|
|
||||||
|
// Titres
|
||||||
|
'/^## (.*?)$/m' => '<h3>$1</h3>',
|
||||||
|
'/^# (.*?)$/m' => '<h2>$1</h2>',
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function convertToHTML( string $markdown ): string {
|
||||||
|
$safeMD = htmlspecialchars( $markdown, ENT_QUOTES );
|
||||||
|
foreach( Markdown::getMarkdownEntities() as $key => $value ) {
|
||||||
|
$safeMD = preg_replace( $key, $value, $safeMD );
|
||||||
|
}
|
||||||
|
|
||||||
|
return nl2br( $safeMD );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,4 +50,20 @@ class Request {
|
|||||||
return self::sanitize( $_POST[$name] );
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
45
src/Infrastructure/Database.php
Normal file
45
src/Infrastructure/Database.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Infrastructure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de se connecter à la base de données et de faire le pont
|
||||||
|
* entre la classe PDO et nos actions.
|
||||||
|
*/
|
||||||
|
final class Database {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contient les informations de connexion à notre BDD.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private array $databaseInfos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance PDO.
|
||||||
|
* @var \PDO
|
||||||
|
*/
|
||||||
|
public private(set) \PDO $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructeur, lance la connexion.
|
||||||
|
* @param array $loginInfos ['name','host','port','user','pass']
|
||||||
|
*/
|
||||||
|
public function __construct( array $loginInfos ){
|
||||||
|
$this->databaseInfos = $loginInfos;
|
||||||
|
$this->tryLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'essayer un login à la base de données.
|
||||||
|
* @return void - never si la connexion échoue.
|
||||||
|
*/
|
||||||
|
private function tryLogin(): void {
|
||||||
|
try {
|
||||||
|
$dsn = 'mysql:dbname=' . $this->databaseInfos['name'] . ';host=' . $this->databaseInfos['host'] . ';port=' . $this->databaseInfos['port'];
|
||||||
|
$this->pdo = new \PDO( $dsn, $this->databaseInfos['user'], $this->databaseInfos['pass'] );
|
||||||
|
} catch ( \PDOException $e ) {
|
||||||
|
die( $e->getMessage() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ use App\Helpers\Authentification;
|
|||||||
use App\Helpers\AutoLoader;
|
use App\Helpers\AutoLoader;
|
||||||
use App\Helpers\ConfigFactory;
|
use App\Helpers\ConfigFactory;
|
||||||
use App\Http\Router;
|
use App\Http\Router;
|
||||||
|
use App\Infrastructure\Database;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classe primaire du site.
|
* Classe primaire du site.
|
||||||
@@ -22,6 +23,14 @@ final class Kernel {
|
|||||||
*/
|
*/
|
||||||
public private(set) static array $configs = [];
|
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.
|
* Instance actuelle de l'application.
|
||||||
* @var Kernel|null
|
* @var Kernel|null
|
||||||
@@ -62,6 +71,7 @@ final class Kernel {
|
|||||||
public function init(): void {
|
public function init(): void {
|
||||||
$this->buildAutoloader();
|
$this->buildAutoloader();
|
||||||
$this->loadConfig();
|
$this->loadConfig();
|
||||||
|
$this->loadDatabase();
|
||||||
|
|
||||||
Authentification::startSession();
|
Authentification::startSession();
|
||||||
|
|
||||||
@@ -89,6 +99,7 @@ final class Kernel {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
self::$configs['general'] = ConfigFactory::loadConfigFile('general');
|
self::$configs['general'] = ConfigFactory::loadConfigFile('general');
|
||||||
|
self::$configs['database'] = ConfigFactory::loadConfigFile('database');
|
||||||
self::$configs['route_arguments'] = ConfigFactory::loadConfigFile('route_arguments');
|
self::$configs['route_arguments'] = ConfigFactory::loadConfigFile('route_arguments');
|
||||||
self::$configs['views'] = ConfigFactory::loadConfigFile('views');
|
self::$configs['views'] = ConfigFactory::loadConfigFile('views');
|
||||||
|
|
||||||
@@ -97,4 +108,12 @@ final class Kernel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de se connecter à la base de données principale.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loadDatabase(): void {
|
||||||
|
self::$DB = new Database( self::$configs['database'] );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,2 +1,9 @@
|
|||||||
<h1>Coucou</h1>
|
<h1>Coucou</h1>
|
||||||
<?php echo \App\Http\Router::getRouteURL( 'test', 3 ); ?>
|
<?php $str = "Markdown **text**\n# SOLONG\n*text*."; $md = \App\Helpers\Markdown::convertToHTML( $str ); echo "<p>$md</p>"; ?>
|
||||||
|
|
||||||
|
<?php $rec = new \App\Domain\Recettes\RecetteRepository()->getByID( 4 );
|
||||||
|
$rec->temps_de_preparation = 7;
|
||||||
|
$rec->titre_recette = "Tutu";
|
||||||
|
|
||||||
|
new \App\Domain\Recettes\RecetteRepository()->delete( $rec );
|
||||||
|
?>
|
||||||
|
|||||||
23
views/login.php
Normal file
23
views/login.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php use App\Infrastructure\View as V; use App\Http\Router; ?>
|
||||||
|
|
||||||
|
<div class=formcont>
|
||||||
|
<form id='login-form' action='<?php echo Router::getRouteURL( 'api->auth' ); ?>' method='post'>
|
||||||
|
|
||||||
|
<div class='form-group'>
|
||||||
|
<label for='logmsg'>Se Connecter</label>
|
||||||
|
</div>
|
||||||
|
<div id="login-errors" class="form-error" style="display:none;color:red;">
|
||||||
|
</div>
|
||||||
|
<div class='form-group'>
|
||||||
|
<input type='text' class='form-control' name='username' id='username' value='' placeholder="Pseudo">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='form-group'>
|
||||||
|
<input type='text' class='form-control' name='password' id='password' value='' placeholder="Mot de Passe">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type='submit' class='btn btn-primary'>Confirmer</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<script src="<?php echo Router::getAssetURL( 'js/login.js' ); ?>" defer></script>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<link rel="stylesheet" href="<?php V::assetUrl( 'css/style.css' ); ?>" />
|
<link rel="stylesheet" href="<?php V::assetUrl( 'css/style.css' ); ?>" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header id="header">x
|
<header id="header">
|
||||||
<div id="logo">
|
<div id="logo">
|
||||||
<a href="index.php">
|
<a href="index.php">
|
||||||
<img src="<?php V::assetUrl( 'images/Logo.jpg'); ?>" class="logo">
|
<img src="<?php V::assetUrl( 'images/Logo.jpg'); ?>" class="logo">
|
||||||
@@ -18,6 +18,12 @@
|
|||||||
<ul class="nav-list">
|
<ul class="nav-list">
|
||||||
<li><a id="google" class="nav-element" href="google.com">Google</a></li>
|
<li><a id="google" class="nav-element" href="google.com">Google</a></li>
|
||||||
<li><a id="unTest" class="nav-element" href="bing.com">Bing</a></li>
|
<li><a id="unTest" class="nav-element" href="bing.com">Bing</a></li>
|
||||||
|
<?php if( \App\Helpers\Authentification::isLoggedIn() ): ?>
|
||||||
|
<li><a id="login" class= "nav-element" href="<?php V::routeUrl( 'logout'); ?>">Logout</a></li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li><a id="login" class= "nav-element" href="<?php V::routeUrl( 'login'); ?>">Login</a></li>
|
||||||
|
<?php endif; ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
<input type="text" class="search-form search-form-recette" placeholder="Rechercher une recette ...">
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
Reference in New Issue
Block a user