Catégories, sous-catégories existantes ou non

Eléphant du PHP | 337 Messages

19 févr. 2012, 04:34

Bonjour à tous,

J'ai une table contenant des catégories sur deux niveaux (disons des catégories et des sous-catégories). Je l'ai organisée de la façon suivante :
- id
- titre
- niveau (0 pour catégorie et 1 pour sous-catégorie)
- pere (id de la catégorie père si c'est une sous-catégorie, sinon 0 pour une catégorie)
Bon déjà, je suis pas sûr de mon coup, il y a sans doute mieux à faire...
Puis, lorsque je veux afficher mes catégories par un script php, je ne sais pas comment faire ma requête pour éviter d'imbriquer 2 requêtes l'une dans l'autre, j'ai lu partout que c'était trèèèès mal... Jusqu'à présent j'ai toujours fait comme ça par paresse, mais j'aimerais bien faire mieux ; j'ai notamment lu des trucs sur JOIN, mais ça ne m'inspire pas :D (d'autant que dans ce cas, la relation entre les données est à l'intérieur de la même table).

Avez-vous une idée pour m'aider ?

Merci !

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

19 févr. 2012, 07:36

Je pense que le système convient, tu as d'autres façons de faire comme celle-ci par exemple http://sqlpro.developpez.com/cours/arborescence/ mais en règle générale c'est ça.

Ou alors sans considération de niveau tu as juste une colonne "catégorie parente", et quand c'est NULL c'est que c'est le plus haut.

Tu peux très bien faire une jointure sur une même table :
SELECT top_cat.id, top_cat.titre, sous_cat.id, sous_cat.titre
FROM categories top_cat
INNER JOIN categories sous_cat ON to_cat.id = sous_cat.pere
WHERE top_cat.niveau = 0
ORDER BY top_cat.titre ASC, sous_cat.titre.ASC
Cette requête devrait te renvoyer quelque chose comme ça :
1 - "Top Categorie 1" - 2 - "Sous Categorie 1.1"
1 - "Top Categorie 1" - 3 - "Sous Categorie 1.2"
4 - "Top Categorie 2" - 5 - "Sous Categorie 2.1"
4 - "Top Categorie 2" - 6 - "Sous Categorie 2.2"
7 - "Top Categorie 3" - 8 - "Sous Categorie 3.1"
7 - "Top Categorie 4" - 9 - "Sous Categorie 3.2"
C'est dans ton script PHP que tu feras un petit test pour savoir quand est-ce que tu changes de Top Catégorie pour en afficher le titre.

Par contre, le INNER JOIN ne sélectionnera que les catégories qui ont des sous-catégories. Pour sélectionner celles qui n'en ont pas, utilise OUTER JOIN :
SELECT top_cat.id, top_cat.titre, sous_cat.id, sous_cat.titre
FROM categories top_cat
LEFT OUTER JOIN categories sous_cat ON to_cat.id = sous_cat.pere
WHERE top_cat.niveau = 0
ORDER BY top_cat.titre ASC, sous_cat.titre.ASC

Eléphant du PHP | 337 Messages

20 févr. 2012, 01:33

Merci beaucoup pour cette réponse : malgré tout j'ai un peu de mal à concevoir l'instruction JOIN, bien qu'ayant lu plusieurs tutos sur le sujet. Ça devrait venir en insistant j'imagine :mrgreen:

J'ai également du mal à exploiter ensuite les résultats en php... Dans mon exemple, je voudrais afficher la liste des catégories, avec leurs sous-catégories si elles en ont et si une variable de navigation définit que ladite catégorie est sélectionnée (du genre $_GET['id']=X). J'ai écrit ça :
// Ici une série d'instruction pour définir ma catégorie sélectionnée, si c'est le cas (sinon aucune des catégories ne devra être développée dans le menu)

$sql = "
	SELECT parent.id, parent.titre, enfant.id, enfant.titre
	FROM categories parent
	LEFT OUTER JOIN categories enfant ON parent.id = enfant.pere
	WHERE parent.pere = 0
	ORDER BY parent.titre ASC, enfant.titre ASC
	";

$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
	// Et là, comment je récupère mes données ?
}
J'ai essayé notamment avec des $row['parent.titre'] et d'autres trucs du genre, mais sans succès !

