Discussion:
affiche d'enregistrement en UL LI
(trop ancien pour répondre)
ultracolle
2007-05-23 08:28:14 UTC
Permalink
Bonjour à tous,

J'ai un tableau sous cette forme ( que je récupère d'une Bdd ) :

/*
$tab[X] = array(id,parentId,Libellé);
id : id de la catégorie;
parentId : id de la catégorie à laquelle elle appartient, si
parentId=0 la catégorie n'est pas dans une catégorie
libellé : nom de la catégorie
*/

$tab[0] = array(1,0,'Lampe');
$tab[1] = array(2,0,'Lampadaires');
$tab[3] = array(3,0,'Appliques');
$tab[4] = array(5,1,'Fer forgé');
$tab[5] = array(6,1,'Bambou');
$tab[6] = array(7,1,'Art Déco');
$tab[7] = array(8,2,'Arabesque');
$tab[8] = array(9,2,'Cristal');
$tab[9] = array(10,2,'Barroque');
$tab[10] = array(11,8,'Rococo');
$tab[11] = array(12,8,'Neo');

et j'aurais souhaité les afficher ainsi :

<ul>
<li>Lampe
<ul>
<li>Fer forgé</li>
<li>Bambou</li>
<li>Art Déco</li>
</ul>
</li>
<li>Lampadaires
<ul>
<li>Arabesque
<ul>
<li>Rococo</li>
<li>Neo</li>
</ul>
</li>

<li>Cristal</li>
<li>Barroque</li>
</ul>
</li>

<li>Appliques</li>

</ul>

c'est a dire sous forme de liste UL LI classique ( en vue d'en faire
un petit un menu / sous menu ).

Je suppose qu'il est nécessaire d'utiliser une fonction récursive,
mais j'avoue que je ne suis pas à l'aise avec cette notion de
récursivité. Pourriez vous me donner une piste ?
Merci par avance.
Thief13
2007-05-23 12:05:12 UTC
Permalink
Pour faire des arbres, les intervalaires sont bien mieux que la
récursivité. si tu pense ton tableau avec les intervalaires, tu pourras
reconstruire ton arbre sans utiliser de récursivité.

Description de la technique :
http://sqlpro.developpez.com/cours/arborescence/
Olivier Miakinen
2007-05-23 12:37:41 UTC
Permalink
Post by Thief13
Pour faire des arbres, les intervalaires sont bien mieux que la
récursivité. si tu pense ton tableau avec les intervalaires, tu pourras
reconstruire ton arbre sans utiliser de récursivité.
http://sqlpro.developpez.com/cours/arborescence/
J'ai jeté un coup d'œil rapide. Ça semble sympa quand on a souvent à
faire des recherches en base de données pour retrouver les parents d'un
élément donné.

En revanche il est moins évident de faire des insertions et des
suppressions... surtout, cela nécessite de disposer des transactions
et je ne sais pas si toutes les bases de données accessibles par PHP
les proposent.
Thief13
2007-05-23 21:19:02 UTC
Permalink
Post by Thief13
Pour faire des arbres, les intervalaires sont bien mieux que la
récursivité. si tu pense ton tableau avec les intervalaires, tu pourras
reconstruire ton arbre sans utiliser de récursivité.
http://sqlpro.developpez.com/cours/arborescence/
J'ai jeté un coup d'½il rapide. Ça semble sympa quand on a souvent à
faire des recherches en base de données pour retrouver les parents d'un
élément donné.
En revanche il est moins évident de faire des insertions et des
suppressions... surtout, cela nécessite de disposer des transactions
et je ne sais pas si toutes les bases de données accessibles par PHP
les proposent.
Si tu crée des tables INNODB, pas de probleme pour le transactionnel
avec MySQL, qui est la base de donnée la plus courante.

PostreGres SQL supporte aussi le transactionnel

De plus, il est certain que l'insertion demande 3 requete au lieu d'une,
mais sur un arbre d'une 50ene de page, avec 5 niveau de profondeur, une
technique récursive peut engendrer pres d'une trentaine de requette SQL,
là ou 1 à 3 requettes suffisent avec le transactionnel pour récupérer
les infos nécessaires... Et comme une page est comme un menu est quand
meme sensé etre plus souvent consulté que modifié, y'a pas photos : les
intervallaires sont 100x mieux.

