Menu déroulant

Fermé
lina_paradis Messages postés 4 Date d'inscription jeudi 17 mai 2018 Statut Membre Dernière intervention 17 mai 2018 - Modifié le 17 mai 2018 à 18:00
jordane45 Messages postés 38201 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 5 juin 2024 - 17 mai 2018 à 23:44
Bonjour,

J'ai suivi un poste sur le Forum comment ça marche j'étais attirer par un petite script de
menu dynamique
En voulant l'intégrer sur mon site j'ai rencontré quelques problèmes quand je crée une sous-rubriques la barre horizontal de menu principal se replie sur elle-même et toute le menu se reconstruit en verticale:( est-ce que vous pouvez m'aider s'il vous plait.



<?php
// Lien vers notre base de données Connexion Script & Exécuter Fonction
doDB();
// Lancer : Construire haut niveau
$top_sql = "SELECT * FROM menu_top_level WHERE top_level_visible = 'Yes' ORDER BY 
top_level_order ASC"; // Créer une requête de base de données
$top_res = mysqli_query($mysqli, $top_sql) or die(mysqli_error($mysqli)); // Vérifier la connexion et d'exécuter la requête
if ($top_res = mysqli_query($mysqli, $top_sql)) { // Si la requête contient les résultats ...
while ($row = mysqli_fetch_array($top_res)) {
// Boucle à travers chaque ligne donnée
if( isset($row['top_level_url'])){
$urlTmp =($row['top_level_url']);
$arrTmp = explode('/',$urlTmp);
$id = $arrTmp[count($arrTmp)-1];
if($id){ 
$URL = "index.php?pg=pages&id=".$id;
}else{
$URL ='#' ;
}
}else{
$URL ='#' ;
}
@$menu_block .= "<li class='dropdown'><a href='".$URL."' class='dropdown-toggle'>".$row['top_level_name']."</a>";
// Lancer : Construire mi-niveau
$mid_sql = "SELECT * FROM menu_mid_level WHERE mid_level_fk_id = $row[top_level_pk_id] AND 
mid_level_visible = 'Yes' ORDER BY mid_level_order ASC"; // Créer une requête de base de données
$mid_res = mysqli_query($mysqli, $mid_sql) or die(mysqli_error($mysqli));
$mid_num_rows = mysqli_num_rows($mid_res); // Obtenir le numéro de la ligne
$mid_num_rows_constant = $mid_num_rows; // Nombre de boutique de ligne comme une valeur
$mid_num_rows_counter = 0; // Correspond à l'encontre de constante en ajoutant 1 chaque boucle ( ligne 32)
if ($mid_num_rows == 0) { // Moins si le nombre de lignes est égal à 0
$menu_block .= "</li>"; // Proche LI
} else if ($mid_num_rows > 0) { // Sinon, si plus de 0 ...
$menu_block .= "<ul class='dropdown-menu'>"; // UL ouvert pour venir rangées LI
}
if ($mid_res = mysqli_query($mysqli, $mid_sql)) { // Si la requête contient les résultats ...
while ($row = mysqli_fetch_array($mid_res)) { // Boucle sur chaque ligne donnée
if( isset($row['mid_level_url'])){
$urlTmp =($row['mid_level_url']);
$arrTmp = explode('/',$urlTmp);
$id = $arrTmp[count($arrTmp)-1];
if($id){ 
$URL = "index.php?pg=pages&id=".$id;
}else{
$URL ='#' ;
}
}else{
$URL ='#' ;
}
$menu_block .= "<li class='dropdown-submenu'><a href='".$URL."'>".$row['mid_level_name']."</a>";
$mid_num_rows_counter = $mid_num_rows_counter + 1; // Compter rangée de marques comme géré ( ligne 60 )
// Lancer : Construire niveau bot
$bot_sql = "SELECT * FROM menu_bot_level WHERE bot_level_fkt_id = $row[mid_level_fk_id] 
&& bot_level_fkm_id = $row[mid_level_order] /* AND bot_level_visible = 'Yes' */ 
ORDER BY bot_level_order ASC"; // Créer une requête de base de données
$bot_res = mysqli_query($mysqli, $bot_sql) or die(mysqli_error($mysqli));
$bot_num_rows = mysqli_num_rows($bot_res); // Vérifie nombre de lignes
if ($bot_num_rows == 0) { // Si aucun rangées intérieures ...
$menu_block .= "</li>"; // Clôture au-dessus LI
} else if ($bot_num_rows > 0) { // Sinon si plus de 0 , il ne contient ...
$menu_block .= "<ul class='dropdown-menu'>"; // Si ouvert UL pour contenir venir LI
}
if ($bot_res = @mysqli_query($mysqli, $bot_sql)) { // Si la requête contient les résultats ...
while ($row = mysqli_fetch_array($bot_res)) { // Boucle sur chaque ligne donnée
$menu_block .= "<li><a href='index.php?pg=pages&id=$row[bot_level_url]'>".$row['bot_level_name']."</a>
</li>"; // Et le résultat de rangée de sortie
$bot_num_rows = $bot_num_rows - 1; // Continuer à compter en arrière une boucle jusqu'à ce que chaque 
if ($bot_num_rows == 0) { // Vérifier ce est exactement 0 ...
$menu_block .= "</ul></li>"; // Ligne a terminé si près UL et LI
}
}
} // Fin: Construire niveau bot
}
} // Fin: Construire mi-niveau
// Si atteint dernière ligne & condition qu'il n'y ait plus de 1 rang , puis fermez UL et LI
if ($mid_num_rows_counter == $mid_num_rows_constant && $mid_num_rows_counter > 1) {
$menu_block .= "</ul></li>";
}
}
} // Fin: Construire haut niveau
// Fermer Connexion à MySQL
mysqli_close($mysqli);
// Output the menu we have built above
echo $menu_block;
?>


A voir également:

2 réponses

jordane45 Messages postés 38201 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 5 juin 2024 4 675
17 mai 2018 à 18:33
Bonjour,

Commence par regarder le code HTML généré ( pour ça tu affiches ta page sur ton navigateur, puis tu fais CTRL+U )
A mon avis, tu dois avec des balises mal fermées.

Bon.. et puis... avoir plusieurs tables pour gérer les différents niveaux de menu....pffff... c'est ( de mon avis..) sacrément brouillon.

Je pense que le mieux est de jouer avec, à minima, une seule table, et de mettre pour chaque item l'id de son parent. (il suffit ensuite de faire du récursif pour obtenir l'arborescence du menu)