Eléphant du PHP | 179 Messages

20 févr. 2012, 11:42

$row['titre'] ;)

Eléphant du PHP | 337 Messages

20 févr. 2012, 14:57

Hem, eeeuh oui, en effet, merci :oops:

Alors du coup maintenant, il me retourne les catégories, mais sans en afficher les titres, avec les sous catégories correspondantes, mais n'affiche pas les catégories ne comportant pas de sous-catégories...

J'ai essayé avec ce code :
/*
Instruction pour déterminer la valeur de la catégorie sélectionnée
*/

$sql = "
	SELECT parent.id, parent.titre, enfant.id, enfant.titre
	FROM $CATEGORIES parent
	LEFT OUTER JOIN $CATEGORIES enfant ON parent.id = enfant.pere
	WHERE parent.pere = 0
	ORDER BY parent.titre ASC, enfant.titre ASC
";

$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
	// Catégorie parent, actuellement sélectionnée
	if($row['pere'] == 0 && $CAT == $row['id']) echo "<li class='ON'>".$row['titre']."</li>";
	// Catégorie parent, non sélectionnée
	else if($row['pere'] == 0 && $CAT == 0) echo "<li class='OFF'><a href='index.php?cat=".$row['id']."'>".$row['titre']."</a></li>";
	// Catégorie enfant, actuellement sélectionnée
	else if($row['pere'] != 0 && $CAT == $row['id']) echo "<li>".$row['titre']."</li>";
	// Catégorie enfant, non sélectionnée
	else echo "<li><a href='index.php?cat=".$row['id']."'>".$row['titre']."</a></li>";
}
Avec ça il me retourne la chose suivante :

- Cat parent 1 (mais sans afficher le titre)
-- Cat enfant 1.1
-- Cat enfant 1.2
- Cat parent 2 (mais sans afficher le titre)
-- Cat enfant 2.1
-- Cat enfant 2.2
-- Cat enfant 2.3

Il ne retourne pas les catégories parent n'ayant pas d'enfant, et $row['id'] ou $row['titre'] me retournent des variables vides pour les catégories parent.
(quel bordel, j'aurais déjà fini depuis trois jours, avec la méthode déconseillée :wink:)

Eléphant du PHP | 179 Messages

20 févr. 2012, 17:07

$sql = "
SELECT parent.id as id_parent, parent.titre as titre_parent, enfant.id as id_enfant, enfant.titre as titre_enfant
FROM categories parent
LEFT OUTER JOIN categories enfant ON parent.id = enfant.pere
WHERE parent.pere = 0
ORDER BY parent.titre ASC, enfant.titre ASC
";

Déja tu récupères tes values avec $row['titre_parent']; et tu n'auras plus de problème lors de l'affichage des titres, car la le titre de l'enfant overwright le titre du parent..

Après pour ton problème de sous catégories vide, regarde les différentes fonctionnalité et option du JOIN, il doit en avoir une qui affiche même si il n'a rien a jointer avec la ligne ( pas d'enfant dans ce cas la )... mais je pensais que c'était OUTER justement, chez moi cela fonctionne :/

Eléphant du PHP | 337 Messages

21 févr. 2012, 00:36

Merci pour ces réponses : je n'ai pas encore réglé mon problème, mais l'instruction JOIN m'ouvre des horizons prometteurs :D

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

24 févr. 2012, 21:50

car la le titre de l'enfant overwright le titre du parent..
Je viens de voir ca... le mot que tu cherches c'est "overwrite". "Overright", ca n'a aucun sens. Pourquoi ne pas utiliser "remplacer" par exemple, plutot que de dire n'importe en quoi dans un vieil anglais? :roll: