Page 1 sur 1

Catégories en cascade et suppression

Publié : 26 déc. 2007, 16:33
par Artengo34
Bonjour et bonnes fêtes :wink:

Nouveau sur ce forum, je me retrouve actuellement confronté à un problème qui me fait tourner en bourrique...

J'ai cette table (volontairement réduite au strict minimum)

CREATE TABLE categories (
id_cat bigint(20) NOT NULL auto_increment,
lib_cat varchar(45) NOT NULL default '',
parent_cat bigint(20) NOT NULL default '0'
PRIMARY KEY (id_cat)
) TYPE=MyISAM;

Cette structure me permet la création de catégories en cascade en utilisant le champ parent_id comme clé étrangère (id de la catégorie mère)

catégorie 1
-categorie 1.1
--categorie 1.1.1
--categorie 1.1.2
---categorie 1.1.2.1
---categorie 1.1.2.2
----categorie 1.1.2.2.1
--categorie 1.1.3
---categorie 1.1.3.1
-categorie 1.2
-categorie 1.3
categorie 2
etc..........

Mon souci se situe au niveau de la suppression d'une catégorie et tous ses enfants.

Si je souhaite supprimer la catégorie 1.1, je pourrai facilement supprimer les 1.1.1, 1.1.2 et 1.1.3, mais je planche complètement pour la suppression des 1.1.2.1, 1.1.2.2, 1.1.2.2.1 et 1.1.3.1 (les descendants indirects).

Ca fait plus d'une heure que je suis dessus, sans arriver à trouver le script correct qui me permettrait de supprimer proprement une catégorie et tous ses descendants, à l'aide de requêtes et de boucles en cascades. Sachant bien entendu qu'il serait possible d'avoir 50 générations dans une catégorie...

À l'aide de ce bout de code, je peux supprimer la génération descendante directe (j'ai plusieurs autres traitements dans le while, utilisant l'ID renvoyé), mais pas toutes les autres qu'il peut y avoir après...

Code :

Code : Tout sélectionner

$rq1 = mysql_query("SELECT id_cat
				FROM categories
				WHERE parent_cat = $catcour");
while($res1 = mysql_fetch_array($rq1)) {
	mysql_query("DELETE FROM categories
				WHERE id_cat = $res1[0]");
	}
Si vous avez de quoi éclairer ma lanterne, ça serait formidable :D

Merci d'avance :)

Message envoyé avec : Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9b2) Gecko/2007121120 Firefox/3.0b2

Publié : 26 déc. 2007, 17:27
par Artengo34
Trouvé :mrgreen:

Voilà la réponse :

Code : Tout sélectionner

function delete_childs($id) {
	echo '-';
	$rq1 = mysql_query("SELECT count(*)
						FROM categoriestest
						WHERE parent_cat = $id");
	$res1 = mysql_fetch_array($rq1);
	if($res1[0] == 0) {
		mysql_query("DELETE FROM categoriestest WHERE id_cat = $id");
		echo $id.'<br>';
		}
	else {
		$rq2 = mysql_query("SELECT id_cat
						FROM categoriestest
						WHERE parent_cat = $id");
		while($res2 = mysql_fetch_array($rq2)) {
			delete_childs($res2[0]);
			}
		mysql_query("DELETE FROM categoriestest WHERE id_cat = $id");
		echo $id.'<br>';
		}
	}
Merci à Léortien qui m'a guidé sur Developpez.net :)

Message envoyé avec : Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9b2) Gecko/2007121120 Firefox/3.0b2

Publié : 26 déc. 2007, 17:43
par calimo
Hello,

La réponse simple, propre, sans code supplémentaire, c'est les foreign keys avec un CASCADE. Mais bon, dans MySQL...il faut obligatoirement utiliser le moteur InnoDB !

Code : Tout sélectionner

CREATE TABLE categories (
id_cat bigint(20) NOT NULL auto_increment,
lib_cat varchar(45) NOT NULL default '',
parent_cat bigint(20) NOT NULL default '0'
PRIMARY KEY (id_cat)
FOREIGN KEY (parent_cat) REFERENCES categories(id_cat) ON UPDATE CASCADE ON DELETE CASCADE
) TYPE=InnoDB; 
(Note: non testé.)
(Note2: le "ON UPDATE CASCADE" est moins utile)

Tu fais une clé étrangère qui référence la même table. Avec ça, quand tu supprimes une entrée, toutes celles qui lui font référence (enfants) sont supprimées également. Automatiquement. Bien plus simple que ta fonction :wink:
De plus, tu ne peux pas créer par accident une catégorie "isolée" qui ferait référence à un parent inexistant... bien pratique pour garantir l'intégrité de ta table :)

PS : tu as vraiment besoin d'un bigint pour ta clé primaire ?
PPS : Tu n'as pas besoin de définir explicitement "NOT NULL" sur la clé primaire, car une clé primaire est implicitement UNIQUE et NOT NULL. C'est donc redondant.

Message envoyé avec : Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.11) Gecko/20071204 Firebird/7.10 Firefox/2.0.0.11

Publié : 29 déc. 2007, 17:18
par Asumbaa
Salut,

Pour un truc comme ça avec les contraintes de MySQL, j'aurais fait toalement autrement, par exemple en créant la clé sur le modèle suivant :

Code : Tout sélectionner

Clé caté : 2.35.2.25.1
Ainsi pour supprimer toutes les catégories descendantes de la caté 2, tu fais un

Code : Tout sélectionner

delete from table where id_cat like '2.%'
Ça t'évite pas mal de traitements.

Message envoyé avec : Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11

Publié : 29 déc. 2007, 18:44
par calimo
Le problème ensuite c'est que si tu te rends compte que quelque chose vient avant 2, tu dois changer tous les 2. en 3. avant d'ajouter ta nouvelle catégorie 2, ce qui n'est de loin pas des plus pratique du tout :roll:

Message envoyé avec : Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.11) Gecko/20071204 Firegecko/7.10 Firefox/2.0.0.11

