From ae3164483166596a28dd6a4e4bacb899a7022749 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 3 Apr 2026 12:55:05 +0200 Subject: [PATCH] Fix logins and add routes. --- public/assets/css/style.css | 0 public/assets/images/Logo.jpg | Bin public/assets/js/advanced-search.js | 11 +- public/assets/js/login.js | 0 public/index.php | 0 public/uploads/.gitignore | 0 src/Domain/Ingredients/Ingredient.php | 6 + .../Ingredients/IngredientRepository.php | 2 +- .../Ingredients/IngredientsAPIController.php | 70 +++++ src/Domain/Recettes/Recette.php | 11 + src/Domain/Recettes/RecetteRepository.php | 2 +- src/Domain/Recettes/RecettesAPIController.php | 253 +++++++++++++++++- src/Domain/Tags/TagsAPIController.php | 72 +++++ src/Helpers/UploadFiles.php | 51 ++++ views/partials/header.php | 2 +- 15 files changed, 470 insertions(+), 10 deletions(-) mode change 100644 => 100755 public/assets/css/style.css mode change 100644 => 100755 public/assets/images/Logo.jpg mode change 100644 => 100755 public/assets/js/advanced-search.js mode change 100644 => 100755 public/assets/js/login.js mode change 100644 => 100755 public/index.php create mode 100644 public/uploads/.gitignore create mode 100644 src/Domain/Ingredients/IngredientsAPIController.php create mode 100644 src/Domain/Tags/TagsAPIController.php create mode 100644 src/Helpers/UploadFiles.php diff --git a/public/assets/css/style.css b/public/assets/css/style.css old mode 100644 new mode 100755 diff --git a/public/assets/images/Logo.jpg b/public/assets/images/Logo.jpg old mode 100644 new mode 100755 diff --git a/public/assets/js/advanced-search.js b/public/assets/js/advanced-search.js old mode 100644 new mode 100755 index 519619b..bc433f2 --- a/public/assets/js/advanced-search.js +++ b/public/assets/js/advanced-search.js @@ -12,13 +12,13 @@ document.addEventListener('DOMContentLoaded', function(){ document.getElementById( "recetteList" ).innerHTML = ''; for( const [key, recette] of Object.entries( data ) ) { - let HTML_CONTENT = ` - + let HTML_CONTENT = ` +

${recette.titre_recette}

  • Temps de préparation : ${recette.temps_de_preparation}
  • -
  • Nombre d'ingrédients : data.nb_ing
  • +
  • Nombre d'ingrédients : ${recette.nb_ingredients}
` @@ -95,10 +95,9 @@ document.addEventListener('DOMContentLoaded', function(){ } const SEARCHBAR = document.getElementById( 'searchForm' ); - searchInput = new URLSearchParams(document.location.search).get("s"); - console.log( searchInput ); + searchInput = new URLSearchParams(document.location.search).get("s") || searchInput; - if( searchInput !== "" ) { + if( searchInput !== undefined && searchInput != "" ) { advancedSearch(); } diff --git a/public/assets/js/login.js b/public/assets/js/login.js old mode 100644 new mode 100755 diff --git a/public/index.php b/public/index.php old mode 100644 new mode 100755 diff --git a/public/uploads/.gitignore b/public/uploads/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Domain/Ingredients/Ingredient.php b/src/Domain/Ingredients/Ingredient.php index 8a965db..3469a6f 100644 --- a/src/Domain/Ingredients/Ingredient.php +++ b/src/Domain/Ingredients/Ingredient.php @@ -21,6 +21,12 @@ class Ingredient extends Model { */ 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 diff --git a/src/Domain/Ingredients/IngredientRepository.php b/src/Domain/Ingredients/IngredientRepository.php index daca14c..234732d 100644 --- a/src/Domain/Ingredients/IngredientRepository.php +++ b/src/Domain/Ingredients/IngredientRepository.php @@ -35,7 +35,7 @@ class IngredientRepository extends Repository implements LinkableInterface { return [ 'table' => 'Ingredient', 'columns' => [ - 'num_ingredient', 'nom_ingredient' + 'num_ingredient', 'nom_ingredient', 'photo_ingredient', ], 'link_recettes' => 'Listeingredient' ]; diff --git a/src/Domain/Ingredients/IngredientsAPIController.php b/src/Domain/Ingredients/IngredientsAPIController.php new file mode 100644 index 0000000..1e3b59d --- /dev/null +++ b/src/Domain/Ingredients/IngredientsAPIController.php @@ -0,0 +1,70 @@ +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( [] ); + } +} \ No newline at end of file diff --git a/src/Domain/Recettes/Recette.php b/src/Domain/Recettes/Recette.php index 6073250..3f16eac 100644 --- a/src/Domain/Recettes/Recette.php +++ b/src/Domain/Recettes/Recette.php @@ -6,11 +6,13 @@ 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; @@ -43,6 +45,15 @@ class Recette extends Model { 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(); diff --git a/src/Domain/Recettes/RecetteRepository.php b/src/Domain/Recettes/RecetteRepository.php index 0075f6a..552f16a 100644 --- a/src/Domain/Recettes/RecetteRepository.php +++ b/src/Domain/Recettes/RecetteRepository.php @@ -90,7 +90,7 @@ class RecetteRepository extends Repository implements UseIngredientsInterface, U * @return Recette|null */ public function getBySlug( string $slug ): ?Recette { - $sqlQuery = "SELECT * FROM {$this->tableName} WHERE slug = {$slug}"; + $sqlQuery = "SELECT * FROM {$this->tableName} WHERE slug = '{$slug}'"; $results = $this->selectGetAll($sqlQuery); if( $results === null || count( $results ) > 1 ) return null; diff --git a/src/Domain/Recettes/RecettesAPIController.php b/src/Domain/Recettes/RecettesAPIController.php index a2e3440..7811cf2 100644 --- a/src/Domain/Recettes/RecettesAPIController.php +++ b/src/Domain/Recettes/RecettesAPIController.php @@ -3,8 +3,12 @@ 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 { @@ -12,6 +16,9 @@ class RecettesAPIController extends Controller { { 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'] ), ]; } @@ -35,9 +42,253 @@ class RecettesAPIController extends Controller { $resp = $recetteRepo->advancedRecetteSearch( $title, $tagsId, $ingredientsId ); $resp = array_map( function($recette) use ($recetteRepo){ - return $recetteRepo->getByID( $recette['num_recette'] ); + $r = $recetteRepo->getByID( $recette['num_recette'] ); + $r->url = Router::getRouteURL( 'recettes->show', $r->slug ); + $r->nb_ingredients = $r->getNumberOfIngredients(); + return $r; }, $resp ?? [] ); JSONResponse::sendSuccess( [ 'data' => $resp ] ); } + + public function create(){ + + // Récupération des champs. + $name = Request::post( 'nom' ) ?? ""; + $temps = Request::post( 'temps' ) ?? ""; + $fileField = "image"; + + $tagsId = explode( ",", Request::post( 'tag' ) ) ?? []; + $ingredientsId = explode( ",", Request::post( 'ingr' ) ) ?? []; + + 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" ] ); + } + + // 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 ); + $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( 'tag' ) ) ?? []; + $ingredientsId = explode( ",", Request::post( 'ingr' ) ) ?? []; + + 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 ); + $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(); + + } } \ No newline at end of file diff --git a/src/Domain/Tags/TagsAPIController.php b/src/Domain/Tags/TagsAPIController.php new file mode 100644 index 0000000..95f5402 --- /dev/null +++ b/src/Domain/Tags/TagsAPIController.php @@ -0,0 +1,72 @@ +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(); + + } +} \ No newline at end of file diff --git a/src/Helpers/UploadFiles.php b/src/Helpers/UploadFiles.php new file mode 100644 index 0000000..658fe48 --- /dev/null +++ b/src/Helpers/UploadFiles.php @@ -0,0 +1,51 @@ +
- +