开发者

deep copy of doctrine record

开发者 https://www.devze.com 2023-01-10 23:36 出处:网络
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.

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 开发者_如何学Gorelationships
            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.


This works for me, it's a variant from the question code:

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;
}

I can't believe I'm working with Doctrine 1.2 in 2017.

0

精彩评论

暂无评论...
验证码 换一张
取 消