2012-06-18 9 views
9

Tengo una entidad Doctrina con el campo de tipo de matriz:¿Cómo forzar a Doctrine a actualizar los campos de tipo de matriz?

/** 
* @ORM\Table() 
*/ 
class MyEntity 
{ 
    (...) 

    /** 
    * @var array $items 
    * 
    * @ORM\Column(type="array") 
    */ 
    private $items; 

    /** 
    * @param SomeItem $item 
    */ 
    public function addItem(SomeItem $item) 
    { 
     $this->items[] = $item; 
    } 

    (...) 
} 

Si añado elemento a la matriz, este código funciona correctamente:

$myEntityObject->addItems(new SomeItem()); 
$EntityManager->persist($myEntityObject); 
$EntityManager->flush(); 

$myEntityObject se guarda en la base de datos con los datos correctos (array se serializa y se deserializa al consultar la base de datos).

Desafortunadamente, cuando cambio uno de los objetos dentro de la matriz sin cambiar el tamaño de esa matriz, Doctrine no hace nada si intento guardar los cambios en la base de datos.

$items = $myEntityObject->getItems(); 
$items[0]->setSomething(123); 
$myEntityObject->setItems($items); 
$EntityManager->persist($myEntityObject); 
$EntityManager->flush(); 
print_r($myEntityObject); 

Aunque, print_r en la última línea de código que muestra los datos modificados de objetos, Doctrina no sabe que algo ha cambiado dentro de la matriz si tamaño de la matriz no cambió. ¿Hay alguna manera de forzar a Doctrine a guardar los cambios realizados en ese campo (o informarle suavemente sobre los cambios en ese campo que se deben guardar)?

Respuesta

21

Doctrine utiliza el operador idéntico (===) para comparar los cambios entre valores antiguos y nuevos. El operador utilizado en el mismo objeto (o conjunto de objetos) con datos diferentes siempre devuelve verdadero. Existe la otra forma de resolver este problema, puede clonar un objeto que necesita ser cambiado.

$items = $myEntityObject->getItems(); 
$items[0] = clone $items[0]; 
$items[0]->setSomething(123); 
$myEntityObject->setItems($items); 

// ... 

o cambiar el método setItems() (Tenemos que clonar un solo objeto a persistir toda la matriz)

public function setItems(array $items) 
{ 
    if (!empty($items) && $items === $this->items) { 
     reset($items); 
     $items[key($items)] = clone current($items); 
    } 
    $this->items = $items; 
} 

En cuanto a la segunda pregunta:

¿Alguien sabe cómo preservar la política de seguimiento predeterminada para otros campos y usa NotifyPropertyChanged solo para el campo que almacena la matriz?

No puede establecer la política de seguimiento solo para un campo.

+0

Estoy intentando esto y le atribuiré la bonificación lo antes posible si esto funciona. ¡Tu respuesta suena realmente bien! Muchas gracias @Vladim Ashikhman – Mick

+0

¡Asombroso! Solo puedo atribuir la recompensa en 2 horas, pero es todo tuyo. Pasé bastante tiempo en esto, y para ser honesto, no soy el único. La lógica es simplemente hermosa. ¡Eres increíble! Remita esta publicación a esta [pregunta] (http://stackoverflow.com/questions/13227658/doctrine-does-not-update-a-simple-array-type-field/13232142#13232142). Resulta ser un problema muy similar. ¡Bien hecho! – Mick

+1

He cambiado el setMethod() un poco, ahora funciona si cambias el cursor de la matriz. –

1

sólo se encuentra en la documentación de una manera de resolver mi problema:

http://docs.doctrine-project.org/en/latest/reference/change-tracking-policies.html

Se requiere una gran cantidad de cambios en el código, pero funciona. ¿Alguien sabe cómo preservar la política de seguimiento predeterminada para otros campos y usa NotifyPropertyChanged solo para el campo que almacena la matriz?

+0

Puede usar '$ uow-> propertyChanged ($ entity, $ propertyName, $ oldValue, $ newValue)'. Por ejemplo, en la devolución de llamada 'preUpdate'. – origaminal

1

La forma en que arreglé esto en mi código fue para usar createQueryBuilder y simplemente crear la consulta de actualización. Esta doctrina manera no tiene manera de decir que no :)

Así que pasó de este

$em   = $this->getDoctrine()->getManager(); 
$matchEntity = $em->getReference('MyBundleBundle:Match', $match_id); 


$matchEntity->setElement($element); 
$matchEntity->setTeamHomeColour($data['team_a_colour']); 
$matchEntity->setTeamAwayColour($data['team_b_colour']); 

A esto:

$repository = $this->getDoctrine()->getRepository('MyBundleBundle:Match'); 
$query  = $repository->createQueryBuilder('u') 
    ->update() 
    ->set('u.element', ':element') 
    ->set('u.teamHomeColour', ':thomecolour') 
    ->set('u.teamAwayColour', ':tawaycolour') 
    ->where('u.matchId = :match') 

    ->setParameter('element', $element) 
    ->setParameter('thomecolour', $data['team_a_colour']) 
    ->setParameter('tawaycolour', $data['team_b_colour']) 
    ->setParameter('match', $matchEntity) 

    ->getQuery(); 

$query->execute(); 

Es un poco más líneas de código, pero no hay una clonación o cualquier otro tipo de magia ¡Solo dile a doctrine directamente que hagas una maldita actualización! Espero que esto ayude.

Nota: En mi situación era el $ elemento que no se estaba estableciendo. Desactivé todas las coincidencias en una consulta previa y la doctrina simplemente no la veía, por lo que me negué a actualizar el elemento.

Cuestiones relacionadas