IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Fonction récursive pour remplissage Array


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut Fonction récursive pour remplissage Array
    Bonjour à tous, j'espère tout d'abord poster au bon endroit ;

    J'ai bien utilisé la fonction de recherche avant, et trouvé une question presque similaire à la mienne, mais le résultat ne me convenait pas (requête dans une boucle, berk.)

    Voilà mon soucis :

    J'extrais d'une base les informations sur des forums (ID, titre, etc.).
    Chaque forum possède un champ 'parent_forum_id', qui peut avoir une valeur NULL. Si NULL, alors le forum est en première ligne, sans parent. Sinon, c'est le sous-forum du parent précisé.

    Du coup, je me retrouve avec un résultat ligne par ligne de forums / sous-forums, et je dois refaire l'arbre ; le but est de refaire cet arbre sous forme d'Array, qui pourra être exploité ultérieurement..



    Voici mon script à l'heure actuelle, qui permet d'avoir dans l'array :

    Les catégories et leurs infos, plus un champ 'children', qui contient NULL par défaut, puis qui contient les array des forums qu'il contient, avec leurs infos, plus un champ 'children', etc.

    J'arrive à créer l'Array avec les catégories, puis à y insérer les Forums sans parents, puis à insérer par le biais d'une fonction les premiers sous-forums ; mais impossible, pour mon pauvre esprit, de concevoir la fonction en mode récursif pour continuer dans la "généalogie"..

    Je comprends bien le concept d'une fonction récursive, mais juste, mes syntaxes finissent par planter (des erreurs d'index inexistants, ou encore de mémoire dépassée)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    <?php
    require('conf/conf.inc.php');
    $categories = $c->get("SELECT * FROM categories");
    $for_parents = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NULL");
    $for_children = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NOT NULL");
     
    $site = array();
    $enfants = array();
     
    foreach($categories as $cat){	
    	$site[$cat['cat_id']] = $cat;
    	$site[$cat['cat_id']]['children'] = NULL;
    }
    foreach($for_parents as $forum){
    	$site[$forum['cat_id']]['children'][$forum['forum_id']] = $forum;
    	$site[$forum['cat_id']]['children'][$forum['forum_id']]['children'] = NULL;
    }
    foreach($for_children as $forum){
    	$enfants[$forum['forum_id']] = $forum;
    }
     
    function addEnfants($parents, $enfants){
    	foreach($parents as $key=>&$parent){
    		foreach($enfants as &$enf){
    			if($enf['parent_forum_id'] == $key){
    				$parent['children'][$enf['forum_id']] = $enf;
    				$parent['children'][$enf['forum_id']]['children'] = NULL;
    				// ICI, je suis censé placer un "unset" pour vider petit à petit l'array des enfants, puis relancer la fonction "addEnfant()". C'est à cette ligne que je bloque...
    			}
    		}
    	}
    	return $parents;
    }
     
    foreach($site as &$parents){
    	$parents = addEnfants($parents['children'], $for_children);
    }
    unset($parents);
     
    echo '<pre>';
    var_dump($site);
    echo '</pre>';
    echo '<br />--------------<br />';
    ?>

    Si quelqu'un a une idée pour me décoincer Merci !

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Je post la suite de mon problème, ayant avancé seul..

    J'ai réussi à créer ma fonction, et le résultat est quasiement bon ; le seul bémol, c'est qu'au résultat final, mon array est correctemetn construit mais j'ai perdu au passage toute information concernant les catégories et les 1ers forums.

    Les sous-forums, quant à eux, sont parfaitement en place avec toutes leurs informations (titre, etc..)

    voilà mon script à l'heure actuelle :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    <?php
    require('conf/conf.inc.php');
    $categories = $c->get("SELECT * FROM categories");
    $for_parents = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NULL");
    $for_children = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NOT NULL");
     
    $site = array();
    $enfants = array();
     
    function addEnfants($parents, $enfants){
    	foreach($parents as $key=>&$parent){
    		foreach($enfants as &$enf){
    			if($enf['parent_forum_id'] == $parent['forum_id']){
    				$parent['children'][$enf['forum_id']] = $enf;
    				$parent['children'][$enf['forum_id']]['children'] = NULL;
    				$parent['children'] = addEnfants($parent['children'], $enfants);
    			}
     
    		}
    	}
    	return $parents;
    }
     
    // Initialisation des catégories
    foreach($categories as $cat){
    	$site[$cat['cat_id']] = $cat;
    	$site[$cat['cat_id']]['children'] = NULL;
    }
    // Initialisation des forums sans parents
    foreach($for_parents as $forum){
    	$site[$forum['cat_id']]['children'][$forum['forum_id']] = $forum;
    	$site[$forum['cat_id']]['children'][$forum['forum_id']]['children'] = NULL;
    }
    // On récupère tous les sous-forums
    foreach($for_children as $forum){
    	$enfants[$forum['forum_id']] = $forum;
    }
    // On créé le tableau contenant l'arbre
    foreach($site as &$parents){
    	$parents = addEnfants($parents['children'], $for_children);
    }
    unset($parents);
     
    echo '<pre>';
    var_dump($site);
    echo '</pre>';
    echo '<br />--------------<br />';
    ?>

  3. #3
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    Salut WibiMaster,

    le seul bémol, c'est qu'au résultat final, mon array est correctemetn construit mais j'ai perdu au passage toute information concernant les catégories et les 1ers forums.
    C'est normal car ta fonction addEnfant devrait travailler avec des références et non des copies de tableaux :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function addEnfant(&$parents, &$enfants)
    Enfin si je ne suis pas à côté de la plaque.

    De mon côté j'ai eu à faire un truc de ce genre et je te l'ai adapté (grosso modo) à tes besoins j'y ai mis des annotations dans le code histoire de ne pas être paumé. Je ne saurais que trop te conseiller de te pencher sur la SPL (Standard PHP Library) car elle offre des tas de fonctions sacrément utiles par exemple dans le cas de la récursivité. Et pour ne rien gâcher la récursivité est mise à plat et du coup tu ne risque pas de mettre à genoux le serveur en cas d'algorithme attaquant des données très volumineuses (vécu).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    <?php
     
    require('conf/conf.inc.php');
     
    $categs = $c->get("SELECT * FROM categories");
    # il faut absolument avoir les forums parents avant les enfants
    $forums = $c->get("SELECT * FROM forums ORDER BY parent_forum_id");
     
    $map   = array();
    $paths = array();
     
    # récupération de toutes les catégories
    # pour profiter de la SPL (voir plus bas) il fallait éviter
    # les collisions entre les clés des cat_id et des forum_id
    # donc j'ai préfixé les cat_id avec un 'g'
    foreach($categs as $cat) {
       $map['g' . $cat['cat_id']] = array('type'      => 'categ',
                                          'details'    => $cat, 
                                          'subforums'  => array());
    }
     
    # arborescence des forums
    foreach($forums as $forum) {
     
       $idParent = $forum['parent_forum_id'];
       $idForum  = $forum['forum_id'];
     
       if (is_null($idParent)) {
          # forum racine
          $map['g' . $forum['cat_id']]['subforums'] += array($idForum => array('type'      => 'forum',
                                                                               'details'   => $forum, 
                                                                               'subforums' => array()));
          # chemin vers le forum courant
          $paths[$idForum] = array('g' . $forum['cat_id']);
     
       } else {
          # sous-forum
          # chemin vers le forum courant
          $paths[$idForum] = array_merge($paths[$idParent], array($idParent));
     
          foreach (new RecursiveIteratorIterator(
                      new RecursiveArrayIterator($map),
                            RecursiveIteratorIterator::SELF_FIRST) as $key => $value) 
          {
             # si la clé courante correspond à la clé recherchée
             # on ajoute le sous-forum au tableau $map
             if ($key == $idParent) {
                $nb = count($paths[$idForum]) - 1;
                for($i = 0; $i <= $nb; $i++) {
                   if ($i == 0) {
                      $last =& $map[$paths[$idForum][$i]];
                   } else {
                      $last =& $last['subforums'][$paths[$idForum][$i]];
                   }
                }
                $last['subforums'] += array($idForum => array('type'      => 'forum',
                                                              'details'   => $forum,
                                                              'subforums' => array()));
             }
          }
       }
       var_dump($map);
    }
    ?>

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Merci !

    Déjà pour l'histoire des références, il me semblait qu'en la déclarant avec le "&" dans le foreach, je pouvais l'utiliser sans après.. Mais je me trompe peut-être.

    J'ai donc testé ton script, ça fonctionne presque ; les sous-forums de sous-forums ne sont pas pris en compte, et j'obtiens assez régulièrement l'erreur :

    Warning: Cannot use a scalar value as an array in C:\Program Files (x86)\EasyPHP-5.3.3\www\live4dev\test2.php on line 53

    dans le var_dump..

  5. #5
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    Oui j'ai fait une boulette dans la ligne 53
    remplace $last &= par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if ($i == 0) {
       $last =& $map[$paths[$idForum][$i]];
    } else {
       $last =& $last['subforums'][$paths[$idForum][$i]];
    }
    Dans tous les cas j'ai essayé avec ce genre de données :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $categs = array(array('cat_id' => 0, 'nom' => 'PHP'), 
                    array('cat_id' => 1, 'nom' => 'JAVA'), 
                    array('cat_id' => 2, 'nom' => 'VBA'));
     
    $forums = array(array('forum_id' => 1, 'cat_id' => 0, 'nom' => 'Général'),
                    array('forum_id' => 2, 'parent_forum_id' => 1, 'nom' => 'Code'),
                    array('forum_id' => 3, 'parent_forum_id' => 1, 'nom' => 'Syntaxe'),
                    array('forum_id' => 4, 'parent_forum_id' => 1, 'nom' => 'Langage'),
                    array('forum_id' => 5, 'parent_forum_id' => 4, 'nom' => 'Débutant'),
                    array('forum_id' => 6, 'parent_forum_id' => 4, 'nom' => 'Confirmé'));
    Et j'ai bien toute l'arborescence

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Presque bon xD

    C'est la première fois que j'utilise les RecursiveIteratorIterator, alors je prends mon temps pour lire, comprendre, en utilisant la doc.

    En tout cas, c'est quasiement bon, mais ça plantait (serveur down) quand je lançais la page. Alors j'ai essayé avec tes données, moins importantes que les miennes, et ça plante aussi.

    En coupant le chargement de la page au bout de quelques secondes avant que tout pète, j'ai pu remarquer qu'en fait, la boucle ne s'arrêtait jamais ; je retrouve

    array(3) {
    ["g0"]=>

    donc l'origine, plusieurs fois. Comme si toute la page se relançait sans arrêt, donc forcément, tout plante. J'essaie de voir où le script devrait détecter la fin et s'arrêter, pour l'améliorer ; étrange que cela fonctionne correctement chez toi, j'ai pourtant repris point par point tout ce que tu m'as donné..

    Les lignes

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $last &= ($i == 0) ? $map[$paths[$idForum][$i]]
                                      : $last['subforums'][$paths[$idForum][$i]];
    sont bien devenues

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if ($i == 0) {
    					$last =& $map[$paths[$idForum][$i]];
    				} else {
    					$last =& $last['subforums'][$paths[$idForum][$i]];
    				}
    Mais c'est depuis que le script ne s'arrête pas ; j'ai du faire une erreur ici, je cherche et je tiens au courant ^^

    Merci de ton aide !

    [edit]
    En réalité, j'ai bien observer le var_dump (meme arreté avant la fin) ; ce n'est pas que le code se répète indéfiniment (quoi que, sans doute tout de même), c'est assez étrange ce qu'il ressort...

    Tout d'abord, ça me ressort l'array des catégories ; ensuite, l'array des catégories avec le premier forum ; ensuite, la meme chose mais avec plus de forums ; et ainsi de suite.. Comme s'il faisait un var_dump au fur et à mesure, alors que celui-ci est séparé de la boucle.

    Après vérification, celui-ci était bien dans la boucle. Je l'ai donc ressorti, et du coup, ça plante avant de faire le var_dump, je ne vois donc même pas ce qu'il peut contenir. La boucle ne s'arrête réellement jamais.. Je ne comprends pas, dans ton script, c'est un foreach sur un tableau fini, il ne devrait donc pas tourner éternellement...

    Ca doit etre à ce niveau là :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    foreach (new RecursiveIteratorIterator(
                      new RecursiveArrayIterator($map),
                            RecursiveIteratorIterator::SELF_FIRST) as $key => $value)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Fonction récursive pour créer un array
    Par laloune dans le forum Langage
    Réponses: 18
    Dernier message: 26/02/2013, 12h39
  2. [MySQL] Fonction récursive pour affichage arborescence
    Par Mister Paul dans le forum PHP & Base de données
    Réponses: 11
    Dernier message: 01/12/2007, 19h30
  3. Réponses: 6
    Dernier message: 12/04/2007, 20h30
  4. Réponses: 10
    Dernier message: 03/07/2006, 11h32
  5. Fonctions récursives pour parcourir un arbre
    Par mikedavem dans le forum C
    Réponses: 4
    Dernier message: 05/06/2006, 12h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo