2012-06-18 9 views
9

Ho un'entità Doctrine con il campo di tipo array:Come forzare Doctrine ad aggiornare i campi del tipo di array?

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

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

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

    (...) 
} 

Se posso aggiungere elementi alla matrice, questo codice funziona correttamente:

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

$myEntityObject viene salvato nel database con i dati corretti (array è serializzato e deserializzato durante l'interrogazione del database).

Sfortunatamente, quando cambio uno degli oggetti all'interno dell'array senza cambiare la dimensione di quell'array, Doctrine non fa nulla se sto cercando di salvare le modifiche al database.

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

Anche se, print_r nell'ultima riga del display che codice modificato i dati dell'oggetto, Dottrina non sa che qualcosa era cambiato all'interno della matrice, se dimensione della matrice non è cambiato. Esiste un modo per forzare Doctrine a salvare le modifiche apportate in quel campo (o informarlo gentilmente delle modifiche in quel campo che devono essere salvate)?

risposta

21

Doctrine utilizza l'operatore identico (===) per confrontare le modifiche tra valori vecchi e nuovi. L'operatore utilizzato sullo stesso oggetto (o matrice di oggetti) con dati diversi restituisce sempre true. C'è un altro modo per risolvere questo problema, puoi clonare un oggetto che deve essere cambiato.

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

// ... 

o modificare il metodo setItems() (Abbiamo bisogno di clonare un solo oggetto a persistere l'intero array)

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

Per quanto riguarda la seconda domanda:

Qualcuno sa come preservare criterio di tracciamento predefinito per altri campi e utilizzare NotifyPropertyChanged solo per il campo che archivia la matrice?

Non è possibile impostare la politica di tracciamento solo per un campo.

+0

Sto provando questo e ti attribuirò il bonus al più presto se questo funziona. La tua risposta suona davvero bene! Mille grazie @Vladim Ashikhman – Mick

+0

Incredibile! Posso solo attribuire il premio in 2 ore, ma è tutto tuo. Ho passato un sacco di tempo su questo, e ad essere onesti, non sono l'unico. La logica è semplicemente bellissima. Sei fantastico! Per favore riferisci questo post a questa [domanda] (http://stackoverflow.com/questions/13227658/doctrine-does-not-update-a-simple-array-type-field/13232142#13232142). Risulta essere un problema molto simile. Molto bene! – Mick

+1

Ho cambiato un po 'il setMethod(), ora funziona se cambi il cursore dell'array. –

1

Proprio contenute nella documentazione che un modo per risolvere il mio problema:

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

richiede un sacco di cambiamenti nel codice, ma funziona. Qualcuno sa come preservare il criterio di tracciamento predefinito per altri campi e utilizzare NotifyPropertyChanged solo per il campo che memorizza l'array?

+0

È possibile utilizzare '$ uow-> propertyChanged ($ entity, $ propertyName, $ oldValue, $ newValue)'. Ad esempio nel callback 'preUpdate'. – origaminal

1

Il modo in cui l'ho risolto sul mio codice era utilizzare createQueryBuilder e creare la query di aggiornamento. Questa dottrina modo non ha alcun modo di dire no :)

Così sono andato da questo

$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 tal:

$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(); 

suo un paio di righe di codice, ma non v'è alcuna clonazione o qualsiasi altro tipo di magia. Dì solo alla dottrina di fare un dannato aggiornamento! Spero che questo ti aiuti.

Nota: nella mia situazione era l'elemento $ che non era impostato. Ho disinserito tutte le corrispondenze in una query precedente e la dottrina non l'ha visto e quindi ha rifiutato di aggiornare l'elemento.

Problemi correlati