Bonjour,

Voila je rencontre un petit problème avec mon code et je vais essayer d'être le plus clair possible.
J'avais commencé un bout d'application web pour la gestion de mes ingrédients et mes recettes ... et je me suis décidé à visionner la formation PHP POO, ce que j'ai fait plusieurs fois.
Maintenant, j'essais de mettre certaines choses en application en re-visonnant à nouveau !

Ce que je fais

J'ai donc pu réaliser ma partie 'Ingrédients' avec la visualisation de tous mes ingrédients dans un tableau et la possibilité d'ajouter, éditer ou supprimer. Tout fonctionne.

J'y vais lentement, mais sûrement.
Je me penche maintenant sur la partie 'Création d'une recette'.
J'ai cherché comment j'allais présenter visuellement cette page, et sans avoir de réelles idées, je vais sans doute partir sur du simple, comme sur l'image je pense !

Donc, en haut de ma page 'recette.add.php', je pensais mettre ceci :

<?php
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
  $result = $recetteTable->create([
    'categories_id' => $_POST['categories_id'],
    'recettes' => $_POST['recettes'],
    'nbpersonnes' => $_POST['nbpersonnes'],
    'marge' => $_POST['marge'],
    'observations' => $_POST['observations']
  ]);
  if ($result) {
    header('Location: index.php?p=recette.add&id=' . App::getInstance()->getDb()->lastInsertId());
  }
}
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$familles = App::getInstance()->getTable('Famille')->extract('id', 'familles');
$form = new \Core\HTML\BootstrapForm($_POST);
?>

Ce code fonctionne ... que pour l'enregistrement des informations qui sont dans la partie gauche de mon image. Et ces données s'enregistrent bien dans ma table 'Recettes'.

Seulement, mes tables sont jointes comme ceci :

Ce que je veux

Les tables 'Ingredients' et 'Recettes' sont jointes avec 'Composition_Recettes'.
J'imagine donc qu'il me faut la table 'Composition_Recettes' pour pouvoir enregistrer la liste des ingrédients nécessaires à la recette,
ce qui correspont à ma partie droite de ma première image.

Donc voilà, avant de me lancer dans le tri des ingrédients avec le champs 'Familles' que vous pouvez voir, je souhaitais faire un test en faisant un enregistrement avec un ingrédient pour voir le comportement.

Je bloque à ce niveau, comment faire pour réussir à renregistrer
tous les élements au bon endroit, dans les bonnes tables ?

Merci d'avance !!!

172 réponses


Bonjour,

je ne comprends pas trop le problème que tu rencontres. Dans la même transaction tu commences par enregistrer la recette. Tu obtiens alors sa clé primaire. Il suffit ensuite d'enregistrer dans ta table d'association recette-ingrédient la liste des ingrédients fournis dans le formulaire (tu dois avoir l'id).

Dans le cas où cette page permet également de créer des ingrédients, c'est plus compliqué, il faudra d'abord sauvegarder les ingrédients avant de sauvegarder l'association (mais attention aux créations identiques en parallèles)

Dernière chose, au vue de la structure de tes tables, comment vas-tu gérer les accès concurrents? : Toto charge la recette A, Tata modifie la recette A, Toto enregistre ses modifications sur A , qui l'emporte?

Zulkar
Auteur

Bonjour,
Qui l'emporte ? Bahhh ... moi :) ... C'est une appli pesonnelle, pour un usage personnel et en local sur mon pc portable !
Pour les ingrédients, c'était la première partie de l'appli, la création, l'édition et la suppression fonctionne de ce côté là.

Pour la question que tu posais, toi tu ne vois pas de problème car tu es développeur :) ... mais pour moi qui suis cuisinier, comprendre le cheminement est une autre, mais le mette en application avec une retranscription en code, ce n'est pas si évident !

Pour le sujet que j'ai ouvert, la partie ajout d'une recette fonctionne de cette façon :

<?php