Ou mieux... jouer avec la gestion d'arbre intervallaire. (c'est l'idéal pour gérer des listes multi-nivaux). (un peu plus complexe à mettre en œuvre quand on a pas les notions... mais tellement plus simple à manipuler par la suite)...



0
lina_paradis Messages postés 4 Date d'inscription jeudi 17 mai 2018 Statut Membre Dernière intervention 17 mai 2018
17 mai 2018 à 21:51
trop aimable de votre part si vous pouvez me donner un petit model a copier
0
jordane45 Messages postés 38201 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 5 juin 2024 4 675
Modifié le 17 mai 2018 à 22:06
Je suis actuellement en train de bosser sur le sujet....

Voici de quoi débuter :
Deux fichiers "class"
<?php
/*
* Fichier db.class.php
* -> Sert à manipuler la bdd en PDO
*/
class db {
  private $bdd = null;
  private $prepare = NULL;
  private $res = NULL;
  private $host = "localhost";
  private $user = "root";
  private $pwd = "root";
  private $port = "3306";
  private $dbname ="bdd_arbre";
  
  
  /**
  Constructeur
  */
  function __construct($cnxDatas=NULL){
    if(!empty($cnxDatas)){
      if(is_array($cnxDatas)){
        foreach($cnxDatas as $key => $value){
          $propName = strtolower($key);
          if(property_exists('db',$propName)){
            $this->{$propName} = $value;
          }
        }
      }
    } 
    $this->cnx();
  }
  
  /**
  * connexion à la BDD
  */
  private function cnx(){
    try{
      $bdd =new PDO('mysql:host='.$this->host.';dbname='.$this->dbname.'; charset=utf8;port='.$this->port, $this->user, $this->pwd);
      // Activation des erreurs PDO
       $bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      // mode de fetch par défaut : FETCH_ASSOC / FETCH_OBJ / FETCH_BOTH
       $bdd->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    } catch(PDOException $e) {
        die('Erreur : ' . $e->getMessage());
    }
    $this->bdd = $bdd;
  }
  
  /**
  * Execute une requête SQL
  */
  private function dbQuery($sql,$datas=NULL){
    try{
      $this->prepare = $this->bdd->prepare($sql) ;
      $this->res = $this->prepare->execute($datas) ;
    }catch(Exception $e){
      // en cas d'erreur :
       echo " Erreur ! ".$e->getMessage();
       echo " Les datas : " ;
       print_r($datas);
    }
  }
  
  public function db_All($sql,$datas=NULL){
    $this->dbQuery($sql,$datas);
    return $this->prepare->fetchAll(); 
  }
  
  public function db_One($sql,$datas=NULL){
    $this->dbQuery($sql,$datas);
    return $this->prepare->fetch(); 
  }
  
  public function db_Insert($sql,$datas=NULL,$returnId=TRUE){
    $this->dbQuery($sql,$datas);   
    return $returnId ? $this->bdd->lastInsertId() : $this->res; 
  }
  
  public function db_Exec($sql,$datas=NULL){
    $this->dbQuery($sql,$datas);   
    return array('result'=>$this->res,'rowCount'=>$this->prepare->rowCount());
  }
  
    
    
  public function lockTable($tbl){
    // lock table to prevent other sessions from modifying the data and thus preserving data integrity
    $sql = 'LOCK TABLE `' . $tbl . '` WRITE';
    $this->db_Exec($sql);
  }
  
  public function unLockTables(){
    // lock table to prevent other sessions from modifying the data and thus preserving data integrity
    $sql = 'UNLOCK TABLES';
    $this->db_Exec($sql);
  }
    
}