Sur le CMS que je suis en train de dévelloper, une page pouvais metres
jusqu'a 10 seconde en local pour effectuer toutes les requettes et
construire l'arbre en récursif. Un pouillem de seconde suffit maintenant
pour charger la meme page avec les intervallaires.

En franchement, lors d'une modification, 3 requette au lieu d'une,
surtout avec le transactionnel, je ne voix pas la différence, ça prend
toujours moin d'une demis seconde.
Olivier Miakinen
2007-05-23 23:24:21 UTC
Permalink
Post by Thief13
Si tu crée des tables INNODB, pas de probleme pour le transactionnel
avec MySQL, qui est la base de donnée la plus courante.
Ah, très bien. Je ne m'y connais pas beaucoup en bases de données.
Post by Thief13
De plus, il est certain que l'insertion demande 3 requete au lieu d'une,
mais sur un arbre d'une 50ene de page, avec 5 niveau de profondeur, une
technique récursive peut engendrer pres d'une trentaine de requette SQL,
là ou 1 à 3 requettes suffisent avec le transactionnel pour récupérer
les infos nécessaires...
Si les requêtes à la base de données font partie de la récursion, en
effet cela doit être très peu performant. Mais vu que les données ne
sont pas énormes et que le tout tient très facilement en mémoire, il
n'y a besoin que d'une seule requête SQL. Le résultat est stocké dans
le tableau $tab dont parlait ultracolle, puis le programme que j'ai
proposé parcourt exactement deux fois chacune des entrées du tableau :
une fois pour construire les listes d'enfants, une autre fois pour
écrire le résultat.
Post by Thief13
Et comme [] un menu est quand
meme sensé etre plus souvent consulté que modifié,
Ça c'est vrai.
Post by Thief13
y'a pas photos : les
intervallaires sont 100x mieux.
Ça ce serait vrai si tu n'avais besoin de lire qu'une cinquantaine de
données parmi des milliers. Pas si tu as besoin de lire la totalité
des données, et que donc tu peux le faire en une seule requête.
Post by Thief13
Sur le CMS que je suis en train de dévelloper, une page pouvais metres
jusqu'a 10 seconde en local pour effectuer toutes les requettes et
construire l'arbre en récursif. Un pouillem de seconde suffit maintenant
pour charger la meme page avec les intervallaires.
Un micro-pouillème suffirait si tu faisais une seule requête. Regarde
l'algo que je propose, et dis-moi ce que tu en penses.

Olivier Miakinen
2007-05-23 12:48:39 UTC
Permalink
Bonjour ultracolle. J'aime bien ce genre de fonctions et je vais tâcher
Post by ultracolle
$tab[9] = array(10,2,'Barroque');
À moins que ce ne soit une marque commerciale, il n'y a qu'un seul « r »
au mot « baroque ».
Post by ultracolle
$tab[11] = array(12,8,'Neo');
Et là je suppose que c'est plutôt « Néo ».
Olivier Miakinen
2007-05-23 15:47:05 UTC
Permalink
Bon, allons-y, je m'y (ultra)colle.
Post by ultracolle
/*
$tab[X] = array(id,parentId,Libellé);
id : id de la catégorie;
parentId : id de la catégorie à laquelle elle appartient, si
parentId=0 la catégorie n'est pas dans une catégorie
libellé : nom de la catégorie
*/
L'indice X de ton tableau n'ayant aucune signification, je suppose que
tu pourrais facilement le remplacer par id :

$tab[id] = array(id,parentId,Libellé);

Du coup, on peut supprimer id du contenu, mais en revanche on pourrait
y ajouter un sous-tableau initialement vide qui contiendra à terme la
liste des ids des sous-catégories :

tab[id] = array(parentId,Libellé,liste);
Post by ultracolle
$tab[0] = array(1,0,'Lampe');
$tab[1] = array(2,0,'Lampadaires');
$tab[3] = array(3,0,'Appliques');
$tab[4] = array(5,1,'Fer forgé');
$tab[5] = array(6,1,'Bambou');
$tab[6] = array(7,1,'Art Déco');
$tab[7] = array(8,2,'Arabesque');
$tab[8] = array(9,2,'Cristal');
$tab[9] = array(10,2,'Barroque');
$tab[10] = array(11,8,'Rococo');
$tab[11] = array(12,8,'Neo');
On a donc initialement :

