Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
350 views
in Technique[技术] by (71.8m points)

php - Symfony2/Doctrine: How to re-save an entity with a OneToMany as a cascading new row

Firstly, this question is similar to How to re-save the entity as another row in Doctrine 2

The difference is that I'm trying to save the data within an entity that has a OneToMany relationship. I'd like to re-save the entity as a new row in the parent entity (on the "one" side) and then as new rows in each subsequent child (on the "many" side).

I've used a pretty simple example of a Classroom having many Pupils to keep it simple.

So me might have ClassroomA with id=1 and it has 5 pupils (ids 1 through 5). I'd like to know how I could, within Doctrine2, take that Entity and re-save it to the database (after potential data changes) all with new IDs throughout and the original rows being untouched during the persist/flush.

Lets first define our Doctrine Entities.

The Classroom Entity:

namespace AcmeTestBundleEntity;

use DoctrineORMMapping as ORM;
use DoctrineCommonCollectionsArrayCollection;

/**
 * @ORMEntity
 * @ORMTable(name="classroom")
 */
class Classroom
{
    /**
     * @ORMId
     * @ORMColumn(type="integer")
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORMColumn(type="string", length=255)
     */
    private $miscVars;  

   /**
     * @ORMOneToMany(targetEntity="Pupil", mappedBy="classroom")
     */
    protected $pupils;

    public function __construct()
    {
        $this->pupils = new ArrayCollection();
    }       
    // ========== GENERATED GETTER/SETTER FUNCTIONS BELOW ============

}

The Pupil Entity:

namespace AcmeTestBundleEntity;

use DoctrineORMMapping as ORM;
use DoctrineCommonCollectionsArrayCollection;

/**
 * @ORMEntity
 * @ORMTable(name="pupil")
 */
class Pupil
{
    /**
     * @ORMId
     * @ORMColumn(type="integer")
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORMColumn(type="string", length=255)
     */
    private $moreVars;

    /**
     * @ORMManyToOne(targetEntity="Classroom", inversedBy="pupils")
     * @ORMJoinColumn(name="classroom_id", referencedColumnName="id")
     */
    protected $classroom;   

    // ========== GENERATED FUNCTIONS BELOW ============
}

And our generic Action function:

public function someAction(Request $request, $id)
{
    $em = $this->getDoctrine()->getEntityManager();

    $classroom = $em->find('AcmeTestBundle:Classroom', $id);

    $form = $this->createForm(new ClassroomType(), $classroom);

    if ('POST' === $request->getMethod()) {
        $form->bindRequest($request);

        if ($form->isValid()) {
            // Normally you would do the following:
            $em->persist($classroom);
            $em->flush();

            // But how do I create a new row with a new ID 
            // Including new rows for the Many side of the relationship

            // ... other code goes here.
        }
    }

    return $this->render('AcmeTestBundle:Default:index.html.twig');
}

I've tried using clone but that only saved the parent relationship (Classroom in our example) with a fresh ID, while the children data (Pupils) was updated against the original IDs.

Thanks in advance to any assistance.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The thing with clone is...

When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. Any properties that are references to other variables, will remain references.

If you are using Doctrine >= 2.0.2, you can implement your own custom __clone() method:

public function __clone() {
    // Get current collection
    $pupils = $this->getPupils();

    $this->pupils = new ArrayCollection();
    foreach ($pupils as $pupil) {
        $clonePupil = clone $pupil;
        $this->pupils->add($clonePupil);
        $clonePupil->setClassroom($this);
    }
}

NOTE: before Doctrine 2.0.2 you cannot implement a __clone() method in your entity as the generated proxy class implements its own __clone() which does not check for or call parent::__clone(). So you'll have to make a separate method for that like clonePupils() (in Classroom) instead and call that after you clone the entity. Either way, you can use the same code inside your __clone() or clonePupils() methods.

When you clone your parent class, this function will create a new collection full of child object clones as well.

$cloneClassroom = clone $classroom;
$cloneClassroom->clonePupils();

$em->persist($cloneClassroom);
$em->flush();

You'll probably want to cascade persist on your $pupils collection to make persisting easier, eg

/**
 * @ORMOneToMany(targetEntity="Pupil", mappedBy="classroom", cascade={"persist"})
 */
protected $pupils;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...