Publié : 30 déc. 2007, 01:17
par Asumbaa
calimo a écrit :Le problème ensuite c'est que si tu te rends compte que quelque chose vient avant 2, tu dois changer tous les 2. en 3. avant d'ajouter ta nouvelle catégorie 2, ce qui n'est de loin pas des plus pratique du tout :roll:
C'est pas faux :)
Séparer l'ordre d'affichage des identifiants type "cascade" ? Pas super pratique non plus, mais faisable... À voir :)

Message envoyé avec : Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11

Publié : 30 déc. 2007, 10:53
par Benoit
calimo a écrit :De plus, tu ne peux pas créer par accident une catégorie "isolée" qui ferait référence à un parent inexistant... bien pratique pour garantir l'intégrité de ta table :)
Oui, et comme ça si tu supprimes par erreur la catégorie zéro tu ne peux plus en recréer aucune, et il n'y a pas plus intègre qu'une table bien vide.

Je pense que le NOT NULL sur la catégorie parente n'est pas une très bonne idée. Il y a forcément une catégorie parente de toutes les autres.

Publié : 31 déc. 2007, 13:45
par calimo
Benoit a écrit :Oui, et comme ça si tu supprimes par erreur la catégorie zéro tu ne peux plus en recréer aucune, et il n'y a pas plus intègre qu'une table bien vide.
C'est sûr :lol:

Après, c'est certainement très personnel, mais je préfère risquer de tout supprimer (ce qu'on peut éviter de faire) que de devoir faire 25'000 vérification lors du traitement des données.
Benoit a écrit :Je pense que le NOT NULL sur la catégorie parente n'est pas une très bonne idée. Il y a forcément une catégorie parente de toutes les autres.
Ah oui je n'avais pas remarqué. :oops:

Ce qui nous donne quelque chose comme ça (de nouveau, je n'ai pas vérifié l'exactitude de la chose) :

Code : Tout sélectionner

CREATE TABLE categories (
  id_cat bigint(20) PRIMARY KEY auto_increment,
  lib_cat varchar(45) NOT NULL default '',
  parent_cat bigint(20) default '0' REFERENCES categories(id_cat) ON UPDATE CASCADE ON DELETE CASCADE
) TYPE=InnoDB;
Asumbaa a écrit :Séparer l'ordre d'affichage des identifiants type "cascade" ?
Pas sûr d'avoir bien compris ce que tu veux dire... :?

Message envoyé avec : Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.11) Gecko/20071204 Firelimace/7.10 Firefox/2.0.0.11

Publié : 31 déc. 2007, 14:29
par Asumbaa
calimo a écrit :
Asumbaa a écrit :Séparer l'ordre d'affichage des identifiants type "cascade" ?
Pas sûr d'avoir bien compris ce que tu veux dire... :?
Je viens de développer mon idée pour expliquer, et j'ai constaté que ça ne pouvait pas fonctionner :lol:
Je préfère pas préciser pour ne pas donner de mauvaises idées...

Message envoyé avec : Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11