La class arbre
<?php
/**
* Fichier arbre.class.php
* Permet de gérer un arbre
*
* Require db.class.php
*

-------------------------------
METHODS :
-------------------------------
__construct()
add()
copy()
delete()
get_descendants()
get_descendant_count()
get_next_sibling()
get_parent()
get_path()
get_previous_sibling()
get_siblings()
get_tree()
move()
to_list()
to_select()
update()

*/


class arbre extends db{
  private $tbl = 'arbre';
    private $col_id = 'id';
  private $col_uid = 'uid';
  private $col_title = 'title';
  private $cg = 'gauche';
  private $cd = 'droite';
  private $col_parent = 'parent';
  private $lookup = array();
  
  
  function __construct($properties=NULL){
    parent::__construct();
    $this->setProperties($properties);
  }

  private function setProperties($properties=NULL){
    if(!empty($properties) && is_array($properties)){
      foreach($properties as $key => $value){
        $propName = strtolower($key);
        if( !empty($propName) && property_exists('arbre',$propName) ){
          $this->{$propName} = $value;
        }
      }
    } 
  }

  
  public function get_FullTree(){
    $sql = "SELECT *
            FROM ". $this->tbl ."
            ORDER BY ". $this->cg ."";
            
    return parent::db_all($sql);
  }
  
  private function _init(){
    if(empty($this->lookup)){
      $allTree = $this->get_FullTree();
      if(!empty($allTree)){
        foreach($allTree as $R){
          $this->lookup[$R[$this->col_id]] = $R;  
        }
      }  
    }
  }
  
  /**
  * Updates the lookup array after inserts and deletes.
  */
  private function _reorder_lookup_array() {
    $tmp = array();
    foreach ($this->lookup as $properties){        
      ${$this->cg}[] = $properties[$this->cg];
    }    
    array_multisort(${$this->cg}, SORT_ASC, $this->lookup);
    foreach ($this->lookup as $properties){
        $tmp[$properties[$this->col_id]] = $properties;
    }
    $this->lookup = $tmp;
    unset($tmp);
 }

