2010-08-09 22 views
6

Je souhaite faire une copie/clone en profondeur d'un enregistrement de doctrine dans un projet symfony. La méthode de copie existante ($ deep) ne fonctionne pas correctement avec $ deep = true.copie profonde de l'enregistrement de doctrine

Pour un exemple, jetons un coup d'œil à une leçon en classe. Cette leçon a une date de début et de fin et entre eux il y a plusieurs pauses. Cette salle de classe est dans une construction. Leçon-break est une relation un-à-plusieurs, donc beaucoup de pauses pourraient être dans une leçon. la construction de leçons est une relation plusieurs-à-un, donc une leçon ne peut être que dans un bâtiment.

Si je veux faire une copie de la pièce, les ruptures doivent également être copiées. Le bâtiment devrait rester le même (aucune copie ici).

J'ai trouvé quelques exemples sur le web qui créent une classe PHP qui s'étend à partir de sfDoctrineRecord et remplace la méthode de copie.

Ce que j'ai essayé était:

class BaseDoctrineRecord extends sfDoctrineRecord { 
    public function copy($deep = false) { 
     $ret = parent::copy(false); 
     if (!$deep) 
      return $ret; 

     // ensure to have loaded all references (unlike Doctrine_Record) 
     foreach ($this->getTable()->getRelations() as $name => $relation) { 
      // ignore ONE sides of relationships 
      if ($relation->getType() == Doctrine_Relation::MANY) { 
       if (empty($this->$name)) 
        $this->loadReference($name); 

       // do the deep copy 
       foreach ($this->$name as $record) 
        $ret->{$name}[] = $record->copy($deep); 
      } 
     } 
     return $ret; 
    } 
} 

Maintenant, cela provoque un échec: Doctrine_Connection_Mysql_Exception: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '2-1' for key 'PRIMARY'

donc je dois « null » l'identifiant du nouveau record ($ ret) parce que cela devrait être un nouvel enregistrement. Où et comment pourrais/devrais-je le faire?

MISE À JOUR: L'erreur est fixé avec le code suivant:

class BaseDoctrineRecord extends sfDoctrineRecord { 
    public function copy($deep = false) { 
     $ret = parent::copy(false); 

     if($this->Table->getIdentifierType() === Doctrine_Core::IDENTIFIER_AUTOINC) { 
      $id = $this->Table->getIdentifier(); 
      $this->_data[$id] = null; 
     } 

     if(!$deep) { 
      return $ret; 
     } 

     // ensure to have loaded all references (unlike Doctrine_Record) 
     foreach($this->getTable()->getRelations() as $name => $relation) { 
      // ignore ONE sides of relationships 
      if($relation->getType() == Doctrine_Relation::MANY) { 
       if(empty($this->$name)) { 
        $this->loadReference($name); 
       } 

       // do the deep copy 
       foreach($this->$name as $record) { 
        $ret->{$name}[] = $record->copy($deep); 
       } 
      } 
     } 

     return $ret; 
    } 
} 

Mais cela ne fonctionne pas bien. Dans la leçon DoctrineCollection-> Pauses, toutes les nouvelles pauses sont bonnes. Mais ils ne sont pas sauvegardés dans la base de données. Je veux copier une leçon et ajouter 7 jours, il est temps:

foreach($new_shift->Breaks as $break) { 
    $break->start_at = $this->addOneWeek($break->start_at); 
    $break->end_at = $this->addOneWeek($break->end_at); 
    $break->save(); 
} 

Comme vous le voyez, les pauses sont sauvés, mais il semble qu'ils ne sont pas dans la db.

+0

J'ai écrit une méthode spécifique pour mes besoins. La solution générique produit plus de problèmes qu'elle n'en résout ... Eh bien, actuellement cela ne résout aucun problème du tout :) – hering

Répondre

0

Thomas le Tank Engine est en train de me projeter dans l'oreille, je ne peux donc pas vraiment me concentrer sur les points les plus précis de votre question, mais cela me semble familier. Est-ce que cela répond à votre question?

Copy a Doctrine object with all relations

Fondamentalement, la méthode Doctrine_Record::link() est votre ami :)

+1

en fait ... J'ai lu votre question mal. la réponse que j'ai donnée est pour quand vous voulez copier un disque, mais gardez les références originales, à savoir: * pas * copier les disques relatifs. Je suis prêt à parier que vous utilisez Doctrine 1.0. Le problème de copie (profonde) est résolu (avec beaucoup d'autres choses utiles comme synchronizeWithArray()) dans Doctrine 1.2. Lorsque vous copiez profondément dans Doctrine 1.2, il copiera et sauvegardera les références sans problème. Je ne sais pas si la mise à niveau vers 1.2 est une option pour vous si ... –

+1

En fait j'ai la version 1.4.4 :) – hering

+0

haha. oh oui ... peu importe: D –

0

Cela fonctionne pour moi, il est une variante du code question:

public function realCopy($deep = false) { 
    $ret = self::copy(false); 

    if(!$deep) { 
     return $ret; 
    } 

    // ensure to have loaded all references (unlike Doctrine_Record) 
    foreach($this->getTable()->getRelations() as $name => $relation) { 
     // ignore ONE sides of relationships 
     if($relation->getType() == Doctrine_Relation::MANY) { 
      if(empty($this->$name)) { 
       $this->loadReference($name); 
      } 

      // do the deep copy 
      foreach($this->$name as $record) { 
       $ret->{$name}[] = $record->realCopy($deep); 
      } 
     } 
    } 

    // this need to be at the end to ensure Doctrine is able to load the relations data 
    if($this->Table->getIdentifierType() === Doctrine_Core::IDENTIFIER_AUTOINC) { 
     $id = $this->Table->getIdentifier(); 
     $this->_data[$id] = null; 
    } 

    return $ret; 
} 

Je ne peux pas croire que je suis travailler avec Doctrine 1.2 en 2017.