symfony2 findonebyid español entidad create crear consultas php doctrine symfony doctrine-orm clone

php - findonebyid - Symfony2/Doctrine: Cómo volver a guardar una entidad con OneToMany como una nueva fila en cascada



symfony create entity (3)

Hago esto:

if ($form->isValid()) { foreach($classroom->getPupils() as $pupil) { $pupil->setClassroom($classroom); } $em->persist($classroom); $em->flush(); }

En primer lugar, esta pregunta es similar a Cómo volver a guardar la entidad como otra fila en Doctrine 2

La diferencia es que estoy tratando de guardar los datos dentro de una entidad que tiene una relación OneToMany. Me gustaría volver a guardar la entidad como una nueva fila en la entidad principal (en el lado "uno") y luego como nuevas filas en cada elemento secundario posterior (en el lado "muchos").

He usado un ejemplo bastante simple de un aula que tiene muchos alumnos para que sea sencillo.

Así que yo podría tener ClassroomA con id = 1 y tiene 5 alumnos (ids 1 a 5). Me gustaría saber cómo podría, dentro de Doctrine2, tomar esa Entidad y volver a guardarla en la base de datos (después de posibles cambios en los datos), todos con nuevos ID y las filas originales que no se tocaron durante la persistencia / vaciado.

Primero definamos nuestras Entidades de Doctrina.

La entidad del aula:

namespace Acme/TestBundle/Entity; use Doctrine/ORM/Mapping as ORM; use Doctrine/Common/Collections/ArrayCollection; /** * @ORM/Entity * @ORM/Table(name="classroom") */ class Classroom { /** * @ORM/Id * @ORM/Column(type="integer") * @ORM/GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM/Column(type="string", length=255) */ private $miscVars; /** * @ORM/OneToMany(targetEntity="Pupil", mappedBy="classroom") */ protected $pupils; public function __construct() { $this->pupils = new ArrayCollection(); } // ========== GENERATED GETTER/SETTER FUNCTIONS BELOW ============ }

La Entidad Estudiantil:

namespace Acme/TestBundle/Entity; use Doctrine/ORM/Mapping as ORM; use Doctrine/Common/Collections/ArrayCollection; /** * @ORM/Entity * @ORM/Table(name="pupil") */ class Pupil { /** * @ORM/Id * @ORM/Column(type="integer") * @ORM/GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM/Column(type="string", length=255) */ private $moreVars; /** * @ORM/ManyToOne(targetEntity="Classroom", inversedBy="pupils") * @ORM/JoinColumn(name="classroom_id", referencedColumnName="id") */ protected $classroom; // ========== GENERATED FUNCTIONS BELOW ============ }

Y nuestra función de acción genérica:

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

He intentado usar el clon, pero eso solo guardó la relación de padres (Classroom en nuestro ejemplo) con una ID nueva, mientras que los datos de los niños (Pupils) se actualizaron con las ID originales.

Gracias de antemano a cualquier ayuda.


La cosa con el clone es ...

Cuando se clona un objeto, PHP 5 realizará una copia superficial de todas las propiedades del objeto. Todas las propiedades que sean referencias a otras variables, seguirán siendo referencias.

Si está utilizando Doctrine> = 2.0.2, puede implementar su propio método __clone () personalizado:

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

NOTA: antes de Doctrine 2.0.2 no puede implementar un método __clone() en su entidad, ya que la clase proxy generada implementa su propio __clone() que no verifica ni llama a parent::__clone() . Así que tendrás que hacer un método separado para eso como clonePupils() (en el Classroom ) y llamar a eso después de clonar la entidad. De cualquier manera, puede usar el mismo código dentro de sus __clone() o clonePupils() .

Cuando clona su clase principal, esta función creará también una nueva colección llena de clones de objetos secundarios.

$cloneClassroom = clone $classroom; $cloneClassroom->clonePupils(); $em->persist($cloneClassroom); $em->flush();

Probablemente querrá hacer una persistencia en cascada en su colección de $pupils para que la persistencia sea más fácil, por ejemplo

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


Lo hice así y funciona bien.

Dentro de la entidad clonada tenemos magia __clone () . Allí tampoco nos olvidamos de uno a muchos .

/** * Clone element with values */ public function __clone(){ // we gonna clone existing element if($this->id){ // get values (one-to-many) /** @var /Doctrine/Common/Collections/Collection $values */ $values = $this->getElementValues(); // reset id $this->id = null; // reset values $this->elementValues = new /Doctrine/Common/Collections/ArrayCollection(); // if we had values if(!$values->isEmpty()){ foreach ($values as $value) { // clone it $clonedValue = clone $value; // add to collection $this->addElementValues($clonedValue); } } } } /** * addElementValues * * @param /YourBundle/Entity/ElementValue $elementValue * @return Element */ public function addElementValues(/YourBundle/Entity/ElementValue $elementValue) { if (!$this->getElementValues()->contains($elementValue)) { $this->elementValues[] = $elementValue; $elementValue->setElement($this); } return $this; }

En algún lugar simplemente clonarlo:

// Returns /YourBundle/Entity/Element which we wants to clone $clonedEntity = clone $this->getElement(); // Do this to say doctrine that we have new object $this->em->persist($clonedEntity); // flush it to base $this->em->flush();