    public function add($parent, $title, $position = false) {
        
        $this->_init();
        // make sure parent ID is an integer
        $parent = (int)$parent;
        // continue only if
        if (
            // we are adding a topmost node OR
            $parent == 0 ||
            // parent node exists in the lookup array
            isset($this->lookup[$parent])
        ) {
            // get parent's descendant nodes (no deeper than the first level)
            $descendants = $this->get_descendants($parent);
            // if node is to be inserted in the default position (as the last of the parent node's children)
            // give a numerical value to the position
            if ($position === false) $position = count($descendants);
            // if a custom position was specified
            else {
                // make sure that position is an integer value
                $position = (int)$position;
                // if position is a bogus number
                // use the default position (as the last of the parent node's children)
                if ($position > count($descendants) || $position < 0) $position = count($descendants);
            }
            // if parent has no descendants OR the node is to be inserted as the parent node's first child
            if (empty($descendants) || $position == 0)
                // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
                // the insert, and will need to be updated
                // if parent is not found (meaning that we're inserting a topmost node) set the boundary to 0
                $boundary = isset($this->lookup[$parent]) ? $this->lookup[$parent][$this->cg] : 0;
            // if parent node has descendant nodes and/or the node needs to be inserted at a specific position
            else {
                // find the child node that currently exists at the position where the new node needs to be inserted to
                $slice = array_slice($descendants, $position - 1, 1);
                $descendants = array_shift($slice);
                // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
                // the insert, and will need to be updated
                $boundary = $descendants[$this->cd];
            }
            // iterate through all the records in the lookup array
            foreach ($this->lookup as $id => $properties) {
                // if the node's "left" value is outside the boundary
                if ($properties[$this->cg] > $boundary)
                    // increment it with 2
                    $this->lookup[$id][$this->cg] += 2;
                // if the node's "right" value is outside the boundary
                if ($properties[$this->cd] > $boundary)
                    // increment it with 2
                    $this->lookup[$id][$this->cd] += 2;
            }
            // lock table to prevent other sessions from modifying the data and thus preserving data integrity
            parent::lockTable($this->tbl);
            // update the nodes in the database having their "left"/"right" values outside the boundary
            $this->updateBorne($this->cg,2,$boundary);
            $this->updateBorne($this->cd,2,$boundary);
                        
            // insert the new node into the database
            $nodesDatas = array();
            $nodesDatas[$this->col_title] = $title; 
            $nodesDatas[$this->cg] = $boundary + 1;
            $nodesDatas[$this->cd] = $boundary + 2;
            $nodesDatas[$this->col_parent] = $parent;     
            $nodesDatas[$this->col_uid] = $boundary + 2;
            $node = $this->insertNode($nodesDatas);
            $node_id = $node['id'];
            $node_uid = $node['uid'];
            // release table lock
            parent::unlockTables();
            // add the node to the lookup array
            $this->lookup[$node_id] = array(
                $this->col_id      => $node_id,
                $this->col_uid      => $node_uid,
                $this->col_title   => $title,
                $this->cg    => $boundary + 1,
                $this->cd   => $boundary + 2,
                $this->col_parent  => $parent,
            );
            // reorder the lookup array
            $this->_reorder_lookup_array();
            // return the ID of the newly inserted node
            return array('id'=>$node_id,'uid'=>$node_uid);
        }
        
        return false;
    }
    /**


*  Creates a copy of a node (including its descendant nodes) as the child node of a given node.
*  @return mixed                   Returns the ID of the newly created copy, or FALSE on error.
*/
    public function copy($source, $target, $position = false) {
        
        $this->_init();
        // continue only if
        if (
            // source node exists in the lookup array AND
            isset($this->lookup[$source]) &&
            // target node exists in the lookup array OR is 0 (indicating a topmost node)
            (isset($this->lookup[$target]) || $target == 0)
        ) {
            // get the source's children nodes (if any)
            $source_children = $this->get_descendants($source, false);
            // this array will hold the items we need to copy
            // by default we add the source item to it
            $sources = array($this->lookup[$source]);
            // the copy's parent will be the target node
            $sources[0][$this->col_parent] = $target;
            // iterate through source node's children
            foreach ($source_children as $child)
                // save them for later use
                $sources[] = $this->lookup[$child[$this->col_id]];
            // the value with which items outside the boundary set below, are to be updated with
            $source_rl_difference =
                $this->lookup[$source][$this->cd] -
                $this->lookup[$source][$this->cg]
                + 1;
            // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
            // the insert, and will need to be updated
            $source_boundary = $this->lookup[$source][$this->cg];
            // get target node's children (no deeper than the first level)
            $target_children = $this->get_descendants($target);
            // if copy is to be inserted in the default position (as the last of the target node's children)
            if ($position === false)
                // give a numerical value to the position
                $position = count($target_children);
            // if a custom position was specified
            else {
                // make sure given position is an integer value
                $position = (int)$position;
                // if position is a bogus number
                if ($position > count($target_children) || $position < 0)
                    // use the default position (the last of the target node's children)
                    $position = count($target_children);
            }
            // we are about to do an insert and some nodes need to be updated first
            // if target has no children nodes OR the copy is to be inserted as the target node's first child node
            if (empty($target_children) || $position == 0)
                // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
                // the insert, and will need to be updated
                // if parent is not found (meaning that we're inserting a topmost node) set the boundary to 0
                $target_boundary = isset($this->lookup[$target]) ? $this->lookup[$target][$this->cg] : 0;
            // if target has children nodes and/or the copy needs to be inserted at a specific position
            else {
                // find the target's child node that currently exists at the position where the new node needs to be inserted to
                $slice = array_slice($target_children, $position - 1, 1);
                $target_children = array_shift($slice);
                // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
                // the insert, and will need to be updated
                $target_boundary = $target_children[$this->cd];
            }
            // iterate through the nodes in the lookup array
            foreach ($this->lookup as $id => $properties) {
                // if the "left" value of node is outside the boundary
                if ($properties[$this->cg] > $target_boundary)
                    // increment it
                    $this->lookup[$id][$this->cg] += $source_rl_difference;
                // if the "right" value of node is outside the boundary
                if ($properties[$this->cd] > $target_boundary)
                    // increment it
                    $this->lookup[$id][$this->cd] += $source_rl_difference;
            }
            // lock table to prevent other sessions from modifying the data and thus preserving data integrity
            parent::lockTable($this->tbl);
            
            // update the nodes in the database having their "left"/"right" values outside the boundary
            $this->updateBorne($this->cg ,$source_rl_difference,$target_boundary);
            $this->updateBorne($this->cd ,$source_rl_difference,$target_boundary);
            
            // finally, the nodes that are to be inserted need to have their "left" and "right" values updated
            $shift = $target_boundary - $source_boundary + 1;
            // iterate through the nodes that are to be inserted
            foreach ($sources as $id => &$properties) {
                // update "left" value
                $properties[$this->cg] += $shift;
                // update "right" value
                $properties[$this->cd] += $shift;
                $node = $this->insertNode($properties);
                $node_id = $node['id'];
                                
                // because the node may have children nodes and its ID just changed
                // we need to find its children and update the reference to the parent ID
                foreach ($sources as $key => $value){
                  if ($value[$this->col_parent] == $properties[$this->col_id]){
                    $sources[$key][$this->col_parent] = $node_id;
                  }
                }
                // update the node's properties with the ID
                $properties[$this->col_id] = $node_id;
                // update the array of inserted items
                $sources[$id] = $properties;
            }
            // a reference of a $properties and the last array element remain even after the foreach loop
            // we have to destroy it
            unset($properties);
            // release table lock
            parent::unlockTables();
            // at this point, we have the nodes in the database but we need to also update the lookup array
            $parents = array();
            // iterate through the inserted nodes
            foreach ($sources as $id => $properties) {
                // if the node has any parents
                if (count($parents) > 0)
                    // iterate through the array of parent nodes
                    while ($parents[count($parents) - 1]['right'] < $properties[$this->cd])
                        // and remove those which are not parents of the current node
                        array_pop($parents);
                // if there are any parents left
                if (count($parents) > 0)
                    // the last node in the $parents array is the current node's parent
                    $properties[$this->col_parent] = $parents[count($parents) - 1]['id'];
                // update the lookup array
                $this->lookup[$properties[$this->col_id]] = $properties;
                // add current node to the stack
                $parents[] = array(
                    'id'    =>  $properties[$this->col_id],
                    'right' =>  $properties[$this->cd]
                );
            }
            // reorder the lookup array
            $this->_reorder_lookup_array();
            // return the ID of the copy
            return $sources[0][$this->col_id];
        }
        // if scripts gets this far, return false as something must've went wrong
        return false;
    }
    
    
    
