Bonjour à tous,

je suis toujours sur mes problèmes de relations n-n réflexives. Il s'agit ici de produits croisés : un produit peut pointer 0-n autres produits (qui seront proposés comme produits similaires au visiteur du site).
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
 
Product:
  columns:
    name:
      type: string(45)
      notnull: true
  relations:
    Links:
      refClass: ProductLink
      class: Product
      local: product_id
      foreign: linked_product_id
 
ProductLink:
  columns:
    product_id:
      type: integer
      primary: true
      notnull: true
    linked_product_id:
      type: integer
      primary: true
      notnull: true
  relations:
    LinkingProduct:
      class: Product
      local: product_id
      foreignAlias: AsLinker
      onDelete : cascade
      onUpdate : cascade
    LinkedProduct:
      class: Product
      local: linked_product_id
      foreignAlias: AsLinked
      onDelete : cascade
      onUpdate : cascade
Avec ce schéma, Symfony me génère des modèles et des formulaires qui me vont bien, je peux faire mes associations. Mais j'ai besoin de rendre la relation symétrique : si un produit P1 est lié à un produit P2, alors P2 doit être aussi lié à P1. Pour y arriver, mon idée était de surcharger le doSave() du formulaire pour y ajouter les ProductLinks ad hoc et sauvegarder tout ça. La tentative plante lamentablement, je désactive mon code, je mets des traces dans le doSave() :
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
 
    protected function doSave($conn = null) {
	sfContext::getInstance()->getLogger()->debug(__METHOD__ . " avant parent::doSave ligne " . __LINE__);
	parent::doSave($conn);
	sfContext::getInstance()->getLogger()->debug(__METHOD__ . " après parent::doSave ligne " . __LINE__);
	$product = $this->getObject();
 
	// La relation 'produits croisés' est commutative : on s'assure donc que les entrées symétriques
	// à celles du formulaire existent : si #6 est lié à #7, #7 sera lié à #6.
	$linkedProducts = $product->getLinks();
	foreach ($linkedProducts as $linked) {
	    $lp = $product->createLinker($linked);
	    sfContext::getInstance()->getLogger()->debug(__METHOD__ . " avant save ligne " . __LINE__);
	    $lp->save();
	    sfContext::getInstance()->getLogger()->debug(__METHOD__ . " après save ligne " . __LINE__);
	}
    }
et voici le résultat : le produit qui fait les liens est celui d'id #3, les 3 produits liés sont d'id 1, 2, 4 :
Dec 02 11:02:20 symfony [debug] ProductForm::doSave avant parent::doSave ligne 84
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : DELETE FROM product_link WHERE (linked_product_id = ? AND product_id IN (?, ?, ?)) - (3, 1, 2, 4)
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : UPDATE product_link SET linked_product_id = ? WHERE product_id = ? AND linked_product_id = ? - (1, 1, 3)
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : UPDATE product_link SET linked_product_id = ? WHERE product_id = ? AND linked_product_id = ? - (2, 2, 3)
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : UPDATE product_link SET linked_product_id = ? WHERE product_id = ? AND linked_product_id = ? - (4, 4, 3)
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : DELETE FROM product_link WHERE product_id = ? AND linked_product_id = ? - (1, 3)
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : DELETE FROM product_link WHERE product_id = ? AND linked_product_id = ? - (2, 3)
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : DELETE FROM product_link WHERE product_id = ? AND linked_product_id = ? - (4, 3)
Dec 02 11:02:20 symfony [debug] ProductForm::doSave après parent::doSave ligne 86
Dec 02 11:02:20 symfony [debug] ProductForm::doSave avant save ligne 94
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : INSERT INTO product_link (linked_product_id, product_id, created_at) VALUES (?, ?, ?) - (3, 1, 2010-12-02 11:02:20)
Dec 02 11:02:20 symfony [debug] ProductForm::doSave après save ligne 96
Dec 02 11:02:20 symfony [debug] ProductForm::doSave avant save ligne 94
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : INSERT INTO product_link (linked_product_id, product_id, created_at) VALUES (?, ?, ?) - (3, 2, 2010-12-02 11:02:20)
Dec 02 11:02:20 symfony [debug] ProductForm::doSave après save ligne 96
Dec 02 11:02:20 symfony [debug] ProductForm::doSave avant save ligne 94
Dec 02 11:02:20 symfony [info] {Doctrine_Connection_Statement} execute : INSERT INTO product_link (linked_product_id, product_id, created_at) VALUES (?, ?, ?) - (3, 4, 2010-12-02 11:02:20)
Dec 02 11:02:20 symfony [debug] ProductForm::doSave après save ligne 96


Le premier DELETE me laisse déjà perplexe car Symfony me supprime d'office tous les liens pointant vers mon produit, ce qui n'est pas demandé ici.

Les trois UPDATE qui suivent, je ne les comprends tout simplement pas : ils vont créer dans la table ProductLink des entrées avec des produits se pointant eux-mêmes, ce qui est évidemment faux.

Et ces entrées calamiteuses sont de toutes façons supprimées par les 3 DELETE qui suivent...

Toutes ces requêtes abracadabrantes disparaissent si je retire ma surcharge, bien sûr. Mais je ne comprends pas
- pourquoi Symfony génère ces requêtes aussi aberrantes ?
- comment faire pour automatiser la création de liens symétriques ?