Insérer une entrée de table 'nested' avec du php - Gestion des champs 'lft' et 'rgt'

Réduire
X
 
  • Filtrer
  • Heure
  • Afficher
Tout effacer
nouveaux messages

  • [RÉGLÉ] Insérer une entrée de table 'nested' avec du php - Gestion des champs 'lft' et 'rgt'

    En utilisant la documentation Joomla, on peut trouver comment insérer, mettre à jour ou supprimer des données à l'aide de JDatabase avec du code php.

    Il est donc possible de 'jouer' avec les tables de la base de données Joomla de manière totalement personnalisée pour ajouter des fonctionnalités. J'utilise cela assez souvent et je peux donner des exemples si cela est nécessaire pour mieux comprendre ma question.

    Pour permettre l'exécution du code, j'utilise LM-Custom, mais d'autres solutions existent.

    Néanmoins, je bute sur un problème : Certaines tables sont organisées selon un 'Nested Set Model' (je ne sais pas comment il faut traduire en français).
    On trouve plusieurs pages qui expliquent cela très bien, dont celle-ci.
    Cette organisation est pertinente pour les catégories d'articles, les groupes d'utilisateurs, etc. En pratique, dans les tables correspondantes, on trouve en plus des champs 'id', 'title', 'alias' habituels deux champs 'lft' et 'rgt' (pour 'left' et 'right', évidemment). Cette hierarchisation est très pratique pour permettre de retrouver tous les enfants d'une catégorie mère par exemple.

    MAIS : Lorsque l'on veut rajouter ou supprimer une catégorie (ou un groupe d'utilisateurs, c'est ce cas qui m'intéresse), cela oblige à décaler ces valeurs pour la quasi-totalité des entrées de la table.
    Je donne un exemple quand même, pour être compréhensible : Imaginons que deux sous-groupes du groupe Registered ont été créés. On a alors la table 'usergroups' suivante :
    title id lft rgt
    Registered 2 8 19
    Sous-Groupe1 10 15 16
    Sous-Groupe2 11 17 18
    Cette hiérarchisation indique que tous les groupes enfants de Registered ont une valeur lft>8 et une valeur rgt<19

    Quand on va rajouter un Sous-groupe3, cet ajout va "pousser" chacun des groupes parents (public, registered) et on va obtenir :
    title id lft rgt
    Registered 2 8 21
    Sous-Groupe1 10 15 16
    Sous-Groupe2 11 17 18
    Sous-Groupe3 12 19 20
    Les autres groupes (Super Users, Public,...) seront décalés aussi. C'est compréhensible, donc c'est codable, mais fastidieux. Et une erreur peut mettre par terre toute l'architecture de la table, rendant le site inutilisable.

    Il semble y avoir une méthode pour réaliser cet ajout proprement (décrite ici), mais je ne parviens pas à la mettre en oeuvre.
    Pour juste tenter de rajouter un groupe enfant de Registered, en m'inspirant de la partie 'Adding a new node', je tente :

    <?php
    $data_array = array(
    $title->quote('GroupeEnfant')
    );

    $table = JTable::getInstance('#__usergroups');
    $table->setLocation(2, 'last-child');
    $table->bind($data_array);
    $table->id = 0;
    $table->check();
    $table->store();
    ?>​​​

    J'obtiens une belle erreur 0 : Call to a member function setLocation() on bool

    Qu'est-ce que je rate?

    Note : Version de Joomla 5.1.0 ; php 8.2.20
    Dernière édition par jeejeeboy à 14/06/2024, 12h32

  • #2
    Bonjour,

    As tu inclu la table en question avant :

    JTable::addIncludePath(JPATH_COMPONENT . '/tables');

    Car la tu appelles des noeuds sur une variable nulle​

    Commentaire


    • #3
      Merci pour la réponse rapide!

      Effectivement, je n'avais pas compris cette ligne, et donc pas utilisé non plus.
      Telle qu'elle, elle produit l'erreur :

      Call to a member function quote() on null

      Mais j'imagine qu'il faut adapter l'emplacement '/tables'.
      Quelques essais ne produisent pas de résultat satisfaisant. Toujours la même erreur.

      Commentaire


      • #4
        Hello.

        En règle générale, il ne faut pas écrire directement dans les tables gérées par des extensions (tierces ou du framework).

        Il faut utiliser les méthodes des helpers ou des models du composants qui gère la table.​

        Dans le cas de la création de users group, il faudrait appeler la méthode save de Joomla\Component\Users\Administrator\Model​\GroupModel

        On recupere le model ainsi :
        Code PHP:
        $userModel $this->bootComponent('com_users')->getMVCFactory()
        ->
        createModel('User''Administrator', ['ignore_request' => true]); 


        Dernière édition par roland_d_alsace à 15/06/2024, 14h30
        A tous les utilisateurs de Joomla du très Grand Est de la France et du Jura suisse
        Rejoignez le Joomla Users Groupe Alsace...
        roland_d_alsace va-t-il devenir roland_du_grand_est ?

        Commentaire


        • #5
          Envoyé par jeejeeboy Voir le message
          Merci pour la réponse rapide!

          Effectivement, je n'avais pas compris cette ligne, et donc pas utilisé non plus.
          Telle qu'elle, elle produit l'erreur :

          Call to a member function quote() on null

          Mais j'imagine qu'il faut adapter l'emplacement '/tables'.
          Quelques essais ne produisent pas de résultat satisfaisant. Toujours la même erreur.
          1) où est-ce que tu instancies $title ?
          2)
          Code PHP:
          $table JTable::getInstance('#__usergroups'); 
          n'est pas bon, ce serait plutôt
          Code PHP:
          $table JTable::getInstance('UserGroup''Joomla\\CMS\\Table\\'); 
          Je pense que tu as vu que cette façon de faire est dépréciée :
          * @deprecated 4.3 will be removed in 6.0
          * Use the MvcFactory instead
          * Example: Factory::getApplication()->bootComponent('...')->getMVCFactory()->createTable($name, $prefix, $config);


          donc plutôt quelque chose de ce type :
          Code PHP:
          $table Factory::getApplication()->bootComponent('com_users')->getMVCFactory()->createTable('usergroups'); 

          Du coup tu n'as pas à t'occuper du
          Code PHP:
          JTable::addIncludePath(JPATH_COMPONENT '/tables'); 
          qui de toutes façons n'est certainement pas bon, JPATH_COMPONENT étant le chemin de ton composant, et pas celui où on trouvera la description de la classe de #_usergroups

          Il faudrait plutôt faire
          Code PHP:
          Table::getInstance('UserGroup''Joomla\\CMS\\Table\\');​ 
          Dernière édition par roland_d_alsace à 15/06/2024, 14h42
          A tous les utilisateurs de Joomla du très Grand Est de la France et du Jura suisse
          Rejoignez le Joomla Users Groupe Alsace...
          roland_d_alsace va-t-il devenir roland_du_grand_est ?

          Commentaire


          • #6
            Vraiment merci pour l'aide apportée, je fais le maximum pour être autonome, ne pas me sentir dépassé et progresser. Et aider les autres aussi quand je m'en sens le niveau.
            Mais j'avoue que j'ai peut-être atteint ma limite sur ce coup.
            Je tente quand même d'y arriver, et de me faire comprendre.

            Envoyé par roland_d_alsace Voir le message
            Hello.

            En règle générale, il ne faut pas écrire directement dans les tables gérées par des extensions (tierces ou du framework).​

            ​[/COLOR][/FONT][/COLOR]
            Bien sûr. Je ne bidouille pas les tables natives de Joomla sans arrêt. Mais pas sans raison non plus... Je précise mon besoin, en espérant que cela éclaire ma question :
            • Professeur, je permets à mes élèves d'accéder à mes cours, des exercices, des entraînements etc.
            • Joomla est le CMS rêvé pour ça, compte tenu de la gestion des ACL. Chaque classe possède des accès spécifiques, accède à certains cours, certaines pièces jointes, certains devoirs. Tout fonctionne à merveille.
            • Pour chacune de mes classes, je crée un groupe d'utilisateurs. Et un niveau d'accès. Mais les classes, ça va, ça vient. Elles changent chaque année de nom, certaines disparaissent, d'autres arrivent...
            • Comme extension, j'utilise Fabrik pour gérer mes multiples formulaires : Travail à rendre, calendrier, cahier de texte etc... Et dans cette optique, j'utilise systématiquement des pré-filtres d'affichages de table, justement pour limiter les enregistrements affichés en fonction des droits de l'élève qui se connecte.
            Donc : à chaque création de classe, il faut :
            • Créer un groupe enfant de Registered
            • Créer un niveau d'accès
            • Ajouter une dizaine d'instructions de pré-filtres dans les options d'affichage des listes de Fabrik
            L'inverse en cas de suppression de classe.
            En cas de changement de nom d'une classe (chaque année donc), les mêmes manipulations en modifiant les champs un par un.

            C'est fastidieux, et j'ai entrepris d'automatiser tout ça. J'ai trouvé l'ensemble des emplacements où les informations étaient stockées dans les tables (tables créées par moi-même à partir de Fabrik, pas de risque de corrompre l'ensemble du site). Modification, création et suppression des pré-filtres sont parfaitement gérées avec des boutons qui appellent des scripts php. Grâce à LM-Custom, mais je passe mon temps à écrire à quel point UP et LM-Custom sont fascinants, on va finir par croire que j'ai des actions LOMART...

            Modification du nom d'un groupe déjà existant, c'est fait aussi. Il ne me reste donc plus qu'une écharde dans le pied : Je ne sais pas ajouter les 5 lignes de code qui permettent d'ajouter proprement un groupe d'utilisateurs enfant de Registered, en raison des champs 'lft' et 'rgt' à gérer. Et j'avoue que maintenant que j'ai un bon gros bouton qui fait tout le travail de gestion, je trouve rageant de ne pas réussir à finaliser le travail.

            En espérant que toute cette explication n'était pas trop longue, je résume mon besoin : Je cherche un exemple de code permettant d'ajouter dans la table 'usergroups' un groupe enfant de Registered, proprement.
            J'ai essayé de lire la documentation plusieurs fois, et je bute.

            roland_d_alsace : Merci beaucoup pour les conseils, ça va m'aider. Pour le moment , je n'ai malheureusement pas réussi à les mettre en oeuvre, mais je creuse....

            Commentaire


            • #7
              Non testé mais ça devrait être un truc comme ça

              use Joomla\CMS\Factory;
              use Joomla\CMS\Table\Table;

              $app = Factory::getApplication('site');

              Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_users/tables');
              $usergroup = Table::getInstance('Usergroup', 'UsersTable');​

              $data = array(
              'title' => 'Nom du nouveau groupe', // Nom du groupe
              'parent_id' => 2 // ID du groupe parent
              );

              if (!$usergroup->bind($data)) {
              $app->enqueueMessage($usergroup->getError(), 'error');
              }

              if (!$usergroup->check()) {
              $app->enqueueMessage($usergroup->getError(), 'error');
              }

              if (!$usergroup->store()) {
              $app->enqueueMessage($usergroup->getError(), 'error');
              } else {
              $app->enqueueMessage('Le nouveau groupe a été créé avec succès.', 'message');
              }​

              Commentaire


              • #8
                Nioupixel , roland_d_alsace , merci pour les réponses.

                Nioupixel : Avec le code de la réponse #7 , je me retrouve avec une erreur 'unexpected variable "$data"' qui m'étonne bien, parce que je trouve plutôt logique et en accord avec ce que j'ai lu dans la doc de Joomla, la partie :

                $data = array(
                'title' => 'Nom du nouveau groupe',
                'parent_id' => 2
                );

                $usergroup->bind($data);
                $usergroup->check();
                $usergroup->store();

                Quant aux premières lignes pour utiliser les méthodes Joomla, je m'y perds un peu. En l'état, ça ne fonctionne pas. J'ai tenté de modifier le code en tenant compte des conseils des messages#4 et #5 de roland_d_alsace, mais rien n'y fait.
                Ce n'est pas si grave, parce que le Back-end Utilisateurs:Groupes n'est pas si contraignant à utiliser. Mais ce bouton "+Nouveau" me rend quand même jaloux!

                Commentaire


                • #9
                  Bonjour,

                  Cette fois le code a été testé avant d'envoyer et ça fonctionne :
                  Code PHP:
                  use Joomla\CMS\Factory;
                  use 
                  Joomla\Component\Users\Administrator\Model\GroupModel;

                  $app Factory::getApplication();

                  try {
                  $groupModel = new GroupModel();

                  // Définir les données du nouveau groupe
                  $data = array(
                  'id' => 0,
                  'title' => 'Nom du nouveau groupe test'// Nom du groupe
                  'parent_id' => // ID du groupe parent
                  );

                  // Sauvegarder le groupe d'utilisateurs
                  if (!$groupModel->save($data)) {
                  // Gérer les erreurs de sauvegarde
                  $app->enqueueMessage('Erreur lors de la création du groupe''error');
                  } else {
                  $app->enqueueMessage('Le nouveau groupe a été créé avec succès.''message');
                  }
                  } catch (
                  Exception $e) {
                  $app->enqueueMessage($e->getMessage(), 'error');
                  }
                  ​ 
                  Dernière édition par Nioupixel à 18/06/2024, 11h44

                  Commentaire


                  • #10
                    Nioupixel : Merci beaucoup pour l'investissement, et l'aide précieuse.
                    Un petit espace s'est glissé en ligne 2, il me semble. C'est bien "use Joomla\Component\Users\Administrator\Model\GroupModel;" je pense

                    Ce code est tout à fait compréhensible pour moi, ce qui m'arrange bien.
                    MAIS malheureusement, je n'arrive toujours pas à le faire fonctionner.
                    Je suis sous Joomla 5.1.0 ; php 8.2.20, et j'utilise LM-Custom pour intégrer du code php.

                    Du coup, je suis gêné, puisque vous l'avez testé et qu'il fonctionne! Comment le faites-vous tourner? En appelant un fichier php? placé où? Ou avec une autre méthode?

                    Commentaire


                    • #11
                      Bonjour,

                      Je l'ai testé simplement en le mettant dans le code de la template admin atum pour tester, je ne connait pas LM-Custom mais je doute qu'il gère les name space, dans mon cas c'est directement dans un fichier php

                      EDIT: j'avauis oublié de mettre un apostrophe j'ai corrigé mon message précédent
                      Dernière édition par Nioupixel à 18/06/2024, 11h45

                      Commentaire


                      • #12
                        Et bien me voilà triplement embêté parce qu'aucune de mes tentatives ne fonctionne :
                        • Je copie-colle votre code dans un fichier *****.php, je place ce fichier dans /administrator/templates/atum/****.php, j'appelle le fichier en rentrant son url, après connexion à l'administration : rien ne se passe.
                        En ajoutant un 'echo("tout va bien");' balladeur, je constate que le script cesse de fonctionner dès l'instruction $app = Factory::getApplication();
                        • Je n'arrive pas (je l'ai déjà dit) à faire fonctionner le script grâce à LM-Custom. J'ai une erreur "unexpected token "try""
                        • LM-Custom me permet pourtant de faire tourner tous les scripts php que j'ai écrits, en utilisant en particulier les règles glanées dans la doc Joomla pour chercher, insérer ou modifier un enregistrement d'une table.
                        Par exemple, ce genre de code fontionne parfaitement avec LM-Custom :

                        Code PHP:
                        $db JFactory::getDbo();
                        $query $db
                        ->getQuery(true)
                        ->
                        select('COUNT(*)')
                        ->
                        from($db->quoteName('#__vacances_scolaires'))
                        ->
                        where($db->quoteName('debut_vacances') . " <= " $db->quote($debut_date), 'AND')
                        ->
                        where($db->quoteName('fin_vacances') . " >= " $db->quote($debut_date));
                        $db->setQuery($query);
                        $count $db->loadResult();

                        if ((
                        $count) > 0) {
                        echo 
                        "Vacances scolaires : Enregistrement annulé<br>";
                        } else {
                        $query $db->getQuery(true);
                        $colonnes = array('date_time''titre''classe''heure_debut''heure_fin''travail_a_faire_booleen''publie');
                        $valeurs = array($db->quote($actuel), $db->quote($titre), $db->quote($classe), $db->quote($debut_revu), $db->quote($fin_revue), $db->quote('0'),$db->quote('0'));
                        $query
                        ->insert($db->quoteName('#__agenda'))
                        ->
                        columns($db->quoteName($colonnes))
                        ->
                        values(implode(','$valeurs));
                        $db->setQuery($query);
                        $db->execute();
                        echo 
                        "Nouvel enregistrement créé avec succès<br>";


                        Certes, il n'y a pas de "Joomla\Component\Users\Administrator\Model\GroupMo del​ "
                        Dernière édition par jeejeeboy à 18/06/2024, 14h17

                        Commentaire


                        • #13
                          Après si vous voulez une solution sans les namespaces c'est possible mais ça sera un peu moins propre par contre

                          Après le code simplifier au max actuel donnerait ça:
                          Code PHP:
                          use Joomla\Component\Users\Administrator\Model\GroupModel;

                          $groupModel = new GroupModel();

                          // Définir les données du nouveau groupe
                          $data = array(
                          'id' => 0,
                          'title' => 'Nom du nouveau groupe test'// Nom du groupe
                          'parent_id' => // ID du groupe parent
                          );

                          $groupModel->save($data); 
                          EDIT: la version moins propre sans namespace::

                          Code PHP:
                          function getParentInfo($parentId) {
                              
                          $db JFactory::getDbo();
                              
                          $query $db->getQuery(true)
                                          ->
                          select('id, lft, rgt')
                                          ->
                          from($db->quoteName('#__usergroups'))
                                          ->
                          where($db->quoteName('id') . ' = ' . (int) $parentId);

                              
                          $db->setQuery($query);
                              return 
                          $db->loadObject();
                          }

                          function 
                          insertGroup($title$parentInfo) {
                              
                          $db JFactory::getDbo();

                              
                          // Calcul des nouvelles valeurs lft et rgt
                              
                          $newLft $parentInfo->rgt;
                              
                          $newRgt $parentInfo->rgt 1;

                              
                          // Mise à jour des autres noeuds
                              
                          $query $db->getQuery(true)
                                          ->
                          update($db->quoteName('#__usergroups'))
                                          ->
                          set($db->quoteName('rgt') . ' = ' $db->quoteName('rgt') . ' + 2')
                                          ->
                          where($db->quoteName('rgt') . ' >= ' . (int) $parentInfo->rgt);
                              
                          $db->setQuery($query);
                              
                          $db->execute();

                              
                          $query $db->getQuery(true)
                                          ->
                          update($db->quoteName('#__usergroups'))
                                          ->
                          set($db->quoteName('lft') . ' = ' $db->quoteName('lft') . ' + 2')
                                          ->
                          where($db->quoteName('lft') . ' > ' . (int) $parentInfo->rgt);
                              
                          $db->setQuery($query);
                              
                          $db->execute();

                              
                          // Insertion du nouveau groupe
                              
                          $columns = array('parent_id''lft''rgt''title');
                              
                          $values = array(
                                  (int) 
                          $parentInfo->id,
                                  (int) 
                          $newLft,
                                  (int) 
                          $newRgt,
                                  
                          $db->quote($title)
                              );

                              
                          $query $db->getQuery(true)
                                          ->
                          insert($db->quoteName('#__usergroups'))
                                          ->
                          columns($db->quoteName($columns))
                                          ->
                          values(implode(','$values));
                              
                              
                          $db->setQuery($query);
                              
                          $db->execute();

                              return 
                          $db->insertid();
                          }

                          $parentId 2// ID du groupe parent
                          $title "Nouveau Groupe";

                          $parentInfo getParentInfo($parentId);
                          if (
                          $parentInfo) {
                              
                          $newGroupId insertGroup($title$parentInfo);
                              echo 
                          "Nouveau groupe créé avec ID: " $newGroupId;
                          } else {
                              echo 
                          "Le groupe parent n'existe pas.";
                          }



                          ​ 
                          Dernière édition par Nioupixel à 18/06/2024, 16h27

                          Commentaire


                          • #14
                            Je dois dire que je ne vois pas bien quel petit changement s'est produit entre le code "entier" du message #9 et le code "simplifié" du message #13. Ça ne fonctionnait pas, et maintenant ÇA FONCTIONNE À MERVEILLE.
                            Trois constats :
                            • La version "moins propre sans namespace" est celle que j'allais finir par coder en désespoir de cause. J'avoue ne pas l'avoir testée, mais je constate que vous codez plus efficacement que moi...
                            • Le code "propre et simplifié" fonctionne parfaitement.
                            • LM-Custom intégre parfaitement (j'en étais sûr...) ce code avec namespace.
                            En conclusion, Nioupixel un énorme merci pour cette aide précieuse, rapide et efficace.
                            Sujet résolu avec brio
                            Dernière édition par jeejeeboy à 19/06/2024, 14h32

                            Commentaire


                            • #15
                              AJOUT :
                              Pour supprimer un groupe (adaptable à toute table de données 'nested'), on peut utiliser le code suivant :

                              Code PHP:
                              //Utiliser le modèle Joomla de gestion des groupes
                              use Joomla\Component\Users\Administrator\Model\GroupModel;
                              //initialiser
                              $groupModel = new GroupModel();

                              //supprimer le groupe
                              $groupModel->delete($id_groupe); 
                              Source : Documentation Joomla : https://docs.joomla.org/Using_nested_sets

                              Commentaire

                              Annonce

                              Réduire
                              Aucune annonce pour le moment.

                              Partenaire de l'association

                              Réduire

                              Hébergeur Web PlanetHoster
                              Travaille ...
                              X