  private function updateBorne($borne = 'cd',$source_rl_difference,$target_boundary,$aom='+',$compare='>'){
   $sql = 'UPDATE  `' . $this->tbl . '`
              SET  `' . $borne . '` = `' . $borne . '` '.$aom.' ' . $source_rl_difference . '
            WHERE  `' . $borne . '` '.$compare.' ' . $target_boundary . ';';
    parent::db_Exec($sql);
  }
  
  private function insertNode($properties=array()){
    $sql = 'INSERT INTO `' . $this->tbl . '`(`' . $this->col_uid . '`,`' . $this->col_title . '`,`' . $this->cg . '`, `' . $this->cd . '`, `' . $this->col_parent . '`) VALUES (:uid,:title,:cg,:cd,:parent)';
    $uid = uniqid('ai',false);
    $datas = array(':uid'=>$uid,
                   ':title'=> $properties[$this->col_title], 
                   ':cg'=> $properties[$this->cg],
                   ':cd'=> $properties[$this->cd],
                   ':parent'=> $properties[$this->col_parent]
                  );
    
    return array('id'=>parent::db_Insert($sql,$datas, TRUE),'uid'=>$uid);
  }
  
  /**


* Deletes a node, including the node's descendant nodes.

*/
  public function delete($node = 0) {
    $this->_init();
    if (isset($this->lookup[$node])) {
        // get target node's descendant nodes (if any)
        $descendants = $this->get_descendants($node, false);
        // iterate through target node's descendant nodes
        foreach ($descendants as $descendant){
            // remove node from the lookup array
            unset($this->lookup[$descendant[$this->col_id]]);
        }
        //lock table
        parent::lockTable($this->tbl);
        
        //Delete nodes from the database
        $sql = "DELETE  FROM " . $this->tbl ."
                WHERE ". $this->cg . ' >= ' . $this->lookup[$node][$this->cg] . "
                AND ". $this->cd . " <= " . $this->lookup[$node][$this->cd] ;
       parent::db_Exec($sql);
       
        // the value with which items outside the boundary set below, are to be updated with
        $target_rl_difference =  $this->lookup[$node][$this->cd] - $this->lookup[$node][$this->cg] + 1;
        
        // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
        // the insert, and will need to be updated
        $boundary = $this->lookup[$node][$this->cg];
        // remove the target node from the lookup array
        unset($this->lookup[$node]);
        // iterate through nodes in the lookup array
        foreach ($this->lookup as $id => $properties) {
            // if the "left" value of node is outside the boundary
            if ($this->lookup[$id][$this->cg] > $boundary){
                // decrement it
                $this->lookup[$id][$this->cg] -= $target_rl_difference;
            }
            // if the "right" value of node is outside the boundary
            if ($this->lookup[$id][$this->cd] > $boundary){
                // decrement it
                $this->lookup[$id][$this->cd] -= $target_rl_difference;
            }
        }
        
        // update the nodes in the database having their "left"/"right" values outside the boundary
        $this->updateBorne($this->cg,$target_rl_difference,$boundary,'-');        
        $this->updateBorne($this->cd,$target_rl_difference,$boundary,'-');
                
        // release table lock
         parent::unLockTables();
         
        // return true as everything went well
        return true;
    }
    
    return false;
  }
  
  /**


*  Returns an unidimensional (flat) array with the descendant nodes of a given parent node.

*/
  public function get_descendants($node = 0, $direct_descendants_only = true) {
    $this->_init();
    if (isset($this->lookup[$node]) || $node === 0) {
      $descendants = array();
      $keys = array_keys($this->lookup);
      foreach ($keys as $item){
        if ( $this->lookup[$item][$this->cg] > ($node !== 0 ? $this->lookup[$node][$this->cg] : 0) &&
             $this->lookup[$item][$this->cg] < ($node !== 0 ? $this->lookup[$node][$this->cd] : PHP_INT_MAX) &&
             (!$direct_descendants_only || $this->lookup[$item][$this->col_parent] == $node)
        ){
          $descendants[$this->lookup[$item][$this->col_id]] = $this->lookup[$item];
        }
      }
      return $descendants;
    }
    return false;
  }
  
  
  /**


* Returns the number of descendant nodes of a given node.

*/
  public function get_descendant_count($node=0, $direct_descendants_only = true) {
    $this->_init();
    if (isset($this->lookup[$node])){
      if (!$direct_descendants_only){
        return ($this->lookup[$node][$this->cd] - $this->lookup[$node][$this->cg] - 1) / 2;
      }else {
        $result = 0;
        foreach ($this->lookup as $id => $properties){
          if ($this->lookup[$id][$this->col_parent] == $node){
            $result++;
          }
        }
        return $result;
      }
    }
    // if script gets this far, return false as something must've went wrong
    return false;
  }
  
  
  /**


* Returns the next sibling of a node.

*/
  public function get_next_sibling($node = 0) {
    if ($siblings = $this->get_siblings($node, true)) {
      $node_position = array_search($node, array_keys($siblings));
      $sibling = array_slice($siblings, $node_position + 1, 1);
      return !empty($sibling) ? array_pop($sibling) : 0;
    }
    return false;
  }
  
