2010-04-28 5 views
13

Grails offre la possibilità di creare e associare automaticamente oggetti di dominio a un elenco di molti, come descritto nello grails user guide.Grails - Rimozione di un elemento da un'associazione hasMany Elenco sul binding dei dati?

Così, per esempio, se il mio dominio oggetto "Autore" ha una lista di molti oggetti "libro", ho potuto creare e associare questi utilizzando il seguente markup (dal manuale d'uso):

<g:textField name="books[0].title" value="the Stand" /> 
<g:textField name="books[1].title" value="the Shining" /> 
<g:textField name="books[2].title" value="Red Madder" /> 

In questo caso, se uno dei libri specificati non esiste già, Grails li creerà e imposterà i titoli in modo appropriato. Se ci sono già libri negli indici specificati, i loro titoli saranno aggiornati e saranno salvati. La mia domanda è: c'è un modo semplice per dire a Grails di rimuovere uno di quei libri dall'associazione "libri" sul data bind?

Il modo più ovvio per farlo sarebbe quello di omettere l'elemento del modulo che corrisponde all'istanza del dominio che si desidera eliminare; Purtroppo, questo non funziona, come da manuale d'uso:

Poi Grails crea automaticamente una nuova istanza per voi al definita posizione. Se hai "saltato" alcuni elementi nel mezzo ... Allora Grails creerà automaticamente delle istanze tra .

Mi rendo conto che una soluzione specifica potrebbe essere progettata come parte di un oggetto comando o come parte di un particolare controller, tuttavia, la necessità di questa funzionalità appare ripetutamente in tutta la mia applicazione, su più oggetti di dominio e per associazioni di molti diversi tipi di oggetti. Una soluzione generale, quindi, sarebbe l'ideale. Qualcuno sa se c'è qualcosa di simile incluso in Grails?

risposta

0

Sto appena iniziando a imparare Grail e ho visto la tua domanda come un interessante esercizio di ricerca per me. Non penso che tu possa usare il convenzionale meccanismo di associazione dei dati, poiché riempie gli spazi vuoti usando una specie di mappa pigra dietro le quinte. Quindi, per voi per raggiungere il tuo obiettivo il metodo di "salvare" (? o è una funzione) è improbabile per contenere qualcosa di simile:

def Book = new Book(params) 

Hai bisogno di un meccanismo per modificare il metodo "salvare" del controller.

Dopo alcune ricerche, ho appreso che è possibile modificare il modello di scaffold che è responsabile della generazione del codice del controller o dei metodi di runtime. Puoi ottenere una copia di tutti i modelli utilizzati da Grails eseguendo "modelli di installazione di grails" e il file modello che dovresti modificare è chiamato "Controller.groovy".

Quindi in teoria, è possibile modificare il metodo "salva" per l'intera applicazione in questo modo.

Grande! Penseresti che tutto ciò che devi fare ora è modificare il tuo metodo di salvataggio nel modello in modo che itera attraverso le voci dell'oggetto (ad es. Libri) nella mappa params, salvando ed eliminando man mano che vai.


Tuttavia, penso che la soluzione richiesta potrebbe essere ancora abbastanza problematica da raggiungere. Il mio istinto mi dice che ci sono molte ragioni per cui il meccanismo che suggerisci è una cattiva idea.

Per una ragione, in cima alla mia testa, immagina di avere un elenco impaginato di libri. Potrebbe significare che il tuo "salvataggio" potrebbe cancellare l'intera tabella del database eccetto la pagina attualmente visibile? Ok, diciamo che riesci a capire quanti elementi sono visualizzati su ogni pagina, e se l'elenco fosse ordinato in modo che non fosse più in ordine numerico - cosa cancellerai adesso?

Forse più pulsanti di invio nel modulo potrebbero essere un approccio migliore (ad esempio, salvare le modifiche, aggiungere, eliminare). Non ho provato questo tipo di cose in Grails, ma capisco che actionSubmit dovrebbe aiutarti a ottenere più pulsanti di invio. Certamente facevo questo genere di cose in Struts!

HTH

12

Mi sono imbattuto in questo problema. È facile da risolvere. Grails usa java.util.Set per rappresentare gli elenchi. Puoi semplicemente utilizzare il metodo clear() per cancellare i dati, quindi aggiungere quelli che desideri.

//clear all documents 
bidRequest.documents.clear() 

//add the selected ones back 
params.documentId.each() { 
    def Document document = Document.get(it) 
    bidRequest.documents.add(document) 
    log.debug("in associateDocuments: added " + document) 
}; 

//try to save the changes 
if (!bidRequest.save(flush: true)) { 
    return error() 
} else { 
    flash.message = "Successfully associated documents" 
} 

scommetto che si può fare la stessa cosa utilizzando il metodo "remove()" nel caso in cui non si vuole "clear()" tutti i dati.

+0

ho trovato che "bidRequest.documents = []" funziona meglio dal momento che i documenti potrebbero essere nulli. – mlathe

0

Mi sto solo imbattendo in questo stesso problema.

Il dominio della mia applicazione è piuttosto semplice: ha oggetti Stub che hanno una relazione MOLTA con gli oggetti Intestazione. Poiché gli oggetti Header non hanno vita propria, sono interamente gestiti dal controller e dalle viste Stub.

Le definizioni di classi di dominio:

class Stub { 
List headers = new ArrayList(); 
static hasMany = [headers:Header] 
static mapping = {headers lazy: false} 
} 

class Header { 
String value 
static belongsTo = Stub  
} 

che ho provato il metodo "chiaro e legare", ma il risultato finale è che gli oggetti "liquidati" vengono lasciati nel database e Grails sarà solo creare nuovi istanze per quelli che non sono stati rimossi dalla relazione. Sembra funzionare dal punto di vista dell'utente, ma lascerà molti oggetti inutili nel database.

Il codice nel metodo update() del controllore è:

stubInstance.headers.clear() 
stubInstance.properties = params 

Un esempio: durante la modifica del lato-molti di questo rapporto ho (per un dato stub con id = 1):

<g:textField name="headers[0].value" value="zero" id=1 /> 
<g:textField name="headers[1].value" value="one" id=2 /> 
<g:textField name="headers[2].value" value="two" id=3 /> 

nel database ci sono 3 istanze di Header:

id=1;value="zero" 
id=2;value="one" 
id=3;value"two" 

dopo la rimozione di intestazione "uno" e salvare il S vasca oggetto la banca dati avrà intestazioni:

id=1;value="zero" 
id=2;value="one" 
id=3;value"two" 
id=4;value="zero" 
id=5;value="two" 

e l'oggetto Stub avranno ora un'associazione con intestazioni con id = 4 e id = 5 ...

Inoltre, senza la compensazione della lista, se un indice non è presente nell'elenco request.headers presentato, sui grails vincolanti i dati manterrà inalterato l'oggetto esistente in quella posizione.

La soluzione che mi viene in mente è di associare i dati, quindi controllare le intestazioni di Stub per gli elementi che non sono presenti nell'elenco presentato e rimuoverli.

Questo sembra uno scenario piuttosto semplice, non esiste alcuna funzionalità integrata per affrontarlo? È un po 'eccessivo dover scrivere la propria logica di sincronizzazione per mantenere le relazioni, specialmente quando le stranezze che la rendono non banale sono causate dallo stesso graal.

Che dire dell'eliminazione, gli elementi clear() non dovrebbero essere eliminati dal database? Mi manca qualcosa nella relazione o nelle definizioni di oggetti di dominio?

+1

Alla fine ho trovato una risposta a tutte le mie domande qui: http://omarello.com/2010/08/grails-one-to-many-dynamic-forms/. In poche parole: aggiungi una nuova proprietà ai tuoi oggetti per passare da "eliminata" alla vista, quindi nel controller leghi e rimuovi tutti gli oggetti con deleted = true. –

3

Per una buona spiegazione di eliminazione di una collezione di oggetti figlio con GORM uno sguardo alla sezione Eliminazione dei bambini di questo post del blog - GORM gotchas part 2

Si consiglia la lettura, come lo sono le parti 1 e 3 della serie.

0
class Stub { 
    List headers = new ArrayList(); 
    static hasMany = [headers:Header] 
    static mapping = { 
    headers lazy: false 
    **headers cascade: "all-delete-orphan"** 
    } 
    } 

class Header { 
    String value 
    static belongsTo = Stub  
} 

ho aggiunto la proprietà a cascata sul lato possessore di relazione e Ora, se si tenta di salvare lo stub, che si prenderà cura di rimuovere gli elementi eliminati dalla raccolta ed eliminarli dal database.

11

removeFrom*

fronte del metodo addto in quanto rimuove le istanze di un'associazione.

Esempi

def author = Author.findByName("Stephen King") 

def book = author.books.find { it.title = 'The Stand' } 

author.removeFromBooks(book)