$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
    $result = $recetteTable->create([
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    $recette_id = App::getInstance()->getDb()->lastInsertId();
    $compositionTable = App::getInstance()->getTable('CompositionRecette');
    $id_composition = [];
    foreach ($_POST['ingredients_id'] as $key => $ingredient_id) {
        $quantite = $_POST['quantite'][$key];
        $compositionTable->create([
            'recettes_id' => $recette_id,
            'ingredients_id' => $ingredient_id,
            'quantites' => $quantite
        ]);

        $id_composition[] = App::getInstance()->getDb()->lastInsertId();
    }

    if ($recette_id && count($id_composition)) {
        //redirection
    }
}
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$ingredients = App::getInstance()->getTable('Ingredient')->extract('id', 'ingredients');
$form = new \Core\HTML\BootstrapForm($_POST);
?>

Maintenant, je passe à la partie Edition, et là, je n'arrive à faire que la moitié, j'ai plusieurs choses à voir !!

Bonsoir,

Pour pouvoir mieux t'aider, je pense qu'il faudrait que tu nous montres le code de ton formulaire, il se peut que tu n'ai pas utilisé de nom de champs avec des [] pour indiquer que tu passer un tableau d'id d'ingrédients et un tableau de quantité.

Je peux me permettre un conseil en passant ?
Je suppose que ton schéma de base de données a changé par rapport à ton image ici à la la lecture de ton code.

Je te suggère les modifications suivantes :

  • table 'recettes', champ 'recettes' à renommer en nom, tu pourras écrire $recette->nom à la place de $recette->recette, tu t'y retrouveras mieux pareil pour les tables 'familles', 'ingredient', 'categories' ...
  • table 'recettes' toujours en renommant le champ categories_id en categorie_id, tu feras référence à l'id de la catégorie de ta recette, une recette appartenant à une seule catégorie selon ton schéma
  • table 'composition_recettes' devrait s'appeler 'ingredients_recettes' par convention avec des clés au singulier
  • table 'ingredient', j'aurais sorti le fournisseur et le prix comme tu as commencé à faire avec les familles (2 fournisseurs peuvent de faire 2 prix pour un même produit)
  • enfin pour des raisons de sécurité (même pour un usage personnel, il n'est pas inutile de se former avec des bonnes pratiques), on ne fait JAMAIS confiance aux données fournies par l'utilisateur, tu peux certainement convertir des $_POST... en nombre entier là où tu t'attends à voir des nombres entiers.

Sinon pour re-répondre à ton problème, je suppose qu'il se trouve dans le code de ton formulaire.

Zulkar
Auteur

Bonsoir et merci pour tous ces conseils.
Je ne sais pas par où commencer ... pour te répondre.
Pour répondre à ta supposition, non, mon schéma de base de données n'a pas changé depuis le début du projet.
Et voici le code complet de ma page ADD, je pensais partir de cette page pour créer la page EDIT.
Pour la partie EDIT la table Recettes, j'y arrive, mais quant aux ingrédients et aux quantités, ça c'est une autre histoire.

<?php

$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
    $result = $recetteTable->create([
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    $recette_id = App::getInstance()->getDb()->lastInsertId();
    $compositionTable = App::getInstance()->getTable('CompositionRecette');
    $id_composition = [];
    foreach ($_POST['ingredients_id'] as $key => $ingredient_id) {
        $quantite = $_POST['quantite'][$key];
        $compositionTable->create([
            'recettes_id' => $recette_id,
            'ingredients_id' => $ingredient_id,
            'quantites' => $quantite
        ]);

        $id_composition[] = App::getInstance()->getDb()->lastInsertId();
    }

    if ($recette_id && count($id_composition)) {
        //redirection
    }
}
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$ingredients = App::getInstance()->getTable('Ingredient')->extract('id', 'ingredients');
$form = new \Core\HTML\BootstrapForm($_POST);
?>

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        Recettes
        <small>Control Panel</small>
      </h1>
      <ol class="breadcrumb">
        <li><a href="index.php?p=home"><i class="fa fa-dashboard"></i> Home</a></li>
        <li class="active"><a href="index.php?p=ingredients">Recettes</a></li>
        <li class="active">Ajouter</li>
      </ol>
    </section>

    <!-- Main content -->
    <section class="content">

    <div class="row"> 

      <form method="POST"> 

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Création de la recette</h3>
            </div>
            <div class="box-body">

              <!-- Catégories -->
              <?= $form->select('categories_id', 'Catégorie', $categories); ?>
              <!-- /.form group -->

              <!-- Nom de la recette -->
              <?= $form->input('recettes', 'Choisir le nom de la recette'); ?>
              <!-- /.form group -->

              <!-- Nombre de personnes -->
              <?= $form->input('nbpersonnes', 'Nombre de personnes'); ?>
              <!-- /.form group -->

              <!-- Marge -->
              <?= $form->input('marge', 'Marge'); ?>
              <!-- /.form group -->

              <!-- Observation -->
              <?= $form->input('observations', 'Observations', ['type'=>'textarea']); ?>
              <!-- /.form group -->

              <button id="add" class="btn btn-block submit">Valider</button>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Sélectionner les ingrédients</h3>
            </div>
            <div class="box-body">
            <div data-role="dynamic-fields">
              <div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                  <?= $form->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <?= $form->input('quantite[]', 'Quantité'); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-2">
                  <label>Action</label>
                  <button class="btn btn-default remove" data-role="remove"> <span class="glyphicon glyphicon-remove"></span></button>
                  <button class="btn btn-default" data-role="add"> <span class="glyphicon glyphicon-plus"></span></button>
                </div>
              </div>
            </div>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

      </form>
      <!-- /.form -->

    </div>
    <!-- /.row -->

    </section>
    <!-- /.content -->

  </div>
  <!-- /.content-wrapper -->

Pour les modifications que tu me demandes, je vais y regarder ... MERCI !!!

Attention, je ne demande rien moi, je te conseille simplement ! ^^

Là ou je pense que ton schéma a changé, c'est quand je lis :

    $result = $recetteTable->create([
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

Dans la table 'recettes', il te manque les champs 'marge' et 'observations' donc ton script devrait échouer et ne pas aller plus loin je pense.

Il faudrait que tu regardes le code html généré par la class de formulaire et vérifier que tu as bien 'ingredients_id[]' et 'quantite[]' dans le code source de ta page, je pense que c'est ça qui doit bloquer aussi.

Tu peux également vérifier les champs transmis lors de la soumission du formulaire en modifiant ton script comme ceci :

' Pour débogage
echo '<pre>' . print_r($_POST, true) . '</pre>'; die();
$recetteTable = App::getInstance()->getTable('Recette');
...

Tu commentes la ligne 'echo ...' quand tu veux exécuter ton script et la décommente quand tu veux vérifier les champs transmis...

Zulkar
Auteur

Ah oui, bien vue ^^, j'ai en effet ajouté les champs 'marge' et 'observations' :)

Le code HTML généré pour la page EDIT, sur laquelle je souhaite travailler et qui fournit donc une erreur :


  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        Recettes
        <small>Control Panel</small>
      </h1>
      <ol class="breadcrumb">
        <li><a href="index.php?p=home"><i class="fa fa-dashboard"></i> Home</a></li>
        <li class="active"><a href="index.php?p=ingredients">Recettes</a></li>
        <li class="active">Editer</li>
      </ol>
    </section>

    <!-- Main content -->
    <section class="content">

    <div class="row"> 

      <form method="POST"> 

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Mise à jour de la recette</h3>
            </div>
            <div class="box-body">

              <!-- Catégories -->
              <div class="form-group"><label>Catégorie</label><select class="form-control" name="categories_id"><option value='1'>Apéritifs</option><option value='2'>Entrées</option><option value='3'>Plats</option><option value='4'>Desserts</option><option value='5' selected>Sauces</option><option value='6'>Divers</option><option value='7'>Cake Design</option></select></div>              <!-- /.form group -->

              <!-- Nom de la recette -->
              <div class="form-group"><label>Choisir le nom de la recette</label><input type="text" name="recettes" value="Crème de champignons" class="form-control"></div>              <!-- /.form group -->

              <!-- Nombre de personnes -->
              <div class="form-group"><label>Nombre de personnes</label><input type="text" name="nbpersonnes" value="4" class="form-control"></div>              <!-- /.form group -->

              <!-- Marge -->
              <div class="form-group"><label>Marge</label><input type="text" name="marge" value="4.5" class="form-control"></div>              <!-- /.form group -->

              <!-- Observation -->
              <div class="form-group"><label>Observations</label><textarea name="observations" class="form-control">Test observations crème de champignons</textarea></div>              <!-- /.form group -->

              <button id="add" class="btn btn-block submit">Sauvegarder</button>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Sélectionner les ingrédients</h3>
            </div>
            <div class="box-body">
            <div data-role="dynamic-fields">
              <div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                  <br />
<font size='1'><table class='xdebug-error xe-uncaught-exception' dir='ltr' border='1' cellspacing='0' cellpadding='1'>
<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> Fatal error: Uncaught Error: Call to undefined method App\Entity\RecetteEntity::getIngredients_id[]() in /srv/http/Dev/Dashboard/core/Entity/Entity.php on line <i>13</i></th></tr>
<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> Error: Call to undefined method App\Entity\RecetteEntity::getIngredients_id[]() in /srv/http/Dev/Dashboard/core/Entity/Entity.php on line <i>13</i></th></tr>
<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>
<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>
<tr><td bgcolor='#eeeeec' align='center'>1</td><td bgcolor='#eeeeec' align='center'>0.0004</td><td bgcolor='#eeeeec' align='right'>370664</td><td bgcolor='#eeeeec'>{main}(  )</td><td title='/srv/http/Dev/Dashboard/public/index.php' bgcolor='#eeeeec'>.../index.php<b>:</b>0</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>2</td><td bgcolor='#eeeeec' align='center'>0.0011</td><td bgcolor='#eeeeec' align='right'>421552</td><td bgcolor='#eeeeec'>require( <font color='#00bb00'>'/srv/http/Dev/Dashboard/pages/recettes/recette.edit.php'</font> )</td><td title='/srv/http/Dev/Dashboard/public/index.php' bgcolor='#eeeeec'>.../index.php<b>:</b>31</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>3</td><td bgcolor='#eeeeec' align='center'>0.0064</td><td bgcolor='#eeeeec' align='right'>496384</td><td bgcolor='#eeeeec'>Core\HTML\BootstrapForm->select(  )</td><td title='/srv/http/Dev/Dashboard/pages/recettes/recette.edit.php' bgcolor='#eeeeec'>.../recette.edit.php<b>:</b>109</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>4</td><td bgcolor='#eeeeec' align='center'>0.0064</td><td bgcolor='#eeeeec' align='right'>496520</td><td bgcolor='#eeeeec'>Core\HTML\BootstrapForm->getValue(  )</td><td title='/srv/http/Dev/Dashboard/core/HTML/BootstrapForm.php' bgcolor='#eeeeec'>.../BootstrapForm.php<b>:</b>35</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>5</td><td bgcolor='#eeeeec' align='center'>0.0064</td><td bgcolor='#eeeeec' align='right'>496520</td><td bgcolor='#eeeeec'>App\Entity\RecetteEntity->__get(  )</td><td title='/srv/http/Dev/Dashboard/core/HTML/Form.php' bgcolor='#eeeeec'>.../Form.php<b>:</b>41</td></tr>
</table></font>
Zulkar
Auteur

Et ton code pour le débogage me renvoie un Array vide

Tu as un problème à cette ligne :

<?= $form->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>

Supprime la ainsi que la ligne pour la catégorie et teste pour voir, ton problème vient de là. La classe form n'est pas adaptée pour générer le code html de plusieurs champs select avec le même identifiant.

Autrement dit, si tu modifies ton code comme ceci :

<?= $form->select('ingredients_id', 'Ingrédient', $ingredients); ?>

ça va fonctionner mais tu n'auras qu'un seul ingrédient, il te faut modifier la classe form pour l'adapter à ce que tu veux faire.

Peux tu me donner l'url du chapitre de la formation dans lequel la classe est modifiée pour gérer le select s'il te plait ?

J'ai trouvé l'url https://www.grafikart.fr/formations/programmation-objet-php/tp-backend à partir de 37min, je la regarde et on voit ça demain en fin d'après-midi...

Zulkar
Auteur

La class form a été créée dans ce chapitre :
https://www.grafikart.fr/formations/programmation-objet-php/exemple-class-form
Il y a aussi des modifications dans le chapitre TP: Back-end :
https://www.grafikart.fr/formations/programmation-objet-php/tp-backend

J'ai bien vu que dans le cas de l'édition, les [ ] posaient problème et en les enlevant, le problème était toujours le même.

Si je retire les lignes ingredients_id[] et quantite[] la mise à jour fonctionne pour les champs catégories_id, recettes, nbpersonnes, marge, observations.

Zulkar
Auteur

OK pour demain en fin d'après-midi, ça m'arrange car c'est mon après-midi en soins hospitaliers ...

Le problème vient bien de la class form qui ne gère pas les identifiants avec [] pour le moment, on va corriger ça prochainement...

En attendant, tu peux modifier dans la fonction select de la classe BootstrapFrom.php la ligne '$input...' comme ceci :

$input = '<select> class="form-control" name='. $name .'[]">';

pour contourner le problème mais c'est une solution temporaire plutôt dégueulasse ^^

Zulkar
Auteur

Hello,
Un SELECT mais des options différentes !!!
L'explication pour mon cas ?

Je n'ai pas compris ta question.

Zulkar
Auteur

Le lien que tu donnes, c'est pour mon problème d'édition des ingrédients et des quantités ?
Donc je me demandais qu'elle action ça aurait pour mon cas.

Dans les réponses données sur la page dont je t'ai indiqué le lien, il est fait mention de plusieurs selects avec des [] et un nom identique (cars[]) , c'est le même principe que tu devrais utiliser pour tes ingrédients et quantités.

Zulkar
Auteur

Pour faire le tri par Familles ?
Mais pour les quantités, je ne vois pas !!!

Tu rajoutes bien un certain nombre d'ingrédients d'une certaine famille avec ton bouton + dans ta colonne action ?

Zulkar
Auteur

Oui, tout à fait !
Et je me disais même qu'il serait bien de filtrer les ingrédients par familles, sinon je vais me retrouver avec un SELECT mega long !

Je me suis mal exprimé. Est-ce que tu cherches à ajouter les ingrédients un par un en soumettant le formulaire à chaque fois ou quand tu appuies sur le bouton +, tu as de nouveaux champs select et quantité ?

Je te propose que l'on discute de tout ça en live sur le tchat.

Zulkar
Auteur

Ah ok excuses, je suis à l'ouest ... lol ... je préfère ajouter un ingrédient, sa quantité ... cliquer sur + pour ajouter de nouveaux champs pour ajouter un nouvel ingrédient et sa quantité ... ainsi de suite !!

Dans ce cas, c'est encore plus simple, tu oublies les [] et tu gères ça avec une requête ajax quand tu appuies sur le +

Je pense cependant que tu devrais envisager de modifier un peu ta façon de faire et faire comme ce que tu fais déjà avec tes ingrédients (ajout, édition, suppression) mais avec tes recettes et ne gérer les ingrédients que lorsque tu édites une recette.

Tu créés une nouvelle recette, tu peux pas ajouter d'ingrédient.
Tu édites ta nouvelle recette, et là seulement tu peux ajouter/supprimer tes ingrédients (tu t'arrages pour mettre l'id de ta recette dans le formulaire).
Ce sera certainement plus facile à coder.

Zulkar
Auteur

J'avais déjà réfléchi à cette solution en effet.

Donc aujourd'hui, j'ai mon fichier recette.add.php qui enregistre bien le champ catégorie, recette, nombre de personnes, marge, observation, ingrédient, quantité, le bouton + ajoute une nouvelle ligne pour ajouter un ingrédient, une quantité, etc ... ou supprimer la ligne précédente ...

Avec ta suggestion, j'enlève la partie 'Sélectionner les ingrédients' pour enregistrer seulement le reste.

Donc les ingrédients et les quantités, je les enregistre comment ?
Et lorsque je vais éditer une recette, je vais bien récupérer tous les champs de la table recette + les ingrédients et les quantités ?
Donc le problème de l'édition sera le même ?

Je me trompe peut-être !!!!

Pour l'ajout et la suppression d'une recette, je te conseille de faire aussi simplement que ce que tu sembles avoir fait avec les ingrédients (c'est ce à quoi tu fais référence quand tu parles du tableau dans le premier message de ton sujet).

C'est seulement quand tu édites une recette que tu peux mettre tes ingrédients en relation avec ta recette (et leurs quantités), ajouter/supprimer un ingrédient de ta recette pas de ta base de données.

Je ne sais pas si je suis très clair.

Zulkar
Auteur

Pour mes ingrédients, j'ai fait comme ceci, je récupère tout :

Pour ajouter un ingrédient :

Pour éditer un ingrédient :

Fais la même chose pour tes recettes sans gérer les ingrédients rattachés à tes recettes dans un premier temps, ajoutes la gestion des ingrédients uniquement quand tu modifies tes recttes.

Qu'en penses tu ?

Zulkar
Auteur

Oui, c'est faisable ...j'enlève juste le code php correspondant à cette partie, ça prend pas longtemps !
C'est juste dommage car j'avais réussi à ajouter les ingrédients à cette page lol :D

Mais c'est surtout que je ne comprends pas la différence entre le problème que j'ai pour l'édition en ce moment et le mode édition que tu veux me faire faire.

Et il faut donc que je change mon visuel pour mes recettes alors ?

Je pense que ton problème vient du fait que tu veuilles créer une nouvelle recette et y associer des ingrédients en une seule étape.

Tu auras sans doute également besoin parfois de modifer les ingrédients de tes recettes ou leurs quantités, c'est pour cela que je te conseille de gérer tout ça uniquement quand tu modifies tes recettes.
Du coup tu n'as besoin de traiter le cas si la recette est nouvelle ou si tu la modifies, ton code est plus simple à écrire.

Après, ça reste tes choix et ton code.

Zulkar
Auteur

D'accord !

Dans le visuel/design, tu vois ça comment ?

Le tiens est épuré, priorité au fonctionnel, c'est plutôt classe.

Quelque chose te chagrine ?

Zulkar
Auteur

Merci !!!

Donc, du coup, pour le mode édition !
Je récupère l'id de la recette, tous les champs pré-remplis et pour les ingrédients et les quantités, je ne procède plus comme avant avec le bouton + ?

Si tu fais comme avant et comme tu l'as indiqué.

As tu tenté la modification de la classe BootstrapForm comme je te l'ai conseillé pour gérer les [] ?

Zulkar
Auteur

Tu avais conseillé :

$input = '<select> class="form-control" name='. $name .'[]">';

Ce n'est pas plutôt :

$input = '<select class="form-control" name="' . $name . '[]">';

Bonjour,
Oui en effet, j'ai fait une faute de frappe...

Zulkar
Auteur

Bonjour,
Pour récap. , j'ai donc un fichier recette.add.php avec simplement les champs correspondant à ma table Recette.
Lorsque j'édite la recette, je récupère les champs pré-remplis et en plus, j'ai les champs pour ajouter les ingrédients et les quantités.
Donc dans cette page, j'ai 2 fonctions, à la fois UPDATE pour la partie recette et CREATE pour les ingrédients !

Et si je souhaite ré-éditer la recette, j'en reviens à mon problème initiale, c'est-à-dire récupérer tous les champs pré-remplis y compris les ingrédients et les quantités ? :D

Peux tu me montrer ton nouveau code et capture d'écran s'il te plait ?

Zulkar
Auteur

Donc, pour la partie Ajouter, je n'ai plus la partie de droite avec les ingrédients et les quantités :

<?php
// Connexion à la table
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
    // INSERT des données saisies
    $result = $recetteTable->create([
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    if ($result) {
        // Redirection et/ou Message
    }
}
// Connexion aux tables pour remplir les SELECT
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$ingredients = App::getInstance()->getTable('Ingredient')->extract('id', 'ingredients');
// Chargement des paramètres du formulaire
$form = new \Core\HTML\BootstrapForm($_POST);
?>

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        Recettes
        <small>Control Panel</small>
      </h1>
      <ol class="breadcrumb">
        <li><a href="index.php?p=home"><i class="fa fa-dashboard"></i> Home</a></li>
        <li class="active"><a href="index.php?p=ingredients">Recettes</a></li>
        <li class="active">Ajouter</li>
      </ol>
    </section>

    <!-- Main content -->
    <section class="content">

    <div class="row"> 

      <form method="POST"> 

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Création de la recette</h3>
            </div>
            <div class="box-body">

              <!-- Catégories -->
              <?= $form->select('categories_id', 'Catégorie', $categories); ?>
              <!-- /.form group -->

              <!-- Nom de la recette -->
              <?= $form->input('recettes', 'Choisir le nom de la recette'); ?>
              <!-- /.form group -->

              <!-- Nombre de personnes -->
              <?= $form->input('nbpersonnes', 'Nombre de personnes'); ?>
              <!-- /.form group -->

              <!-- Marge -->
              <?= $form->input('marge', 'Marge'); ?>
              <!-- /.form group -->

              <!-- Observation -->
              <?= $form->input('observations', 'Observations', ['type'=>'textarea']); ?>
              <!-- /.form group -->

              <button id="add" class="btn btn-block submit">Valider</button>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

      </form>
      <!-- /.form -->

    </div>
    <!-- /.row -->

    </section>
    <!-- /.content -->

  </div>
  <!-- /.content-wrapper -->

Pour la partie Edition, je ne peux pas te montrer le visuel car il récupère bien les champs pré-remplis de la partie gauche Recette, mais j'ai une erreur pour la partie ingrédients et quantités.
Il n'aime pas :

<?= $form->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>

Le code complet de la page est :

<?php
//Pour débogage
//echo '<pre>' . print_r($_POST, true) . '</pre>'; die();

// Connexion à la table
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
    // UPDATE des données récupérées
    $result = $recetteTable->update($_GET['id'], [
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    // Trouver le code pour INSERT et UPDATE des ingrédients et quantités

    if ($result) {
        // Redirection et/ou Message
    }
}
// On récupère l'id de la recette
$recette = $recetteTable->find($_GET['id']);
// Connexion aux tables pour remplir les SELECT
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$ingredients = App::getInstance()->getTable('Ingredient')->extract('id', 'ingredients');
// On reprend le formulaire de la recette
$form = new \Core\HTML\BootstrapForm($recette);
?>

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        Recettes
        <small>Control Panel</small>
      </h1>
      <ol class="breadcrumb">
        <li><a href="index.php?p=home"><i class="fa fa-dashboard"></i> Home</a></li>
        <li class="active"><a href="index.php?p=ingredients">Recettes</a></li>
        <li class="active">Editer</li>
      </ol>
    </section>

    <!-- Main content -->
    <section class="content">

    <div class="row"> 

      <form method="POST"> 

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Mise à jour de la recette</h3>
            </div>
            <div class="box-body">

              <!-- Catégories -->
              <?= $form->select('categories_id', 'Catégorie', $categories); ?>
              <!-- /.form group -->

              <!-- Nom de la recette -->
              <?= $form->input('recettes', 'Choisir le nom de la recette'); ?>
              <!-- /.form group -->

              <!-- Nombre de personnes -->
              <?= $form->input('nbpersonnes', 'Nombre de personnes'); ?>
              <!-- /.form group -->

              <!-- Marge -->
              <?= $form->input('marge', 'Marge'); ?>
              <!-- /.form group -->

              <!-- Observation -->
              <?= $form->input('observations', 'Observations', ['type'=>'textarea']); ?>
              <!-- /.form group -->

              <button id="add" class="btn btn-block submit">Sauvegarder</button>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Sélectionner les ingrédients</h3>
            </div>
            <div class="box-body">
            <div data-role="dynamic-fields">
              <div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                  <?= $form->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <?= $form->input('quantite[]', 'Quantité'); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-2">
                  <label>Action</label>
                  <button class="btn btn-default remove" data-role="remove"> <span class="glyphicon glyphicon-remove"></span></button>
                  <button class="btn btn-default" data-role="add"> <span class="glyphicon glyphicon-plus"></span></button>
                </div>
              </div>
            </div>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

      </form>
      <!-- /.form -->

    </div>
    <!-- /.row -->

    </section>
    <!-- /.content -->

  </div>
  <!-- /.content-wrapper -->

Je vois que tu as un champ input avec des [] également.

Je te propose la chose suivante, vu que tu connais parfaitement ton code, n'utilise pas (pour le moment) la classe BootstapFrom chaque fois que tu utilises des [] dans des champs, tu as donc ces lignes à modifier pour les remplacer par le code html attendu :

<?= $form->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>

et

<?= $form->input('quantite[]', 'Quantité'); ?>

Et tu testes avec la ligne de debogage décommentée, normalement tu devrais avoir toutes tes données dans la variable $_POST.

En attendant, je te cherche des liens pour t'expliqer la suite...

Avant de lire la suite, recopies plusieurs fois, ce bloc modifié (code html en dur et non généré par la classe) :

              <div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                  <?= $form->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <?= $form->input('quantite[]', 'Quantité'); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-2">
                  <label>Action</label>
                  <button class="btn btn-default remove" data-role="remove"> <span class="glyphicon glyphicon-remove"></span></button>
                  <button class="btn btn-default" data-role="add"> <span class="glyphicon glyphicon-plus"></span></button>
                </div>
              </div>

Et teste la soumission du formulaire pour voir toutes les données passées dans la variables $_POST.

Du coup, tu vas peut-être changer les boutons + en boutons - (pour supprimer chaque ligne d'ingrédient) et rajouter dessous un seul bouton + pour ajouter un nouveau bloc pour un nouvel ingrédient.

Passons à la suite maintenant...

Regarde par exemple ce lien http://christianelagace.com/php/ajax-pour-gerer-des-listes-deroulantes-imbriquees/ (un pays choisi dans une liste propose le choix d'une province dans une liste liée).
Toi, tu vas pouvoir te servir de ça et adapter pour choisir la famille dans une liste qui te permettra de choisir l'ingrédient dans une liste.

Mais, ce n'est pas tout, il te faut gérer également l'ajout de plusieurs ingrédients (bouton +). Dès que tu appuis sur +, 2 nouvelles listes (familles et ingrédients) et un champ input (quantité) (en gros le bloc que j'ai recopié ici) doivent être ajoutés via une requête ajax.

Que penses-tu de tout ça ?

Zulkar
Auteur

Alors, j'ai décommenté la ligne de debogage !
J'ai remplacé le code, par du code HTML.
Lorsque je demande l'édition d'une recette, il me renvoie un Array vide.
Si je commente la ligne de debogage, j'ai bien ma page recette.edit.php avec les champs de la recette pré-rempli et la partie ingrédients/quantités cette fois-ci.

<?php
//Pour débogage
//echo '<pre>' . print_r($_POST, true) . '</pre>'; die();

// Connexion à la table
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
    // UPDATE des données récupérées
    $result = $recetteTable->update($_GET['id'], [
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    // Trouver le code pour INSERT et UPDATE des ingrédients et quantités

    if ($result) {
        // Redirection et/ou Message
    }
}
// On récupère l'id de la recette
$recette = $recetteTable->find($_GET['id']);
// Connexion aux tables pour remplir les SELECT
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$ingredients = App::getInstance()->getTable('Ingredient')->extract('id', 'ingredients');
// On reprend le formulaire de la recette
$form = new \Core\HTML\BootstrapForm($recette);
?>

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        Recettes
        <small>Control Panel</small>
      </h1>
      <ol class="breadcrumb">
        <li><a href="index.php?p=home"><i class="fa fa-dashboard"></i> Home</a></li>
        <li class="active"><a href="index.php?p=ingredients">Recettes</a></li>
        <li class="active">Editer</li>
      </ol>
    </section>

    <!-- Main content -->
    <section class="content">

    <div class="row"> 

      <form method="POST"> 

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Mise à jour de la recette</h3>
            </div>
            <div class="box-body">

              <!-- Catégories -->
              <?= $form->select('categories_id', 'Catégorie', $categories); ?>
              <!-- /.form group -->

              <!-- Nom de la recette -->
              <?= $form->input('recettes', 'Choisir le nom de la recette'); ?>
              <!-- /.form group -->

              <!-- Nombre de personnes -->
              <?= $form->input('nbpersonnes', 'Nombre de personnes'); ?>
              <!-- /.form group -->

              <!-- Marge -->
              <?= $form->input('marge', 'Marge'); ?>
              <!-- /.form group -->

              <!-- Observation -->
              <?= $form->input('observations', 'Observations', ['type'=>'textarea']); ?>
              <!-- /.form group -->

              <button id="add" class="btn btn-block submit">Sauvegarder</button>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

        <div class="col-md-6">

          <div class="box box-danger">
            <div class="box-header">
              <h3 class="box-title">Sélectionner les ingrédients</h3>
            </div>
            <div class="box-body">
            <div data-role="dynamic-fields">
              <div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                  <div class="group-form">
                    <label>Ingrédient</label>
                    <select name="ingredients" id="ingredients" class="form-control">
                      <?php foreach (App::getInstance()->getTable('Ingredient')->getIngredients() as $ingredient): ?>
                      <option value="<?= $ingredient->id; ?>"><?= $ingredient->ingredients; ?></option>
                      <?php endforeach; ?>
                    </select>
                  </div>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <div class="group-form">
                    <label>Quantité</label>
                    <input type="text" class="form-control" />
                  </div>
                  <!-- /.form group -->
                </div>

                <div class="col-md-2">
                  <label>Action</label>
                  <button class="btn btn-default remove" data-role="remove"> <span class="glyphicon glyphicon-remove"></span></button>
                  <button class="btn btn-default" data-role="add"> <span class="glyphicon glyphicon-plus"></span></button>
                </div>
              </div>
            </div>
            </div>
            <!-- /.box-body --> 
          </div>
          <!-- /.box -->
        </div>

      </form>
      <!-- /.form -->

    </div>
    <!-- /.row -->

    </section>
    <!-- /.content -->

  </div>
  <!-- /.content-wrapper -->

EDIT : Tu as été plus vite que moi pour poster lol ... je lis ton message à l'instant.

J'ai répondu avant ton message, tu as eu le temps de le lire ?

Zulkar
Auteur

Alors je viens de lire ton message avant le miens et je vais faire le point avant qu'on se mélange les pinceaux ;)
Pour le lien que tu donnes, j'avais aussi pensé aux champs imbriqués. Mais je ne l'ai jamais mis en place car je cherchais toutes les différentes manières que je pouvais utiliser pour ajouter mes ingrédients.
Pour les boutons + et -, ils fonctionnent bien depuis le début, avec un code dans mon fichier JS.

Ok pour le point et les boutons + et -.
Attention en effet, tu as changé d'avis entre temps, maintenant tu n'utilises qu'une seule liste pour les ingrédients.

Pour résumer ton problème, il se situe au niveau de ton formulaire html.

Une petite explication au passage qui te sera peut-être inutile : chaque fois que tu utilises un champs dans un formulaire et quelque soit son type (input, select, textarea...) la valeur de l'attribut name sert de clé dans le tableau associatif $_POST.
Tu peux (et c'est ce que tu cherches à faire ici) utiliser plusieurs fois le même name (pour chaque ingrédient et sa quantité) mais seul les derniers champs ingrédient et quantité seront passés dans la variable $_POST, c'est pour cela qu'il faut utiliser des [].

Pour le moment, oublies la partie base de données, recopie ton code html de ta page web plusieurs fois (on va dire le bloc ingrédient dans ta div inline) quite à changer les attributs name (ingredient1, quantite1, ingredient2, quantite2 ...) et testes si tu récupères bien les données dans la variable $_POST.

Tu essaies les deux méthodes :

  • ingredient[] et quantite[]
  • ingredient1 et quantite2

Et tu vois ce qui est passé dans $_POST.

Attention, j'ai modifié mon précédent message.

Zulkar
Auteur

Pour l'instant, ça ne donne rien de rien !
Mais je me disais que c'était sûrment normal puisque à aucun moment je me connecte à la table de liaison ?

Oublies la base de données pour le moment... Il faut d'abord tester ton formulaire et les champs qui seront soumis.

Tu as essayé les deux méthodes ?

Zulkar
Auteur
<div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                  <div class="group-form">
                    <label>Ingrédient</label>
                    <select name="ingredient1" id="ingredient1" class="form-control">
                      <?php foreach (App::getInstance()->getTable('Ingredient')->getIngredients() as $ingredient): ?>
                      <option value="<?= $ingredient->id; ?>"><?= $ingredient->ingredients; ?></option>
                      <?php endforeach; ?>
                    </select>
                  </div>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <div class="group-form">
                    <label>Quantité</label>
                    <input type="text" name="quantite1" class="form-control" />
                  </div>
                  <!-- /.form group -->
                </div>
<div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                  <div class="group-form">
                    <label>Ingrédient</label>
                    <select name="ingredient[]" id="ingredient1" class="form-control">
                      <?php foreach (App::getInstance()->getTable('Ingredient')->getIngredients() as $ingredient): ?>
                      <option value="<?= $ingredient->id; ?>"><?= $ingredient->ingredients; ?></option>
                      <?php endforeach; ?>
                    </select>
                  </div>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <div class="group-form">
                    <label>Quantité</label>
                    <input type="text" name="quantite[]" class="form-control" />
                  </div>
                  <!-- /.form group -->
                </div>

Je dois mal m'y prendre.

Modifies ton code comme ceci :

<?php
// Connexion à la table
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
//Pour débogage
echo '<pre>' . print_r($_POST, true) . '</pre>'; die();

ça risquait pas de marcher sinon :)

J'ai lu ton précédent message, c'est ma faute, avec mon précédent message, ça devrait aller.
Le die() interrompt le script donc forcément là où il était placé, tu avais juste un Array vide sans pouvoir soumettre ton formulaire...

Milles excuses !!! :-)

Zulkar
Auteur

T'inquiètes !!!
Ça ne récupère toujours pas les ingrédients et les quantités dans les champs pré-remplis.

C'est à dire, les ingrédients que tu aurais déjà liés à ta recette ?
La variable $_POST n'est pas vide et contient bien un ingrédient quand tu en rentres un ?

Zulkar
Auteur

Pour la première question ... oui.

Pour le moment, c'est normal sinon le reste fonctionne ? avec les deux méthodes et plusieurs lignes d'ingrédients et quantités ? Les données sont bien transmises dans la variable $_POST ?

Zulkar
Auteur

Autant pour moi, c'est moi qui me plante sur ce que tu veux me faire faire :)
Donc, maintenant que j'ai compris, les 2 méthodes fonctionnent.
Avec les name1 et name2

Array
(
    [categories_id] => 5
    [recettes] => Crème de champignons
    [nbpersonnes] => 4
    [marge] => 4.5
    [observations] => Test observations crème de champignons
    [ingredient1] => 123
    [quantite1] => 0.900
    [ingredient2] => 145
    [quantite2] => 0.500
)

Avec les name[]

Array
(
    [categories_id] => 5
    [recettes] => Crème de champignons
    [nbpersonnes] => 4
    [marge] => 4.5
    [observations] => Test observations crème de champignons
    [ingredient] => Array
        (
            [0] => 123
            [1] => 145
        )

    [quantite] => Array
        (
            [0] => 0.900
            [1] => 0.500
        )

)

Tu peux voir que ça sera facile de récupérer les données avec les deux méthodes :

  • méthode avec [] on récupére des tableau avec des clés "propres"
  • méthode avec des noms et des chiffres, il faudra analyser toutes les clés mais on pourra passer les identifiants des ingrédients

Bon, donc la partie formulaire fonctionne en dur, maintenant essaies avec la classe BootstapFrom avec le code que tu utilisais avant :

<?= $form->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>

ça devrait te donner le même résultat qu'avec la méthode [].

Zulkar
Auteur

Si j'utilise à nouveau la classe Bootstrapform, il me remet mon message d'erreur pour la form ingredients_id[]

Donc il va falloir modifier la classe BootstrapForm et rajouter une nouvelle méthode pour prendre ce cas en compte ou mieux, on va utiliser la solution avec les clés de la forme 'ingredientt_1', 'ingredientt_2', on pourra tester les clés dans la variable $_POST avec les fonctions http://php.net/manual/fr/function.array-keys.php et http://php.net/manual/fr/function.strpos.php mais on modifiera quand même la classe.

Maintenant, on va réfléchir à la mainère de trouver les ingrédients liés à une recette et il faudra générer du code html pour chaque ingrédient (nom de l'ingrédient dans une liste avec l'ingrédient sélectionné et un champ de type input pour la quantité avec l'id de l'ingrédient).

Je te propose de mettre tout ce code dans une classe dans App\Table\Recette par exemple ou autre.
On peut envisager une méthode dans la classe Recette pour écrire $recette->getIngredients(recette_id) ou une méthode $recette->getListHTMLIngredients(recette_id).

A toi de bien penser ton design pour voir si tu utilises un seul bouton + et plusieurs lignes et des boutons - ou autre.

Je regarde la video de la formation sur les tables et on voit ça dimanche, je ne serais pas disponible demain soir.

Zulkar
Auteur

J'ai un seul bouton + qui me rajoute des lignes avec des boutons -

Pourquoi dans mon code de départ, lorsque j'ajoutais les ingrédients et les quantités en même temps que tout le reste, il prenait bien en compte la classe BootstrapForm, mais pour l'édition il n'en veut pas ?

Dans mon App\Table\Recette, j'avais ça :

<?php
namespace App\Table;

use Core\Table\Table;

/**
* 
*/
class RecetteTable extends Table
{

    protected $table = 'recettes';

    /**
    * Récupère les ingrédients en liant les familles et les unités associées
    * @return \App\Entity\IngredientEntity
    */
    public function getRecettes()
    {
        return $this->query("
            SELECT C.categories, C.id, R.recettes, R.nbpersonnes, I.ingredients, I.prix
            FROM
                categories C
                    JOIN recettes R
                        ON C.id=R.categories_id
                    JOIN composition_recettes CR
                        ON R.id=CR.recettes_id 
                    JOIN ingredients I
                        ON I.id=CR.ingredients_id
            ORDER BY recettes DESC");
    }

    public function getDetailRecette()
    {
        return $this->query("
            SELECT C.categories, C.id, R.recettes, R.nbpersonnes, marge, observations, I.ingredients, quantites, I.prix
            FROM categories C
                JOIN recettes R
                    ON C.id=R.categories_id
                JOIN composition_recettes CR
                    ON R.id=CR.recettes_id 
                JOIN ingredients I
                    ON I.id=CR.ingredients_id
        ");
    }

}

Il faut que tu récupéres des informations sur ta table compositions_recettes que tu as peut-être renommé comme je te l'ai conseillé et non la table ingredients mais en 2 étapes.
Dans le meilleur des cas, tu ne trouveras qu'un seul ingrédient avec ta requête actuelle.

Le join te permet de faire une relation 1:1, là il te faut une relation 1:n qui va te retourner plusieurs enregistrements (les ingrédients liés à ta recette) à la différence de la relation 1:1 qui comme son nom l'indique te retourne un seul résultat (ta recette).

Bonjour,
Je viens de modifier les sources du chapitre https://www.grafikart.fr/formations/programmation-objet-php/mvc-model-view-controller pour les adpater à ton application.

Il te faudrait une classe App\Table\IngredientTable.php, définie par exemple comme ceci :

<?php
namespace App\Table;

use Core\Table\Table;

class IngredientTable extends Table{

    protected $table = 'ingredients';

    /**
     * Récupère les ingrédient liés ) une recette
     * @param $id int
     * @return \App\Entity\IngredientEntity
     */
    public function findForRecette($id){
        return $this->query("
            SELECT I.*, F.familles, CR.quantites, U.unites, U.symboles
            FROM ingredients I
            JOIN composition_recettes CR ON CR.ingredients_id = I.id
            JOIN familles F ON F.id = I.familles_id
            JOIN unites U ON U.id = I.unites_id
            WHERE CR.recettes_id = ?", [$id], false);
    }
}

Après tu peux utiliser :

$ingredients_recette = App::getInstance()->getTable('Ingredient')->findForRecette($recette_id);

Tu obtiendras un tableau de App\Entity\IngredientEntity dont voici le code :

<?php
namespace App\Entity;

use Core\Entity\Entity;

class IngredientEntity extends Entity{

}

J'ai gardé la définition de tes tables en suivant l'illustration de ton schéma sans savoir si tu as modifié ou pas les champs de tes tables comme je te l'avais conseillé.

Un argument supplémentaire concernant la modifications des champs :

  • un champ au singulier te fera penser à une relation 1:1 (exemple champs categorie_id et non categories_id dans la table recette puisque une recette appartient à une seule catégorie, ce qui implique l'utilisation de join pour avoir toutes les données liées par la relation)
  • une variable au singulier pour représenter un seul objet
  • une variable au pluriel pour représenter plusieurs objets (souvent un tableau d'objet), exemple $ingredients_recette, ce qui implique une relation 1:n et l'utilisation d'une requête supplémentaire par rapport à ton objet principal (ici ta recette 1:n ingrédients).

Il te faut maintenant une autre classe chargée de générer le code html pour les champs de formulaire de tous les ingrédients en relation avec une recette qui devrait se situer en toute logique dans l'espace de nom App\Html et pourrait s'appeler IngredientForm.

Zulkar
Auteur

Bonjour et merci.
Je n'ai pas encore changé mon schéma par rapport à tes conseils.
J'ai inséré ton code et je n'ai pas touché à IngredientEntity, j'avais déjà cette classe.

Zulkar
Auteur

Ce n'est sans doute l'idéal, mais pour les tests, j'ai donc rajouté ce code à ma page recette.edit.php

$form2 = new \Core\HTML\IngredientForm($_POST);

J'ai laissé une seule div class="inline">...</div avec ingredients_id[] et quantite[]
J'utilise le bouton + pour ajouter une ligne et je saisie des données.
Le tableau me retourne donc :

Array
(
    [categories_id] => 5
    [recettes] => Crème de champignons
    [nbpersonnes] => 4
    [marge] => 4.5
    [observations] => Test observations crème de champignons
    [ingredients_id] => Array
        (
            [0] => 123
            [1] => 124
        )

    [quantite] => Array
        (
            [0] => 0.900
            [1] => 0.500
        )

)

Donc la même chose que nos tests en dur !!!

Rajoutes également l'id de la recette dans un champ caché par exemple, du coup, tu peux lier les ingrédients à ta recette dans ta table composition_recettes.

Tu as 2 tableaux ingredients_id et quantite dans la variable $_POST, l'id de la recette.
Tu dois pouvoir te servir d'une fonction de tableau de php pour obtenir un tableau contenant :

$ingredients[0] = array('recettes_id' => $recette_id, 'ingredients_id' => 123, 'quantite' => 0.900);
$ingredients[1] = array('recettes_id' => $recette_id, 'ingredients_id' => 124, 'quantite' => 0.500);

Tu supprimes d'abord les ingrédients liés à ta recette puis tu ajoutes les données que tu as dans la variable $_POST pour gérer le cas où tu aurais enlever un ou plusieurs ingrédient(s) en éditant ta recette.

Zulkar
Auteur

J'ai fait ceci :

<div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                    <input type="hidden" name="recette_id" value="<?= $recette->id ?>">
                    <?= $form2->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <div class="group-form">
                    <?= $form2->input('quantite[]', 'Quantité'); ?>
                  </div>
                  <!-- /.form group -->
                </div>

Avec 1 seul ingrédient et la quantité, $_POST m'indique l'id de la recette, mais si j'ajoute un 2ème ingrédient, $_POST m'indique un id recette vide.

Essaies comme ceci :

<input type="hidden" name="recette_id" value="<?= $recette->id ?>">
<div class="inline">
                <div class=" col-md-7">
                  <!-- Ingrédient -->
                    <?= $form2->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <div class="group-form">
                    <?= $form2->input('quantite[]', 'Quantité'); ?>
                  </div>
                  <!-- /.form group -->
                </div>

Tu devrais pouvoir rajouter d'autres ingrédients.
Pourquoi ne pas en profiter pour générer le code hml pour les champs des ingrédients avec ta classe IngredientForm en changeant son constructeur pour lui passer également la liste des ingrédients ?

Zulkar
Auteur

Avant de continuer, si je place l'input caché avant la div inline, le bouton + ne fonctionne plus !

Tu gères ton bouton + avec du code javascript je suppose, tu l'as déjà posté ici que l'on regarde tout ça ?

Zulkar
Auteur

C'est bon, j'ai placé le input encore une div avant et je peux ajouter autant d'ingrédients que je veux.

Donc tu voulais dire quoi pour la classe IngredientForm ?

Et bien que tu devrais peut-être t'en servir pour générer out le code html pour la gestion des champs des ingrédients.
Tu passes le tableau des ingrédients dans le constructeur de ta classe et tu peux générer tout ce code :

                <div class=" col-md-7">
                  <!-- Ingrédient -->
                    <?= $form2->select('ingredients_id[]', 'Ingrédient', $ingredients); ?>
                  <!-- /.form group -->
                </div>

                <div class="col-md-3">
                  <!-- Quantités -->
                  <div class="group-form">
                    <?= $form2->input('quantite[]', 'Quantité'); ?>
                  </div>
                  <!-- /.form group -->
                </div>

plusieurs fois pour chaque ingrédients avec une fonction $form2->getFiledsForIngredients par exemple.

Zulkar
Auteur

Sans doute ! Je sais pas !

Donc là, il faut que j'arrive à récupérer les champs pré-remplis pour les ingrédients et les quantités de la recette ?
Ensuite, je dois :
. essayer de modifier un ingrédient ou une quantité
. essayer d'ajouter un ingrédient et la quantité
. essayer de supprimer un ingrédient
???

Tu géres l'affichage des champs avant que ton formulaire ne soit soumis quant tu modifies ta recette tu dois avoir les ingrédients avec le champ ingrédient et son champ quantité.
Les champs pré-remplis tu peux les gérer dans la fonction $form2->getFiledsForIngredients.
Tu fais ça avec une autre fonction chargée de générer le code html pour le champ ingrédient et son champ quantité justement.
Ensuite quand tu peux ajouter un ingrédient en cliquant sur le bouton + qui en ajax te retourne champ ingrédient et son champ quantité.
Tu valide ton formulaire et là tu ajoutes les ingrédients dans la base de données comme indiqué plus haut (en les supprimant avant comme indiqué).

Zulkar
Auteur

Je vais relire, essayer de comprendre et voir si je suis capable de faire ça à tête reposée !

ok ça marche

Zulkar
Auteur

Dans la classe IngredientForm, j'ai les fonctions input et select, donc dans la fonction getFieldsForIngredients il faut que je les utilise. Enfin je pense !
Mais, pour les

<div class=" col-md-7">
// et
<div class=" col-md-3">

Je les indique comment ?

Tu peux utiliser des fonctions echo :

echo '<div class=" col-md-7">';
...
echo '</div>';
...
Zulkar
Auteur
    public function getFieldsForIngredients(){
        echo '<div class="col-md-7">
                <?= $form2->select(' . "'ingredients_id[]'" . ',  ' . "'Ingrédient'" . ', ' . '$ingredients' . '); ?>
              </div>
              <div class="col-md-3">
                <?= $form2->input(' . "'quantite[]'" . ', ' . "'Quantité'" . '); ?>
              </div>';
    }

Hello, c'est pas tout à fait ça.
Je montre comment j'aurais fait à partir de 18h00 ce soir.

Alors, voilà ce que je te propose et que je viens de tester :

App\Entity\IngredientEntity

<?php
namespace App\Entity;

use Core\Entity\Entity;

class IngredientEntity extends Entity
{

    public function getQuantites()
    {
        return isset($this->quantites) ? $this->quantites : null;
    }

    public function getUnites()
    {
        return isset($this->unites) ? $this->unites : null;
    }

}

App\HTML\FormIngredient

<?php
namespace App\HTML;

class FormIngredient
{
    private $ingredients;

    /**
     * Constructeur avec la liste de tous les ingredients
     *
     * @param $ingredients
     */
    public function __construct($ingredients)
    {
        $this->ingredients = $ingredients;
    }

    /** Retourne une ligne d'un groupe de champs
     * @param $ingredient App\Entity\IngredientEntity
     * @return string
     */
    public function getFieldRow($ingredient)
    {
        // Chaîne à retourner
        $html = '';

        // Ouverture d'une div regroupant les champs
        $html .= $this->getStartRow($ingredient->id);

        // Ingrédient sélectionné dans la liste de tous les ingrédients
        $html .= $this->getSelectField($ingredient, 'Ingrédient');

        // Quantité pour l'ingrédient sélectionné
        $html .= $this->getInputField($ingredient, 'Quantité');

        // Actions gérer la suppression d'un ingrédient
        $html .= $this->getActionsRow($ingredient->id);

        // Fermeture de la div
        $html .= $this->getEndRow();

        return $html;
    }

    /**
     * @param $html string Code HTML à entourer
     * @return string
     */
    private function surround($html){
        return "<div class=\"form-group\">{$html}</div>";
    }

    /* Début d'une ligne d'un groupe de champs identifié au niveau javascript
     * par l'identifiant unique d'un ingrédient
     * 
     * @param $ingredient_id int identifiant unique d'un ingrédient
     * @return string
     */
    private function getStartRow($ingredient_id)
    {
        return '<div class="inline" id="' . $ingredient_id . '">';
    }

    /* Affiche une liste d'ingrédients sous la forme d'une liste d'options
     * @param $ingredient App\Entity\IngredientEntity
     *        ingrédient sélectionné dans la liste
     *
     * @param $label string nom du label
     * @return string
     */
    private function getSelectField($ingredient, $label)
    {
        $html = '<div class=" col-md-7">';

        $label = '<label>' . $label . '</label>';
        $input = '<select class="form-control" name="ingredient_' . $ingredient->id . '">';

        foreach($this->ingredients as $an_ingredient)
        {
          $attributes = '';

          // Si l'ingrédient est lié à la recette en cours de modification
          if($an_ingredient->ingredients == $ingredient->ingredients){
            $attributes = ' selected';
          }

          $input .= "<option value='{$an_ingredient->id}'$attributes>{$an_ingredient->ingredients}</option>";
        }
        $input .= '</select>';

        $html .= $this->surround($label . $input);

        $html .= '</div>';

        return $html;
    }

    /** Permet de changer la quantité d'ingrédient
     * @param $ingredient App\Entity\IngredientEntity
     *        ingrédient pour connaître sa quantité
     *
     * @param $label string nom du label
     * @return string
     */
    private function getInputField($ingredient, $label)
    {
        $html = '<div class=" col-md-3">';

        $label = '<label>' . $label . '</label>';

        $input = '<input type="text" name="ingredient_' . $ingredient->id . '" value="' . $ingredient->quantites . '" class="form-control">';
        $help = '<p class="help-block">Exprimée en ' . $ingredient->unites . '</p>';

        $html .= $this->surround($label . $input . $help);

        $html .= '</div>';

        return $html;
    }

    /* Permet la suppression d'un ingrédient
     * 
     * @param $ingredient_id int identifiant unique d'un ingrédient
     * @return string
     */
    private function getActionsRow($ingredient_id)
    {
        $html = '<div class="col-md-2">';

        $html .= '<label>Action</label>';
        $html .= '<button class="btn btn-default remove" data-role="remove" id="' . $ingredient_id . '"> ';
        $html .= '<span class="glyphicon glyphicon-remove"></span></button>';

        $html .= '</div>';

        return $html;
    }

    // Fin d'une ligne d'un groupe de champs
    private function getEndRow()
    {
        return '</div>';
    }

}

A utiliser avec ce bout de code :

        $ingredients = App::getInstance()->getTable('Ingredient')->all();
        $form_ingredient = new \App\HTML\FormIngredient($ingredients);
        $recette = App::getInstance()->getTable('Recette')->findWithCategory($_GET['id']);
        $ingredients_recette = App::getInstance()->getTable('Ingredient')->findForRecette($_GET['id']);

        $ingredients_html = '';

        // $ingredients_html contient tout le code html pour gérer les champs select et input de chaque ingrédient
        foreach($ingredients_recette as $ingredient)
        {
          $ingredients_html .= $form_ingredient->getFieldRow($ingredient);
        }

Je te conseille d'aller jusqu'au chapitre Model/View/Controler de la formation, dans ce cas-là, le dernier bout de code est à mettre dans l'action show du controllleur RecetteController...

Zulkar
Auteur

Wahouuu ... ah oui ... tout ce code ... j'étais loin !!!

Dans la classe Form de Grafikart, il gére la valeur des champs, ici je l'ai codé en dur.
J'ai dû modifier la classe IngredientEntity pour gérer les getters et le cas où les propriétés Quantites et Unites sont nulles (c'est le cas par défaut car ces propriétés ne sont définies que par la liaison avec les tables concernées).
Je n'est pas utilisé la méthode avec des [] mais directement avec les idenfiants des ingrédients pour avoir des clés dans la variable $_POST mieux définies car uniques (ingredient_123, quantite_123 par exemple, on pourra gérer plus facilement la relation entre ingrédient et quantité comme ça).

Zulkar
Auteur

Je m'y perds dans le chapitre Model/View/Controler de la formation, car moi depuis le début, je ne fais pas tout et j'adapte car je ne pensais pas avoir besoin de tout ou fonctionner de la même façon :(

Je te conseille et ça reste un conseil de suivre ce chapitre car tout ton code sera bien mieux organisé.
Ici, je t'ai quand même écrit du code pour que tu puisses l'utiliser dans lire ce chapitre.
A toi de voir pour la suite...

Sinon, tu as eu le temps de tester ce que je t'ai filé ? J'ai oublié de préciser que j'avais englobé toute ta partie design dans mon code.

Et si tu as testé, tu as pu remarquer que j'ai utilisé les unités sous le champ input pour la quantité :)

Zulkar
Auteur

Ah non, je n'ai même pas essayé du tout !
Je me suis jeté directement sur la suite de la formation lol

Ok, je te laisse faire à ton rhytme, fais moi signe en cas de besoin :)

Zulkar
Auteur

Donc ton dernier bout de code, je dois l'intégrer dans ma page recette.edit.php

<?php
// Connexion à la table
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
//Pour débogage
echo '<pre>' . print_r($_POST, true) . '</pre>'; die();
    // UPDATE des données récupérées
    $result = $recetteTable->update($_GET['id'], [
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    // Trouver le code pour INSERT et UPDATE des ingrédients et quantités

    if ($result) {
        // Redirection et/ou Message
    }
}
// On récupère l'id de la recette
$recette = $recetteTable->find($_GET['id']);
// Connexion aux tables pour remplir les SELECT
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$familles = App::getInstance()->getTable('Famille')->extract('id', 'familles');
$ingredients = App::getInstance()->getTable('Ingredient')->extract('id', 'ingredients');
// On reprend le formulaire de la recette
$form = new \Core\HTML\BootstrapForm($recette);
$form2 = new \Core\HTML\IngredientForm($_POST);
?>

Modifies comme ceci :

<?php
// Connexion à la table
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
//Pour débogage
echo '<pre>' . print_r($_POST, true) . '</pre>'; die();
    // UPDATE des données récupérées
    $result = $recetteTable->update($_GET['id'], [
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    // Trouver le code pour INSERT et UPDATE des ingrédients et quantités

    if ($result) {
        // Redirection et/ou Message
    }
}
// On récupère l'id de la recette
$recette = $recetteTable->find($_GET['id']);
// Connexion aux tables pour remplir les SELECT
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$familles = App::getInstance()->getTable('Famille')->extract('id', 'familles');
$ingredients = App::getInstance()->getTable('Ingredient')->extract('id', 'ingredients');
// On reprend le formulaire de la recette
$form = new \Core\HTML\BootstrapForm($recette);
$ingredients = App::getInstance()->getTable('Ingredient')->all();
$form_ingredient = new \App\HTML\FormIngredient($ingredients);
$recette = App::getInstance()->getTable('Recette')->findWithCategory($_GET['id']);
$ingredients_recette = App::getInstance()->getTable('Ingredient')->findForRecette($_GET['id']);

$ingredients_html = '';

// $ingredients_html contient tout le code html pour gérer les champs select et input de chaque ingrédient
foreach($ingredients_recette as $ingredient)
{
  $ingredients_html .= $form_ingredient->getFieldRow($ingredient);
}

/*
tu affiches ton formulaire mais à la place de
<div class="inline">
...
</div>

tu écris echo $ingredients_html;
*/

?>        
Zulkar
Auteur

Je ne retrouve pas la fonction qui a été faite findWithCategory() car il y a une erreur avec à la ligne 32

Au temps pour moi, c'est dans la classe App\Table\RecetteTable.php :

<?php
namespace App\Table;

use Core\Table\Table;

class RecetteTable extends Table{

    protected $table = 'recettes';

    /**
     * Récupère les derniers article
     * @return array
     */
    public function last(){
        return $this->query("
            SELECT recettes.id, recettes.recettes, categories.categories as categorie
            FROM recettes
            LEFT JOIN categories ON categories_id = categories.id
            ORDER BY recettes.id DESC");
    }

    /**
     * Récupère une recette en liant la catégorie associée
     * @param $id int
     * @return \App\Entity\RecetteEntity
     */
    public function findWithCategory($id){
        return $this->query("
            SELECT recettes.id, recettes.recettes, categories.categories as categorie
            FROM recettes
            LEFT JOIN categories ON categories_id = categories.id
            WHERE recettes.id = ?", [$id], true);
    }

}

Du coup avec le bootstrap normal, ça donne un truc comme ça :

Zulkar
Auteur

J'obtiens ceci :

Regarde le code source de la page, je te prie autour du second bloc d'actions.
Oublies les boutons sur ma capture d'écran, j'ai modifié ma page entre temps.

Zulkar
Auteur

Je rajoute ces lignes pour le bouton +

$html .= '<button class="btn btn-default" data-role="add"> ';
$html .= '<span class="glyphicon glyphicon-plus"></span></button>';

Normalement, le code que je t'ai donné gère également les boutons (des boutons + pour le coup qu'il faudra changer en -), je t'invite à regarder mon code plus en détails.

Zulkar
Auteur

Oui, dans FormIngredient, tu gères la modification des quantités, j'ai testé et le $_POST le prend bien en compte.
Le changement d'ingrédient ne change rien !
Tu gères aussi la suppression d'un ingrédient ... que je n'ai pas testé !
Et si j'ajoute le bouton + comme je l'ai indiqué, l'ajout n'est pas pris en compte.

Attends, c'est normal, mon code ne gère "que" la partie formulaire pour les ingrédients liés à une recette.
Le changement de quantités donctionne en effet mais pas le changement d'ingrédient.
Décommentes la ligne de debug pour afficher le contenu de la variable $_POST je te prie.
Pour le bouton + non géré, c'est normal également, il va falloir le gérer en ajax.

Zulkar
Auteur
Array
(
    [categories_id] => 5
    [recettes] => Crème de champignons
    [nbpersonnes] => 4
    [marge] => 4.5
    [observations] => Test observations crème de champignons
    [recette_id] => 3
    [ingredient_145] => 0.400
    [ingredient_124] => 0.250
)

Ah, je pensais que l'on aurait des clés 'quantite_145' et 'quantite_124' aussi mais c'est pas grave.
Le temps pour moi de te coder ça en te donnant les explications...

Modifies ton code comme ceci :

// Trouver le code pour INSERT et UPDATE des ingrédients et quantités
$recette_id = $_POST['recette_id'];
$compositionTable = App::getInstance()->getTable('CompositionRecette');

// Suppresion des ingrédients déjà ajoutés (sinon on aura des doublons)
//$compositionTable->query("DELETE * FROM composition_recettes WHERE recettes_id = ?", [$recette_id]);

// Analyse des clés des varaibles postées
foreach(array_keys($_POST) as $k)
{
    // Si la clé commence par 'ingredient'
    if (strpos($k, 'ingredient') === 0)
    {
      // La variavle $_POST[$k] contient la quantité
      // On décompose la chaîne 'ingredient_xxx' en deux variables
      // $dummy = 'ingredient' (qui ne sert à rien) et $ingredient_id = 'xxx' (l'identifiant de l'ingrédient)
      list($dummy, $ingredient_id) = explode('_', $k);
      $compositionTable->create([
          'recettes_id' => $recette_id,
          'ingredients_id' => $ingredient_id,
          'quantites' => $_POST[$k]
      ]);
    }
}

Je ne suis pas sur de cette ligne alors je l'ai commentée :

//$compositionTable->query("DELETE * FROM composition_recettes WHERE recettes_id = ?", [$recette_id]);

Tu auras sans doute des ingrédients en double si tu la laisse commentée.
Je ne me souviens plus dans quel chapitre Grafikart aborde la suppression d'un enregistrement.

Pour le moment, tu peux modifier les ingrédients mais pas en ajouter ou en retirer...
La suite demain où on va aborder la partie ajout d'ingrédient...

Je t'invite à bien lire le code que je t'ai donné et recherché les fonctions utilisées...

Zulkar
Auteur

L'ajout, l'édition et la suppression, sont dans le TP: Back-end !

ok, je regarde ça et on corrige demain.

Tu peux déjà décommenté cette ligne, je viens de lire le code de la classe Core\Table\Table.php :

$compositionTable->query("DELETE * FROM composition_recettes WHERE recettes_id = ?", [$recette_id]);
Zulkar
Auteur

Pour le moment, j'ai l'impression que modifier un ingrédient ne change rien dans $_POST
Ton code :

//$compositionTable->query("DELETE * FROM composition_recettes WHERE recettes_id = ?", [$recette_id]);

C'est pour supprimer une recette ?
Si oui, ça je l'ai fait directement dans phpmyadmin.

Relis bien le code, il s'agit de la table composition_recettes, ne vas pas trop vite car tu vas mal apprendre :)
Ici, il s'agit de supprimer tous les ingrédients d'une recette avant d'enregistrer les ingrédients lorsque tu soumets le formulaire pour valider la modification de ta recette, sans ça tu auras des doublons.

Tu n'as qu'un seul bouton pour valider ton formulaire (pour modifier ta recette et ses ingrédients), on enregistre les modifications (et donc les ingrédients) en une seule fois.

Pour le moment, ça ne change rien sauf si tu modifie les ingrédients sans pouvoir changer leur nombre (ajout ou suppression).

Zulkar
Auteur

Bonsoir,
De retour après une absence imprévue, donc oui, tu as raison, je suis d'accord avec toi !!!

Bonsoir,
Où en es tu ? Tu es allé jusqu'au chapitre Model/Vue/Controller ou tu restes avec ta structure, histoire que j'adapte mon code au tiens ?
Je ne serais pas là demain soir mais disponible samedi.
On devrait passer à la gestion de l'ajout d'un nouveau bloc pour un ingrédient (avec select et input) en ajax...

Zulkar
Auteur

Bonjour,
Pour le moment, je garde ma structure, car il faut que j'adapte le chapitre des Controllers à mon cas et pour le moment, je ne suis pas au point.
Je visionne et re-visionne le chapitre !!
Ok pour la suite :)

Ok, donc de mon côté, je modifie mon code pour ne pas utiliser les contrôleurs tout comme toi.

Zulkar
Auteur

Bonsoir,
J'ai oublié, mais peut-être que tu attendais mon code qui permets de gérer les boutons + et - en JS :

// FONCTION AJOUTER LES INGREDIENTS D'UNE RECETTE
    // Remove button click
    $(document).on('click', '[data-role="dynamic-fields"] > .inline [data-role="remove"]', function(e) {
            e.preventDefault();
            $(this).closest('.inline').remove();
        }
    );
    // Add button click
    $(document).on('click', '[data-role="dynamic-fields"] > .inline [data-role="add"]', function(e) {
            e.preventDefault();
            var container = $(this).closest('[data-role="dynamic-fields"]');
            new_field_group = container.children().filter('.inline:first-child').clone();
            new_field_group.find('input').each(function(){
                $(this).val('');
            });
            container.append(new_field_group);
        }
    );

Hello, désolé pour hier, j'ai eu des problèmes de connexion internet.
Je teste ton code sur le mien pour voir comment poursuivre...

Je viens de coder et tester ça :
Classe App\Entity\FamilleEntity :

<?php
namespace App\Entity;

use Core\Entity\Entity;

class FamilleEntity extends Entity{

}

Classe App\Table\FamilleTable :

<?php
namespace App\Table;

use Core\Table\Table;

class FamilleTable extends Table{

    protected $table = 'familles';

}

Le fichier index.php modifié :

// Si a est passé en paramètre, on traite des requêtes en ajax
if(isset($_POST['a'])){
    $ajax = $_POST['a'];
}else{
    $ajax = null;
}

// Si a est défini
if ($ajax){

  // On traite la bonne requête ajax
  if ($ajax === 'add_ingredient'){
    require '../ajax/add_ingredient.php';
  }

  // Et on quitte le script
  die();
}

if(isset($_GET['p'])){
...

La page pages/recettes.show modifiée :

<?php

$familleTable = App::getInstance()->getTable('Famille');
$ingredientTable = App::getInstance()->getTable('Ingredient');
$recetteTable = App::getInstance()->getTable('Recette');

$ingredients = $ingredientTable->all();

$form_ingredient = new \App\HTML\FormIngredient($ingredients);
$recette = $recetteTable->findWithCategory($_GET['id']);
$ingredients_recette = $ingredientTable->findForRecette($_GET['id']);

$ingredients_html = '';

foreach($ingredients_recette as $ingredient)
{
  $ingredients_html .= $form_ingredient->getFieldRow($ingredient);
}

$familles = $familleTable->extract('id', 'familles');
$form = new \Core\HTML\BootstrapForm();

?>
<h1><?= $recette->recettes; ?></h1>

<form>
<div class="row" id="liste-ingredients">
<?= $ingredients_html; ?>
</div>  
<div class="row">

  <div class="form-group">
    <label>Famille d'ingredients :</label>
    <select class="form-control" name="famille" id="famille">
      <?php foreach($familles as $k => $v): ?>
        <option value='<?= $k ?>'><?= $v ?></option>
      <?php endforeach; ?>
    </select>
  </div>

  <button class="btn btn-success" data-role="add" id="add-ingredient" data-famille="1"> <span class="glyphicon glyphicon-plus"></span> Ajouter un ingrédient</button>  
</div>
</form>

<script>
$( document ).ready(function() {

  // Changement de famille d'ingrédient
  $( "#famille" ).change(function(e) {
    // On passe l'id de la famille choisie au bouton
    $( "#add-ingredient" ).data('famille', this.value);
  });

  // Suppression d'un ingrédient
  $( "#liste-ingredients" ).on("click", "button.remove", function(e) {
    e.preventDefault();
    var ingredient_id = $( this ).data("ingredient");
    $( '#'+ingredient_id ).slideUp();
  });

  // Ajout d'un ingrédient
  $( "#add-ingredient" ).click(function(e) {
    e.preventDefault();

    var famille_id = $( "#add-ingredient" ).data('famille');

    $.ajax({
       url : 'index.php', 
       type : 'POST', 
       data : 'a=add_ingredient&famille_id=' + famille_id,
       dataType : 'html',
       success : function(html, statut){
         $( "#liste-ingredients" ).append(html);
       }
    });
  });    

});  
</script>

Je n'ai pas adapter ton code en fin de compte.
La liste des familles des ingrédients est proposée au dessus du bouton ajouté. Un changement dans cette liste puis un ajout sur le bouton 'Ajouter un ingrédient' fait apparaître un nouveau bloc pour un nouvel élément (famille 'Boucherie' => Porc, agneau...).
Un appui sur un des boutons 'X' supprime la ligne du bloc concerné avec une petite animation (effet SlideUp), la validation du formulaire devrait permettre l'ajout et/ou la suppression des ingrédients.

Zulkar
Auteur

Pour la Classe App\Entity\FamilleEntity -> OK, je l'avais.
Pour la Classe App\Table\FamilleTable -> OK, je l'avais.

Ca, je n'ai pas cette page :

  // On traite la bonne requête ajax
  if ($ajax === 'add_ingredient'){
    require '../ajax/add_ingredient.php';
  }

Et pour la page recette.show, je ne l'ai pas non plus.

Désolé d'avoir oublié cette page, à mettre dans le dossier ajax dans le dossier parent de public :

<?php

$ingredientTable = App::getInstance()->getTable('Ingredient');

$ingredients_famille = $ingredientTable->findForFamille($_POST['famille_id']);

$form_ingredient = new \App\HTML\FormIngredient($ingredients_famille);

echo $form_ingredient->getFieldRow($ingredients_famille[0]);

?>

La page recette.show est la page que tu affciche quand tu modifes une recette, je ne me souviens plus du nom que tu as utilisé chez toi.

Zulkar
Auteur

Ah oui (recette.edit.php), je me disais aussi que le début du code me disait quelque chose :).
Je teste ça ce midi !!!
MERCIII

Zulkar
Auteur

J'espère ne rien avoir oublié !
J'ai essayé de placer ton code parmis ce qui avait déjà été fait.
Voici en visuel ce que cela donne :

Bonjour, ça semble correct en effet sauf que chez moi je n'ai qu'un seul bouton + comme déjà mentionné.
As tu essayé mon code ("mon" bouton ?) pour ajouter et tes boutons pour supprimer un ingrédient ?

Zulkar
Auteur

En effet, je me suis dit qu'avec mon code, l'ajout et la suppression de ligne fonctionnait.
Mais je voulais voir ce que donnait ton code !

Et donc au final, ça fonctionne ?

Zulkar
Auteur

Avec tes pages et ton code, l'action sur le '+' ou 'x' et sur 'ajouter un ingrédient' ne donnait rien dans le print_r de $_POST
Je ne suis pas sûr non plus que les changements dans index.php et la page ajax soient actifs !

Je t'ai mis tout code sur http://tamplan.free.fr/recettes.zip des fois que j'ai oublié de t'indiquer des modifications...

Zulkar
Auteur

J'essaie de tout vérifier avec minutie, je ne trouve pas pour le moment !
Donc pour toi, dans la recette que je montre toujours comme exemple (crème de champignons, avec 2 ingrédients), tu as 2 boutons X pour supprimer les lignes et seulement ton bouton 'Ajouter un ingrédient' alors ?

Oui, en effet c'est exactement ça.
Si tu es sous Linux, je te propose de rentrer cette commande à la racine de ton site (dossier parent du dossier public) :

php -S 127.0.0.1:8000 -t ./public/ -d display_errors=1

En ayant pris soin de modifer la configuration de la base de données, ensuite en allant sur http://localhost:8000/index.php?p=recettes.show&id=1, tu pourras modifier la recette n°1.

Zulkar
Auteur

Ah oui, en effet, moi je n'ai pas ce résultat de fonctionnement !

C'est à dire, tu as testé mon code ? Il est différent du tien ?

Zulkar
Auteur

J'ai testé ton code oui, à partir du ZIP que tu as partagé !
Et j'ai du mal à voir où est le soucis avec l'adaptation dans mon code

Zulkar
Auteur

Moi aussi je partage mon code, tout en continuant à chercher : https://we.tl/lZUeE7TRrp

Tu as testé et ça fonctionne donc ? Le comportement te convient ?

Regarde déjà mon fichier public/index.php.
Ensuite, j'ai peut-être changé la classe FormIngredient entre temps.
Je te conseille l'utilisation de ce logiciel pour comparer les fichier http://meldmerge.org/

Zulkar
Auteur

Le comportement me plaît bien. Je n'avais pas imaginé ça comme ça !
Je regarde pour tout le reste !

Je viens de regarder vite fait avec Meld, nous avons des noms de classes différents :

  • FamilleEntity chez moi, FamilyEntity chez toi
  • CategorieEntity chez moi, CategoryEntity chez toi

Attention, le code de Grafikart, ne gère pas les formes plurielles des noms en anglais mais en français : CategorieTable pour la table implique CategorieEntity pour l'entité retournée.
Je pense qu'une partie du problème peut venir de là.

Content que ça te plaise :-)

Dans mon code en bonus, tu trouveras la structure MVC sur laquelle tu butais, j'ai commenté une partie du code cependant (fichier public/index.php notament).
[Edit]
Je viens de voir que tu as fait pareil de ton côté pour le MVC...
[/Edit]

Je te laisse regarder et étudier tout ça pour ce soir, fais moi signe demain soir en cas de besoin.
Bonne soirée.

Zulkar
Auteur

Un grand MERCI.
Bonne soirée aussi !

Zulkar
Auteur

Avant d'oublier, j'indique ici ce que j'ai fait :

-> modification des Entity pour avoir les même que toi,

-> ajout de la fonction 'findForFamille' dans la table Ingredient,
que je n'avais pas,

-> j'ai enfin le design attendu avec un seul bouton 'ajouter un ingrédient' et un bouton supprimer à chaque fin de ligne.

Les actions 'supprimer' et 'ajouter' ne donnent rien (que la ligne de débogage soit commentée ou pas), donc je dois encore avoir un petit soucis quelque part !

Je pense que cela pourrait provenir de tout le code que l'on avait fait avant pour éditer les tables 'Recette' et 'CompositionRecette'. Car toi tu n'as pas tout ça dans ton code !

EDIT

J'ai "exporté" la partie JS qui est en bas de la page 'show' dans mon fichier JS et ça fonctionne mieux.

-> pour les animations, il doit avoir du mal à les prendre en compte.

-> impossible d'ajouter 2 ingrédients d'une même famille.

-> supprimer 2 ingrédients, rien ne se passe au click.

Zulkar
Auteur

Bonsoir,
Pour faire les tests, je garde la ligne de débogage décommentée.
-> supprimer un ingrédient existant déjà dans la recette (donc champ pré-rempli), ne "fonctionne" pas,
-> si j'ajoute par exemple 2 ingrédients, et que je décide de faire marche arrière, je peux en supprimer 1, ensuite les boutons "supprimer" n'ont plus d'action.

Bonsoir,
Désolé pour l'attente, j'ai en déplacement loin de ma machine de dévloppement et c'était pas prévu.

Dans le code que je t'avais donné, tu as :

...
  // Suppression d'un ingrédient
  $( "#liste-ingredients" ).on("click", "button.remove", function(e) {
    e.preventDefault();
    var ingredient_id = $( this ).data("ingredient");
    $( '#'+ingredient_id ).slideUp();
  });
  ...

Modifies le comme suit :

...
  // Suppression d'un ingrédient
  $( "#liste-ingredients" ).on("click", "button.remove", function(e) {
    e.preventDefault();
    var ingredient_id = $( this ).data("ingredient");
    $( '#'+ingredient_id ).slideUp();
    $( '#'+ingredient_id ).remove();
  });
  ...

Le bloc était seulement caché au lieu d'être caché puis détruit.

J'ai fait quelques tests y compris l'ajout et le retrait de 2 ingrédients, ça fonctionne chez moi, les champs sont correctment passés.

Zulkar
Auteur

Bonsoir,
Je n'ai pas trop été présent non plus !
Merci pour la modification.

Donc lorsque je prends la recette par exemple 'Crème de champignons' qui contient 2 ingrédients, je l'édite, je supprime 1 ingrédient ... lorsque je valide, la ligne de déboggage m'indique toujours les 2 ingrédients et leur quantité.

Pour le reste, c'est bon !

Je pense que ça vient de ton code js, essaies juste avec le mien pour voir, je viens juste de tester de mon côté et je ne rencontre aucun problème.

Zulkar
Auteur

Pardon, excuses-moi, c'est Chrome qui a du mal à rafraichir les modifications.
Sur Firefox ça fonctionne très bien !
Autant pour moi pffffff

Zulkar
Auteur

Donc dans le code qui suit, à début de ma page, on a plus qu'à prendre en compte tous les changements, modifications, addition, édition et suppression ?

<?php
// Connexion aux tables
$familleTable = App::getInstance()->getTable('Famille');
$ingredientTable = App::getInstance()->getTable('Ingredient');
$recetteTable = App::getInstance()->getTable('Recette');
if (!empty($_POST)) {
//Pour débogage
echo '<pre>' . print_r($_POST, true) . '</pre>'; die();
    // UPDATE des données récupérées
    $result = $recetteTable->update($_GET['id'], [
        'categories_id' => $_POST['categories_id'],
        'recettes' => $_POST['recettes'],
        'nbpersonnes' => $_POST['nbpersonnes'],
        'marge' => $_POST['marge'],
        'observations' => $_POST['observations']
    ]);

    // Trouver le code pour INSERT et UPDATE des ingrédients et quantités
    $recette_id = $_POST['recette_id'];
    $compositionTable = App::getInstance()->getTable('CompositionRecette');

    // Suppression des ingrédients déjà ajoutés (sinon on aura des doublons)
    $compositionTable->query("DELETE * FROM composition_recettes WHERE recettes_id = ?", [$recette_id]);

    // Analyse des clés des varaibles postées
    foreach(array_keys($_POST) as $k)
    {
        // Si la clé commence par 'ingredient'
        if (strpos($k, 'ingredient') === 0)
        {
          // La variable $_POST[$k] contient la quantité
          // On décompose la chaîne 'ingredient_xxx' en deux variables
          // $dummy = 'ingredient' (qui ne sert à rien) et $ingredient_id = 'xxx' (l'identifiant de l'ingrédient)
          list($dummy, $ingredient_id) = explode('_', $k);
          $compositionTable->create([
              'recettes_id' => $recette_id,
              'ingredients_id' => $ingredient_id,
              'quantites' => $_POST[$k]
          ]);
        }
    }

    if ($result) {
        // Redirection et/ou Message
    }
}
// On récupère l'id de la recette
$recette = $recetteTable->find($_GET['id']);
// Connexion aux tables pour remplir les SELECT
$categories = App::getInstance()->getTable('Categorie')->extract('id', 'categories');
$familles = $familleTable->extract('id', 'familles');
$ingredients = $ingredientTable->extract('id', 'ingredients');
// On reprend le formulaire de la recette
$form = new \Core\HTML\BootstrapForm($recette);
$ingredients = $ingredientTable->all();
$form_ingredient = new \App\HTML\FormIngredient($ingredients);
$recette = $recetteTable->findWithCategory($_GET['id']);
$ingredients_recette = $ingredientTable->findForRecette($_GET['id']);

$ingredients_html = '';

// $ingredients_html contient tout le code html pour gérer les champs select et input de chaque ingrédient
foreach($ingredients_recette as $ingredient)
{
  $ingredients_html .= $form_ingredient->getFieldRow($ingredient);
}

?>

Le code est déjà prêt, il te suffit de commenter la ligne de debug.
Prend le temps de bien lire le code avant et note toi le contenu retourné par la ligne de debug justement.

Zulkar
Auteur

Justement, si je décommente la ligne de debug, il m'indique une erreur avec 'recette_id' de cette ligne :

$recette_id = $_POST['recette_id'];

Je pensais que l'on avait parler de mettre un champ caché nommé recette_id avec l'identifiant de la recette.
Tu mets ça bien sûr après l'ouverture du formulaire.

Zulkar
Auteur

J'ai cette erreur :

Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '* FROM composition_recettes WHERE recettes_id = '3'' at line 1 in /srv/http/Dev/Dashboard/core/Database/MysqlDatabase.php on line 54

Tu as bien la table composition_recettes écrite comme ça ?

Zulkar
Auteur

Oui, j'ai vérifié justement et je n'ai rien touché à ce niveau depuis le début.

Modifies le code comme ceci pour tester :

$compositionTable->query("DELETE * FROM composition_recettes WHERE recettes_id = ?", [$recette_id]);
die('Suppression des ingrédients de la recette n°' . $recette_id);

Afin de vérfier que la requête est correctement exécutée, attention si c'est le cas tous les ingrédients de la recette seront supprimer et plus attribués en phase de test...

Zulkar
Auteur

Même message d'erreur et rien dans la console

C'est moi qui déconne, la bonne syntaxe est :

$compositionTable->query("DELETE FROM composition_recettes WHERE recettes_id = ?", [$recette_id]);

ça devrait aller maintenant, penses à retirer la ligne die suivante...

Zulkar
Auteur

Alors ajouter ou supprimer des ingrédients, apparemment ça fonctionne ... SAUF ... lol
Que toutes les quantités se transforment en 145.000, alors que moi je rentre 0.400 par exemple !

Zulkar
Auteur

En fait les chiffres avant le '.' correspondent aux 'id'

ok, c'est le bout de code avec $_POST[$k], je regarde le code...

En changeant ça :

    // Analyse des clés des varaibles postées
    foreach(array_keys($_POST) as $k)
    {
        // Si la clé commence par 'ingredient'
        if (strpos($k, 'ingredient') === 0)
        {
          // La variable $_POST[$k] contient la quantité
          // On décompose la chaîne 'ingredient_xxx' en deux variables
          // $dummy = 'ingredient' (qui ne sert à rien) et $ingredient_id = 'xxx' (l'identifiant de l'ingrédient)
          list($dummy, $ingredient_id) = explode('_', $k);
          $compositionTable->create([
              'recettes_id' => $recette_id,
              'ingredients_id' => $ingredient_id,
              'quantites' => $_POST[$k]
          ]);
        }
    }

Par ça :

    // Analyse des clés des varaibles postées
    foreach(array_keys($_POST) as $k)
    {
        // Si la clé commence par 'ingredient'
        if (strpos($k, 'ingredient') === 0)
        {
          // La variable $_POST[$k] contient la quantité
          // On décompose la chaîne 'ingredient_xxx' en deux variables
          // $dummy = 'ingredient' (qui ne sert à rien) et $ingredient_id = 'xxx' (l'identifiant de l'ingrédient)
          list($dummy, $ingredient_id) = explode('_', $k);
          $compositionTable->create([
              'recettes_id' => $recette_id,
              'ingredients_id' => $ingredient_id,
              'quantites' => $_POST['quantite_' . $k]
          ]);
        }
    }

ça devrait le faire... :-)

Zulkar
Auteur

Undefined index: quantite_ingredient_145 in /srv/http/Dev/Dashboard/pages/recettes/recette.edit.php on line 38

C'est la ligne des quantités !

;)

Tu peux décommenter la ligne de debug et me retourner le contenu de la varaible $_POST je te prie ?

Zulkar
Auteur
Array
(
    [categories_id] => 5
    [recettes] => Crème de champignons
    [nbpersonnes] => 4
    [marge] => 4.5
    [observations] => Test observations crème de champignons
    [recette_id] => 3
    [ingredient_145] => 145
    [quantite_145] => 0.250
    [ingredient_124] => 124
    [quantite_124] => 0.200
    [ingredient_144] => 144
    [quantite_144] => 0.060
    [famille] => 5
)

Je n'ai pas les yeux en face des trous ce soir...
En changeant ça :

    // Analyse des clés des varaibles postées
    foreach(array_keys($_POST) as $k)
    {
        // Si la clé commence par 'ingredient'
        if (strpos($k, 'ingredient') === 0)
        {
          // La variable $_POST[$k] contient la quantité
          // On décompose la chaîne 'ingredient_xxx' en deux variables
          // $dummy = 'ingredient' (qui ne sert à rien) et $ingredient_id = 'xxx' (l'identifiant de l'ingrédient)
          list($dummy, $ingredient_id) = explode('_', $k);
          $compositionTable->create([
              'recettes_id' => $recette_id,
              'ingredients_id' => $ingredient_id,
              'quantites' => $_POST[$k]
          ]);
        }
    }

Par ça :

    // Analyse des clés des varaibles postées
    foreach(array_keys($_POST) as $k)
    {
        // Si la clé commence par 'ingredient'
        if (strpos($k, 'ingredient') === 0)
        {
          // La variable $_POST[$k] contient la quantité
          // On décompose la chaîne 'ingredient_xxx' en deux variables
          // $dummy = 'ingredient' (qui ne sert à rien) et $ingredient_id = 'xxx' (l'identifiant de l'ingrédient)
          list($dummy, $ingredient_id) = explode('_', $k);
          $compositionTable->create([
              'recettes_id' => $recette_id,
              'ingredients_id' => $ingredient_id,
              'quantites' => $_POST['quantite_' . $ingredient_id]
          ]);
        }
    }

ça devrait ENFIN le faire... :-)

Zulkar
Auteur

Juste une ',' en trop à la fin de '$ingredient_id', mais ça m'a l'air de fonctionner.
Je vais faire différents tests !

J'ai juste un truc dans ma page 'recettes.php', elle affiche la liste des recettes et TOUS les ingrédients de toutes les recettes.
Avec ce code, lorsque je clique sur une recette pour afficher le détail correspondant, je récupère bien l'id de la recette dans la barre d'adresse mais il ne me sélectionne pas seulement les ingrédients de la recette en question.

<table id="recettes-table" class="table table-bordered table-striped" width="100%">
                <thead>
                  <tr>
                    <th>Recettes</th>
                    <th width="25%">Actions</th>
                  </tr>
                </thead>
                <tbody>
                <?php foreach (App::getInstance()->getTable('Recette')->all() as $recette): ?>
                  <tr>
                    <td onclick="location.href='index.php?p=recettes&id=<?= $recette->id; ?>'"><?= $recette->recettes; ?></td>
                    <td>
                      <!-- Bouton Editer -->
                      <a href="?p=recette.edit&id=<?= $recette->id; ?>" class="btn btn-xs btn-default btn-flat"><span class="glyphicon glyphicon-pencil"></span></a>
                      <!-- Bouton Imprimer -->
                      <a href="#" class="btn btn-xs btn-default btn-flat"><span class="glyphicon glyphicon-print"></span></a>
                      <!-- Bouton Supprimer -->
                      <form action="?p=recette.delete" method="post" style="display: inline;">
                        <input type="hidden" name="id" value="<?= $recette->id ?>">
                        <button type="submit" class="btn btn-xs btn-default btn-flat"><span class="glyphicon glyphicon-trash"></span></button>
                      </form>
                    </td>
                  </tr>
                <?php endforeach; ?>
                </tbody>
              </table>

J'ai corigé ma coquille...

Je n'ai pas bien compris, normalement quand tu édites une recette, tu ne devrais avoir que la liste des ingrédients liés à cette recette.

Zulkar
Auteur

Oui oui, tu as raison.
Je parlais de ma page 'recettes.php', ma page qui affiche toutes mes recettes avec le détail et les calculs, la page où je clique sur le bouton EDITER.
Mais ça sera un autre post je pense.

Je penche aussi pour un autre sujet dans le forum.

Zulkar
Auteur

Bonjour,
As-tu essayé d'ajouter 2 ingrédients de la même famille ?
Pour ma part, il n'en prend qu'un en compte et si j'insiste en saisissant le 2ème ingrédient qui n'est pas passé, il va simplement changer la quantité du premier.

Salut,
Juste par souci d'efficacité. Je vois beaucoup de App::getInstance() dans ton code.
Tu pourrai faire ceci, quelque part.

function app() {
    return App::getInstance();
}
app()->getTable('Recette');
Zulkar
Auteur

Merci pour ta contribution ;)

Zulkar
Auteur

Bonjour,
Hier soir, j'ai commencé à remplir un peu plus ma base de données afin de mieux me rendre compte de tout le fonctionnement.
J'ai voulu faire des oublis ... volontairement !
J'ai donc créé une recette.
Après la validation, je me rends compte que j'ai oublié un ingrédient.
J'édite donc la recette.
Je choisi la famille, puis l'ingrédient et la quantité ... je valide.
Mais au bout du compte, il affiche le premier ingrédient qui est dans la famille.
Je souhaite quand même le modifier, j'ouvre le SELECT et je vois qu'il me donne les 100 ingrédients que j'ai dans ma base.
Bref, je choisi l'ingrédient que je souhaite depuis le début, je valide.
Mais cela ne change rien, il garde l'ingrédient qu'il avait enregistré.

Je ne vois pas où se trouve le problème en examinant le code !

Bonsoir,
Essaies de voir ce que te retourne la ligne de debug.

Zulkar
Auteur

Bonsoir,
Par exemple, je prends la recette de 'Confit d'oignons' :
beurre 100 gr
oignon 1 kg
sel fin 10 gr

J'édite la recette.
1 - Pour le test, je choisi la famille 'Crèmerie' comme pour le beurre.
Mais je choisi d'ajouter 200 gr de lait.
Après validation, il n'a pas validé le lait, mais à modifier le beurre pour enregistrer 200 gr à la place des 100 gr de départ.

2 - Un autre test, je choisi la famille 'Légumes' comme pour oignon.
Je choisi d'ajouter 200 gr d'échalotes.
Ca fonctionne apparemment

Après 3 tests, sur cette recette, le problème ne concerne que la famille du 1er ingrédient.

Ca, c'est le beug du jour ... j'ai l'impression d'avoir des comportements différents à chaque fois, mais là, c'est ce qu'il me fait.

Zulkar
Auteur

Une nouvelle recette :

Array
(
    [categories_id] => 1
    [recettes] => Cookies Balls aux pignons de pin
    [nbpersonnes] => 30
    [marge] => 3
    [observations] => Cuisson : 190°C / 15 mn
    Réalisation : Former des boules de 10 gr
    [recette_id] => 18
    [ingredient_1] => 1          <--- Farine
    [quantite_1] => 0.090
    [ingredient_2] => 2            <--- Levure chimique
    [quantite_2] => 0.001
    [ingredient_10] => 10        <--- Pignons de pin
    [quantite_10] => 0.050
    [ingredient_17] => 17        <--- Beurre
    [quantite_17] => 0.050
    [ingredient_20] => 20        <--- Parmesan
    [quantite_20] => 0.030
    [ingredient_113] => 113    <--- Basilic
    [quantite_113] => 0.001
    [ingredient_15] => 15         <--- Oeuf
    [quantite_15] => 1.000
    [famille] => 4
)

Pour le test, je supprime l'ingrédient 10 'Pignons de pin'.
Je vérifie dans ma liste de recette et l'ingrédient a bien été supprimé.
J'édite la recette pour l'ajouter à nouveau.
Je choisi la famille 'Epicerie' qui me donne tous les ingrédients figurant dans cette famille.
Je choisi 'Pignons de pin' avec la quantité 0.050 gr. Je valide.

Array
(
    [categories_id] => 1
    [recettes] => Cookies Balls aux pignons de pin
    [nbpersonnes] => 30
    [marge] => 3
    [observations] => Cuisson : 190°C / 15 mn
    Réalisation : Former des boules de 10 gr
    [recette_id] => 18
    [ingredient_1] => 10
    [quantite_1] => 0.050
    [ingredient_2] => 2
    [quantite_2] => 0.001
    [ingredient_17] => 17
    [quantite_17] => 0.050
    [ingredient_20] => 20
    [quantite_20] => 0.030
    [ingredient_113] => 113
    [quantite_113] => 0.001
    [ingredient_15] => 15
    [quantite_15] => 1.000
    [famille] => 4
)

On voit que l'ingrédient 10 'Pignons de pin' n'apparaît pas MAIS que l'ingrédient 1 'Farine' a changé de quantité : 0.050 au lieu de 0.090 précédemment.
Et l'id de l'ingrédient 1 a changé également. Il est affiché 10, ça c'est l'id de 'Pignons de pin'.
L'explication, lorsque j'ai choisi la famille 'Epicerie', le premier ingrédient figurant dans cette famille est 'Farine de blé', donc c'est lui qui a été pris en compte.

J'espère que cette exemple est plus parlant.
Je ne vois pas d'où vient le problème.

Si tu veux te rendre compte, je peux fournir tous les fichiers et le code, avec la base de données !