  /**


*  Returns an array containing a node's direct parent node if the node has a parent node, or "0" if the node is a

*  topmost node.

*/
  public function get_parent($node = 0) {
    $this->_init();
    if (isset($this->lookup[$node])){
        return isset($this->lookup[$this->lookup[$node][$this->col_parent]]) ? $this->lookup[$this->lookup[$node][$this->col_parent]] : 0;
    }    
    return false;
  }
  
  
  /**


* Returns an unidimensional (flat) array with the path to the given node (including the node itself).

*/
  public function get_path($node = 0) {
    $this->_init();
    $parents = array();
    // if node exists in the lookup array
    if (isset($this->lookup[$node])){
      foreach ($this->lookup as $id => $properties){
        if ($properties[$this->cg] < $this->lookup[$node][$this->cg] && $properties[$this->cd] > $this->lookup[$node][$this->cd] ) {
          $parents[$properties[$this->col_id]] = $properties;
        }
      }
      // add also the node given as argument
      $parents[$node] = $this->lookup[$node];
    }
    // return the path to the node
    return $parents;
  }
  
  /**


*  Returns the previous sibling of a node.

*/
  public function get_previous_sibling($node = 0) {
    if ($siblings = $this->get_siblings($node, true)) {    
      $node_position = array_search($node, array_keys($siblings));
      $sibling = $node_position > 0 ? array_slice($siblings, $node_position - 1, 1) : array();
      return !empty($sibling) ? array_pop($sibling) : 0;
    }
    return false;
  }
  
  
  /**


* Returns an array with a node's sibling nodes

*/
  public function get_siblings($node, $include_self = false) {   
    if (isset($this->lookup[$node])) {
      $properties = $this->lookup[$node];
      $siblings = $this->get_descendants($properties['parent']);
      if (!$include_self) unset($siblings[$node]);
      return $siblings;
    }
    return false;
  }
  