$tab[1] = array(0,'Lampe',array());
$tab[2] = array(0,'Lampadaires',array());
$tab[3] = array(0,'Appliques',array());
$tab[5] = array(1,'Fer forgé',array());
$tab[6] = array(1,'Bambou',array());
$tab[7] = array(1,'Art Déco',array());
$tab[8] = array(2,'Arabesque',array());
$tab[9] = array(2,'Cristal',array());
$tab[10] = array(2,'Barroque',array());
$tab[11] = array(8,'Rococo',array());
$tab[12] = array(8,'Neo',array());

On rajoute un élément 0 :

$tab[0] = array(null, null, array());

Un petit foreach va remplir les listes d'ids :

foreach ($tab as $id => $elem) {
if ($id == 0) continue;
$parentId = $elem[0];
$tab[$parentId][2][] = $id;
}

Ce qui donnera donc :

$tab[0] = array(null, null, array(1,2,3));
$tab[1] = array(0,'Lampe',array(5,6,7));
$tab[2] = array(0,'Lampadaires',array(8,9));
$tab[3] = array(0,'Appliques',array());
$tab[5] = array(1,'Fer forgé',array());
$tab[6] = array(1,'Bambou',array());
$tab[7] = array(1,'Art Déco',array());
$tab[8] = array(2,'Arabesque',array(11,12));
$tab[9] = array(2,'Cristal',array());
$tab[10] = array(2,'Barroque',array());
$tab[11] = array(8,'Rococo',array());
$tab[12] = array(8,'Neo',array());
Post by ultracolle
<ul>
<li>Lampe
<ul>
<li>Fer forgé</li>
<li>Bambou</li>
<li>Art Déco</li>
</ul>
</li>
<li>Lampadaires
<ul>
<li>Arabesque
<ul>
<li>Rococo</li>
<li>Neo</li>
</ul>
</li>
<li>Cristal</li>
<li>Barroque</li>
</ul>
</li>
<li>Appliques</li>
</ul>
Allons-y. Tout d'abord, on a besoin d'une fonction pour indenter
(ajouter un certain nombre d'espaces ou de tabulations en début de
ligne). Par exemple :

function INDENT($indent)
{
while ($indent > 0) {
echo "\t";
$indent--;
}
}

Si tu veux indenter avec N espaces au lieu d'une tabulation, c'est
encore plus simple :

function INDENT($indent)
{
printf("%*s", N * $indent, "");
}

Maintenant il ne nous manque plus que des fonctions print_ul et print_li
qui s'appelleront mutuellement. On leur passe toutes deux l'indentation
voulue et le tableau $tab, mais aussi une liste d'ids pour print_ul et
un seul id pour print_li.

L'appel initial sera :

print_ul(0, $tab, $tab[0][2]);

Et voici les fonctions :

function print_ul($indent, &$tab, $liste)
{
INDENT($indent); echo "<ul>\n";
foreach($liste as $id) {
print_li($indent+1, $tab, $id);
}
INDENT($indent); echo "</ul>\n";
}

function print_li($indent, &$tab, $id)
{
list($parentId, $titre, $liste) = $tab[$id];
INDENT($indent); echo "<li>$titre";
if (count($liste) > 0) {
echo "\n";
print_ul($indent+1, $tab, $liste);
INDENT($indent);
}
echo "</li>\n";
}


Voilà. Je précise que je n'ai rien testé, et que je me suis interrompu
une bonne vingtaine de fois entre l'installation d'une freebox et les
appels de mon fils de deux ans, ce qui m'a peut-être fait faire de
grossières erreurs, mais tu vois le genre.
ultracolle
2007-05-23 19:03:16 UTC
Permalink
Merci à tous pour ces reponses,
Je vais jeter un coup d'oeil a l'article sur les intervalaires

Olivier, merci mille fois pour tes explications, et je vais de ce pas
potasser et appliquer tes fonctions.
Continuer la lecture sur narkive:
Loading...