I want to make a deep copy/clone of a doctrine record in a symfony project. The existing copy($deep)-method doesn't work properly with $deep=true.
For an example let's have a look at a classroom lesson. This lesson has a start and end date and between them there are several breaks. This classroom is in a buildung.
lesson-break is a one-to-many relationship, so a lot of breaks could be inside a lesson. lesson-building is a many-to-one relationship, so a lesson could only be in ONE Building.
If I want to make a copy of the room the breaks should be copied also. The building should stay the same (no copy here).
I found some examples on the web which create a PHP class which extends from the sfDoctrineRecord and overrides the copy-method.
What I tried was:
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;
}
}
Now this causes in a failure: Doctrine_Connection_Mysql_Exception: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '2-1' for key 'PRIMARY'
So I need to "null" the id of the new record ($ret) because this should be a new record. Where and how could/should I do it?
UPDATE: The error is fixed with following code: `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;
}
}`
But it doesn't work well. In the DoctrineCollection lesson->Breaks all new breaks are fine. But they aren't saved in the database.
I want to copy a lesson and add 7 days to it's time:
foreach($new_shift->Breaks as $break) {
$break->start_at = $this->addOneWeek($break->start_at);
$break->end_at = $this->addOneWeek($break->end_at);
$break->save();
}
So as you see, the breaks are saved, but it seems they are not in the db.