  /**


*  Returns a multidimensional array with all the descendant nodes (including children nodes of children nodes of

*  children nodes and so on) of a given node.

*  @return array

*/
  public function get_tree($node = 0) {
    $descendants = $this->get_descendants($node);
    foreach ($descendants as $id => $properties){
      $descendants[$id]['children'] = $this->get_tree($id);
    }
    return $descendants;
  }
  
  
  /**


* move node with descendants

*/
  public function move($source, $target, $position = false) {
    $this->_init();
    // continue only if
    if (
        // source node exists in the lookup array AND
        isset($this->lookup[$source]) &&
        // target node exists in the lookup array OR is 0 (indicating a topmost node)
        (isset($this->lookup[$target]) || $target == 0) &&
        // target node is not a child node of the source node (that would cause infinite loop)
        !in_array($target, array_keys($this->get_descendants($source, false)))
    ) {
        // if we have to move the node after/before another node
        if ($position === 'after' || $position === 'before') {
            // get the target's parent node
            $target_parent = $target == 0 ? 0 : $this->lookup[$target]['parent'];
            // get the target's parent's descendant nodes
            $descendants = $this->get_descendants($target_parent);
            // get the target's position among the descendants
            $keys = array_keys($descendants);
            $target_position = array_search($target, $keys);
            // move the source node to the desired position
            if ($position == 'after') return $this->move($source, $target_parent, $target_position + 1);
            else return $this->move($source, $target_parent, $target_position == 0 ? 0 : $target_position - 1);
        }
        // the source's parent node's ID becomes the target node's ID
        $this->lookup[$source][$this->col_parent] = $target;
        // get source node's descendant nodes (if any)
        $source_descendants = $this->get_descendants($source, false);
        // this array will hold the nodes we need to move
        // by default we add the source node to it
        $sources = array($this->lookup[$source]);
        // iterate through source node's descendants
        foreach ($source_descendants as $descendant) {
            // save them for later use
            $sources[] = $this->lookup[$descendant[$this->col_id]];
            // for now, remove them from the lookup array
            unset($this->lookup[$descendant[$this->col_id]]);
        }
        // the value with which nodes outside the boundary set below, are to be updated with
        $source_rl_difference =
            $this->lookup[$source][$this->cd] -
            $this->lookup[$source][$this->cg]
            + 1;
        // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
        // the insert, and will need to be updated
        $source_boundary = $this->lookup[$source][$this->cg];
        // lock table to prevent other sessions from modifying the data and thus preserving data integrity
        parent::lockTable($this->tbl);
        // we'll multiply the "left" and "right" values of the nodes we're about to move with "-1", in order to
        // prevent the values being changed further in the script
        
        $sql = 'UPDATE `' . $this->tbl . '`
                   SET `' . $this->cg . '` = `' . $this->cg . '` * -1,
                       `' . $this->cd . '` = `' . $this->cd . '` * -1
                WHERE  `' . $this->cg . '` >= ' . $this->lookup[$source][$this->cg] . ' 
                  AND  `' . $this->cd . '` <= ' . $this->lookup[$source][$this->cd] . '';    
        parent::db_Exec($sql);
          
        // remove the source node from the list
        unset($this->lookup[$source]);
        // iterate through the remaining nodes in the lookup array
        foreach ($this->lookup as $id=>$properties) {
            // if the "left" value of node is outside the boundary
            if ($this->lookup[$id][$this->cg] > $source_boundary)
                // decrement it
                $this->lookup[$id][$this->cg] -= $source_rl_difference;
            // if the "right" value of item is outside the boundary
            if ($this->lookup[$id][$this->cd] > $source_boundary)
                // decrement it
                $this->lookup[$id][$this->cd] -= $source_rl_difference;
        }
        // update the nodes in the database having their "left"/"right" values outside the boundary
        $this->updateBorne($this->cg,$source_rl_difference,$source_boundary,'-');        
        $this->updateBorne($this->cd,$source_rl_difference,$source_boundary,'-');
       
        // get descendant nodes of target node (first level only)
        $target_descendants = $this->get_descendants((int)$target);
        // if node is to be inserted in the default position (as the last of target node's children nodes)
        // give a numerical value to the position
        if ($position === false) $position = count($target_descendants);
        // if a custom position was specified
        else {
            // make sure given position is an integer value
            $position = (int)$position;
            // if position is a bogus number
            if ($position > count($target_descendants) || $position < 0)
                // use the default position (as the last of the target node's children)
                $position = count($target_descendants);
        }
        // because of the insert, some nodes need to have their "left" and/or "right" values adjusted
        // if target node has no descendant nodes OR the node is to be inserted as the target node's first child node
        if (empty($target_descendants) || $position == 0)
            // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
            // the insert, and will need to be updated
            // if parent is not found (meaning that we're inserting a topmost node) set the boundary to 0
            $target_boundary = isset($this->lookup[$target]) ? $this->lookup[$target][$this->cg] : 0;
        // if target has any descendant nodes and/or the node needs to be inserted at a specific position
        else {
            // find the target's child node that currently exists at the position where the new node needs to be inserted to
            $slice = array_slice($target_descendants, $position - 1, 1);
            $target_descendants = array_shift($slice);
            // set the boundary - nodes having their "left"/"right" values outside this boundary will be affected by
            // the insert, and will need to be updated
            $target_boundary = $target_descendants[$this->cd];
        }
        // iterate through the records in the lookup array
        foreach ($this->lookup as $id => $properties) {
            // if the "left" value of node is outside the boundary
            if ($properties[$this->cg] > $target_boundary)
                // increment it
                $this->lookup[$id][$this->cg] += $source_rl_difference;
            // if the "left" value of node is outside the boundary
            if ($properties[$this->cd] > $target_boundary)
                // increment it
                $this->lookup[$id][$this->cd] += $source_rl_difference;
        }
        // update the nodes in the database having their "left"/"right" values outside the boundary
        $this->updateBorne($this->cg,$source_rl_difference,$target_boundary,'+');        
        $this->updateBorne($this->cd,$source_rl_difference,$target_boundary,'+');
        
        // finally, the nodes that are to be inserted need to have their "left" and "right" values updated
        $shift = $target_boundary - $source_boundary + 1;
        // iterate through the nodes to be inserted
        foreach ($sources as $properties) {
            // update "left" value
            $properties[$this->cg] += $shift;
            // update "right" value
            $properties[$this->cd] += $shift;
            // add the item to our lookup array
            $this->lookup[$properties[$this->col_id]] = $properties;
        }
        // also update the entries in the database
        // (notice that we're subtracting rather than adding and that finally we multiply by -1 so that the values
        // turn positive again)
        $sql = 'UPDATE `' . $this->tbl . '`
                   SET `' . $this->cg . '` = (`' . $this->cg . '` - ' . $shift . ') * -1,
                       `' . $this->cd . '` = (`' . $this->cd . '` - ' . $shift . ') * -1
                WHERE  `' . $this->cg . '` < 0';
        parent::db_Exec($sql);
        
        //Update the parent of the source node
        $sql = 'UPDATE `' . $this->tbl . '`
                   SET `' . $this->col_parent . '` = ' . $target . '
                 WHERE `' . $this->col_id . '` = ' . $source . '';
        parent::db_Exec($sql);
        
        
        // release table lock
        parent::unlockTables();
        // reorder the lookup array
        $this->_reorder_lookup_array();
        // return true as everything went well
        return true;
    }
    // if scripts gets this far, return false as something must've went wrong
    return false;
  }
  
