recherche dans tableau

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 10:38

Bonjour à tous,
J'ai un tableau associatif comme ceci:
Array 
( 
    [0] => Array 
        ( 
            [category_parent_id] => 0 
            [category_child_id] => 1 
            [category_id] => 1 
            [vendor_id] => 1 
            [category_name] => Montagne 
            [category_description] =>  
            [category_thumb_image] =>  
            [category_full_image] => bf3a94d09cb29bddaa13839d184e1c5e.jpg 
            [category_publish] => Y 
            [cdate] => 1182523310 
            [mdate] => 1199471607 
            [category_browsepage] => browse_1 
            [products_per_row] => 1 
            [category_flypage] => shop.flypage 
            [list_order] => 16 
        ) 

    [1] => Array 
        ( 
            [category_parent_id] => 0 
            [category_child_id] => 2 
            [category_id] => 2 
            [vendor_id] => 1 
            [category_name] => Sports de raquettes 
            [category_description] =>  
            [category_thumb_image] => 1e9f38247fc2d98c7a95d02aed200485.gif 
            [category_full_image] =>  
            [category_publish] => Y 
            [cdate] => 1182523344 
            [mdate] => 1203965763 
            [category_browsepage] => browse_1 
            [products_per_row] => 1 
            [category_flypage] => shop.flypage 
            [list_order] => 2 
        ) 

    [2] => Array 
        ( 
            [category_parent_id] => 1 
            [category_child_id] => 3 
            [category_id] => 3 
            [vendor_id] => 1 
            [category_name] => Cycles 
            [category_description] =>  
            [category_thumb_image] => 1e9f38247fc2d98c7a95d02aed200485.gif 
            [category_full_image] =>  
            [category_publish] => Y 
            [cdate] => 1182523355 
            [mdate] => 1188212355 
            [category_browsepage] => browse_1 
            [products_per_row] => 1 
            [category_flypage] => shop.flypage 
            [list_order] => 3 
        ) 

    [3] => Array 
        ( 
            [category_parent_id] => 1 
            [category_child_id] => 4 
            [category_id] => 4 
            [vendor_id] => 1 
            [category_name] => Sports collectifs 
            [category_description] =>  
            [category_thumb_image] => 1e9f38247fc2d98c7a95d02aed200485.gif 
            [category_full_image] =>  
            [category_publish] => Y 
            [cdate] => 1182523369 
            [mdate] => 1188212362 
            [category_browsepage] => browse_1 
            [products_per_row] => 1 
            [category_flypage] => shop.flypage 
            [list_order] => 9 
        ) 
Ma question concerne la recherche dans les tableaux: comment faire pour récupérer tous les arrays qui contiennent [category_parent_id] => 1 ?
Il y en a 2 ici. Existe t-il évetuellement une fonction php qui peut me retourner tous les tableaux respectant la condition?

Merci pour vos réponses.

Mammouth du PHP | 1353 Messages

02 sept. 2008, 10:53

Bonjour,

Tu peux regarder du coté des fonctions array_search() mais je ne sais pas si tu trouveras ton bonheur...

Sinon, un foreach ne pourrait pas te convenir ?
foreach($test as $tab)
	{
		if($tab[category_parent_id] == 1)
			{
			//traitement
			}
	}
Tell me and I forget. Teach me and I remember. Involve me and I learn.

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 11:24

J'essaye en fait de construire un menu/sous-menus en liste de ce type avec le tableau précédent:
<ul>
    <li>Montagne</li>
    <li>
        <ul>
            <li>Ski</li>
            <li>Surf</li>
            <li>Rando</li>
        </ul>
    </li>
   <li>Tennis</li>
    <li>
        <ul>
            <li>Chaussures</li>
            <li>Raquettes</li>
            <li>Sacs</li>
        </ul>
    </li>
</ul>
Les éléments de 1er niveau (Montagne et Tennis) ont [category_parent_id] => O, car ils n'ont pas de parent, mais ils ont des enfants.

Pour récupérer par exemple les sous menus dont [category_parent_id] => 1, je fais :
		echo "<ul>";
		for ($i = 0; $i <count($rows); $i++)
		{
			//si l'élément est de premier niveau, on l'affiche directement comme élément de liste
			if ($rows[$i]["category_parent_id"] == 0)
			{
				echo "<li>";
				echo "<a href='#'>" . $rows[$i]["category_name"] . "</a>";				
				echo "</li>";
			}
			
			
			else 
			{
				//sinon on cherche les enfants de cet élément, qu'on met dans un array $sub_menu
				foreach ($rows as $key => $val)
				{
					if($val["category_parent_id"] == $rows[$i]["category_id"])
					{
						$sub_menu[] = $rows[$i];
					}
				}		
				
				//on affiche ensuite le nom de l'élément et ses sous menus
				echo "<li>";
				echo "<a href='#'>" . $rows[$i]["category_name"] . "</a>";	
				foreach ($sub_menu as $item)
				{
					
					echo "<ul>";
					echo "<li>";
					echo "<a href='#'>" . $item["category_name"] . "</a>";				
					echo "</li>";
					echo "</ul>";
				}					
				echo "</li>";			
			}				
		}
		echo "</ul>";

Ce code ne fonctionne pas, il me lance une boucle infinie. Voyez vous ce qui ne va pas dans mon raisonnement? Comment faire pour retrouver les sous-menus d'un élément de 1er niveau?

Mammouth du PHP | 1353 Messages

02 sept. 2008, 12:26

Il y a effectivement quelques problèmes dans le raisonnement.

Ici, que ca soit un noeud premier ou un noeud secondaire vous l'affichez au meme plan car dans le else vous faites quand meme un <li> au meme endroit que si c'est un parent.

Disons pour faire simple qu'avec cet enchainement il me parait impossible d'avoir ce que vous voulez, en admettant que j ai compris le but de votre code.

Je peux vous décrire la solution la plus rapide qui me vienne à l'esprit mais je pense qu'elle est loin d etre optimisée donc si le menu est conséquent cela ne sera pas une bonne solution. Néanmoins au niveau raisonnement peut etre y trouverez vous une bonne explication :

L'idée est la suivante :
echo "<ul>"; 
        for ($i = 0; $i <count($rows); $i++) 
        { 
            //si l'élément est de premier niveau, on l'affiche directement comme élément de liste 
            if ($rows[$i]["category_parent_id"] == 0) 
            { 
                echo "<li>"; 
                echo "<a href='#'>" . $rows[$i]["category_name"] . "</a>";
                //on cherche directement tous les enfants pour cet élément
                foreach($rows as $val)
                		{
                		//on initialise une variable pour savoir si on en est au 1er enfant ou pas
                		$deb = false;
                    	if($val["category_parent_id"] == $rows[$i]["category_id"]) //si l'élément est un enfant du parent en cours
	                    	{ 
	                    	if(!$deb) { //si il s agit du 1er enfant
	                    		$deb = true;  //on met la variable a true, pour n entrer qu une fois dans cette boucle
			                    echo "<ul>"; //on affiche ul pour le décalage
	                    		}
		                    echo "<li>"; 
		                    echo "<a href='#'>" . $val["category_name"] . "</a>"; //on affiche le nom de l enfant
		                    echo "</li>"; 
	                    	} 
                		if($deb) echo "</ul>"; //si il  y avait des enfants on ferme le <ul> de décalage
                		}                       
                echo "</li>"; 
            }            
        } 
echo "</ul>";  
Ceci n'est qu'une idée et a été très rapidement testé donc n'est pas fiable.

De plus ca n est pas optimisé car pour chaque parent on scan tout le tableau pour trouver ses enfants, il faudrait par exemple envisager de les supprimer une fois qu'ils sont affichés pour limiter les pertes de perf.

En espérant que cela vous aidera.
Tell me and I forget. Teach me and I remember. Involve me and I learn.

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 12:51

merci, ce code me rapproche énormément de ce que je recherche.
2 questions:

Le menu parent est réaffiché pour chaque élément enfant.
Au lieu d'avoir:
Montagne
-Ski
-Surf
-Accesssoires
J'obtiens
Montagne
-Montagne
-Ski
-Montagne
-Surf
-Montagne
-Accesssoires
Voyez vous d'ou ça peut venir?

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 13:13

ok, j'ai pu supprimer le problème en rajoutant un test sur le nom du sous menu dans la boucle.
ça marche, merci!

Mais comment faire si un élément de sous menu peut lui même avoir des enfants, et ainsi de suite à l'infini ? (nombre indéterminé de niveaux dans le menu)

Mammouth du PHP | 1353 Messages

02 sept. 2008, 13:44

A ce moment la il faut partir dans l'idée d'une fonction récursive.

Le bout de code qui s'exécute ici doit être mis dans une fonction qui s'appelle "toute seule" avec un index qui s'incrémente et qui ne revient au traitement qu'une fois l'occurence de la fonction d avant terminée.

Je ne suis pas très doué en récursivité, vous trouverez des détails dans l explication suivante
Tell me and I forget. Teach me and I remember. Involve me and I learn.

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 13:46

Pour obtenir le résultat attendu, j'ai tenté ceci:
		echo "<ul>";  
			for ($i = 0; $i <count($rows); $i++)  
			{  
				//si l'élément est de premier niveau, on l'affiche directement comme élément de liste  
				if ($rows[$i]["category_parent_id"] == 0)  
				{  
					echo "<li style='margin-top: 30px;'>";  
					echo $rows[$i]["category_parent_id"] . " <a href='#'>" . $rows[$i]["category_name"] . "</a>"; 
					//on cherche directement tous les enfants pour cet élément 
					get_children($rows);
				}             
			}  
		echo "</ul>"; 
Et la fonction get_children qui encapule une partie du code que vous avez fourni:
		function get_children($rows) 
		{
		
							foreach($rows as $val) 
							{ 
								//on initialise une variable pour savoir si on en est au 1er enfant ou pas 
								$deb = false; 
								if($val["category_parent_id"] == $rows[$i]["category_id"] && $val["category_name"] != $rows[$i]["category_name"]) //si l'élément est un enfant du parent en cours 
								{  
									if(!$deb) 
									{ //si il s agit du 1er enfant 
										$deb = true;  //on met la variable a true, pour n entrer qu une fois dans cette boucle 
										echo "<ul >"; //on affiche ul pour le décalage 
									} 
									echo "<li>";  
									echo $val["category_parent_id"] . " <a href='#'>" . $val["category_name"] . "</a>"; //on affiche le nom de l enfant 
									echo "</li>";  
								}  
								if($deb) echo "</ul>"; //si il  y avait des enfants on ferme le <ul> de décalage 					
							}                        
							echo "</li>";  
		
		}
J'essaye d'ajouter un comportement récursif. ça ne marche pas, mais je ne vois pas vraiment pourquoi. Merci pour tout conseil.

Mammouth du PHP | 1353 Messages

02 sept. 2008, 14:17

Ce code la ne peut pas fonctionner, car dans la fonction get_children si vous ne passez pas ne serait ce que la valeur de $i ca ne peut pas marcher.

Sinon, pour un comportement récursif il va falloir raisonner différemment, on ne pourra pas le faire comme ceci.

L'idée est la suivante :

Pour chaque élément parent niveau 0 => fonction get_children()
Si cet élément a des enfants
Alors fonction get_children() sur tous les enfant de cet élément

Mais je suis pas bon en récursivité...
Rapidement un exemple de code avec ton cas mais il est possible que ca marche pas et encore une fois c pas optimisé du tout, c plus pour montrer le cheminement :
echo "<ul>";   
            foreach($rows as $val)   
            {   
                //on lance la fonction récursive dès que l'on a un parent niveau 0
                if ($val["category_parent_id"] == 0) display_children($val,$rows);            
            }   
        echo "</ul>";
        
        
function display_children($node,$rows)
	{
	//on affiche le noeud que l'on traite
	echo "<ul>";
	echo "<li style='margin-top: 30px;'>";   
    echo $node["category_parent_id"] . " <a href='#'>" . $node["category_name"] . "</a>";  
    echo "</li>";
    
    //on récupère les enfants de ce noeud
    $children = get_children($node,$rows);
    
    //si le noeud a des enfants
	if(!empty($children)) 
		{
		//on lance cette fonction d affichage pour chaque enfant
		foreach($children as $s_node)
			display_children($s_node,$rows);
		}
	echo "</ul>";
	}

function get_children($val_p,$rows)
{
	 //cette fonction renvoie tous les enfants d'un noeud donné
	 $children = array();
	 foreach($rows as $val)
	 	{
	 	if($val["category_parent_id"]== $val_p["category_id"]) array_push($children,$val);
	 	}
	 return $children;
}
Tell me and I forget. Teach me and I remember. Involve me and I learn.

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 14:23

je ne sais pas d'ou ça vient mais ce code récursif bloque carrément apache et pousse à redémarrer.

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 14:24

je suis prêt à rémunérer pour obtenir un menu récursif à partir de ce code.

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 14:30

//on lance la fonction récursive dès que l'on a un parent niveau 0
if ($val["category_parent_id"] == 0) display_children($val,$rows);
Je ne suis pas sûr, mais cette ligne devrait poser problème car on doit pouvoir lancer une recherche des enfants, même si le parent n'est pas de niveau 0. je me trompe?

Mammouth du PHP | 1353 Messages

02 sept. 2008, 14:33

Désolé pour le blocage :(
Je ne comprend pas trop pourquoi, le code est certes lent si il y a beaucoup d entrées mais de la à planter apache...

Comment est crée le tableau $rows ? Si je pouvais obtenir les enregistrements de ce tableau ca serait plus facile pour moi de tester...
Aussi quand vous avez testé vous avez bien commenté tout le reste, notamment la fonction get_children qui existait déjà dans votre précédent code... ?
Tell me and I forget. Teach me and I remember. Involve me and I learn.

Mammouth du PHP | 1353 Messages

02 sept. 2008, 14:35

//on lance la fonction récursive dès que l'on a un parent niveau 0
if ($val["category_parent_id"] == 0) display_children($val,$rows);
Je ne suis pas sûr, mais cette ligne devrait poser problème car on doit pouvoir lancer une recherche des enfants, même si le parent n'est pas de niveau 0. je me trompe?
C'est le cas dans la fonction display_children(), on lance la fonction display_children pour chaque enfant du noeud...
La c'est juste pour être sur de commencer avec un parent de niveau 0, car sinon si il prenait la 1ere entrée on pourrait avoir un noeud de niveau 3 et donc ne jamais remonter vers les parents...
Tell me and I forget. Teach me and I remember. Involve me and I learn.

Invité
Invité n'ayant pas de compte PHPfrance

02 sept. 2008, 14:37

merci pour ton implication.
Si ça t'intéresse, je peut te contacter en pv pour déterminer un prix et en suite je te passe le sql + php.
Je sens qu'on est pas loin, mais je n'ai pas le niveau pour terminer.
Merci de confirmer évetuellement si ça 'intéresse quelqu'un.