  /**


* create html ul/li list

* @return (string)  liste UL/LI

*/
  public function to_list($node, $list_type='ul', $attributes = '') {
    if (!is_array($node)) $node = $this->get_tree($node);
   
    if (!empty($node)) {
        // start generating the output
        $out = '<' . $list_type . ($attributes != '' ? ' ' . $attributes : '') . '>';
        // iterate through each node
        foreach ($node as $elem){
            // generate output and if the node has children nodes, call this method recursively
            $out .= '<li class="arbre_item arbre_item_' . $elem[$this->col_id] . '"   >'
                 . '<a data-uid="'.$elem[$this->col_uid].'" class="node-item">'.$elem[$this->col_title] .'</a>'  . (is_array($elem['children']) ? $this->to_list($elem['children'], $list_type) : '') .
                  '</li>';
        }
        // return generated output
        return $out . '</'.$list_type.'>';
    }
  }
  
  
  /**


* Mise à jour Node Title

*/
  public function update($node=0, $title='') {
    $this->_init();
    // continue only if target node exists in the lookup array
    if (isset($this->lookup[$node])) {
        parent::lockTable($this->tbl);
               
        $sql = "UPDATE ". $this->tbl . "
                SET " . $this->col_title . " = :title
                WHERE ". $this->col_id . " = :node ";
        $datas = array(':title'=>$title, ':node'=>$node);
        parent::db_Exec($sql,$datas);
        
        parent::unLockTables();

        $this->lookup[$node][$this->col_title] = $title;
        return true;
    }  
    return false;
  }
  
  
  
  
//- FIN DE LA CLASS - //
}




Et enfin, voici un exemple
<?php
require_once 'db.class.php';
require_once 'arbre.class.php';

//on instancie la class Arbre
$mptt = new arbre();

// récupération de l'arbre sous forme de ul li
$tree_list = $mptt->to_list(0);

?>
<!Doctype html>
<html>
  <head>
    <title>Arbre</title>
    <meta charset="utf-8">

  </head>
  <body>
    <div id='jstree_div'>
      <?php
       // Affiche sous forme de liste UL / LI
        echo $tree_list;
      ?>
    </div>

  </body>
</html>


Une fois que tu as ce code mis en place et que tes menus s'affichent bien dans une liste UL / LI
Il faut juste ajouter du CSS pour gérer ton menu vertical.
Mais là... ce n'est plus du PHP ....
Tu peux t'inspirer, par exemple, de :
http://www.frogweb.fr/menu-deroulant-vertical/


0
lina_paradis Messages postés 4 Date d'inscription jeudi 17 mai 2018 Statut Membre Dernière intervention 17 mai 2018
17 mai 2018 à 22:56
un GRAND MERCI t'es adorable.
0
lina_paradis Messages postés 4 Date d'inscription jeudi 17 mai 2018 Statut Membre Dernière intervention 17 mai 2018
17 mai 2018 à 23:02
reste que la BD bdd_arbre je te laisse tranquille :(
0
jordane45 Messages postés 38201 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 5 juin 2024 4 675
17 mai 2018 à 23:44
arf. oui ça serait mieux en effet
CREATE TABLE IF NOT EXISTS `arbre` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `uid` varchar(20) NOT NULL DEFAULT '0' COMMENT 'id unique',
  `title` varchar(256) DEFAULT NULL,
  `gauche` bigint(20) DEFAULT NULL,
  `droite` bigint(20) DEFAULT NULL,
  `parent` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `cg` (`gauche`),
  KEY `cd` (`droite`),
  KEY `parent_id` (`parent`),
  KEY `uid` (`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;


et un jeu d'exemple
DELETE FROM `arbre`;
INSERT INTO `arbre` (`id`, `uid`, `title`, `gauche`, `droite`, `parent`) VALUES
	(1, 'ai5af566457e486', 'Main', 1, 12, 0),
	(2, 'ai5af5664584775', 'Child 1', 2, 3, 1),
	(3, 'ai5af56645901b4', 'Child 2', 4, 11, 1),
	(4, 'ai5af5664596d75', 'Child 2.1', 5, 8, 3),
	(5, 'ai5af566459cf36', 'Child 2.2', 9, 10, 3),
	(6, 'ai5af56645a6441', 'Child 3.1', 6, 7, 4);


Tu peux, si tu le souhaites, ajouter des champs supplémentaires.
Mais en réalité, il est préférable d'ajouter une table à côté qui serait en relation avec le champ uid ( uid = id unique)


Et un peu de documentation sur la gestion d'arbre intervallaire
https://sqlpro.developpez.com/cours/arborescence/
https://stephanelegrand.wordpress.com/2009/01/03/gestion-dune-structure-darbre-sous-mysql/

L'avantage de représenter ses arborescences de cette façon est de ne pas avoir à gérer de récursif pour parcourir l'arbre.
On peut, en une seule requête, récupérer le contenu de l'arbre entier ou d'une seule branche si l'on le souhaite.
Le code que je t'ai donné est plus complet que ce dont tu as besoin. il permet, en plus de créer l'arbre, de pouvoir modifier / supprimer / déplacer un noeud.
0