2011-11-25 10 views
11

Sto cercando dei modi su come pulire il mio codice del controller Grails. In vari controller i più o meno ho la stessa logica ..Rendere i controller Grails più ASCIUTTI?

  • ottenere l'oggetto
  • verifica se esiste
  • ecc ..

c'è un modo suggerito a fare azioni di controllo riutilizzare il codice comune?

--- --- soluzione

Tutte le risposte alla domanda hanno contribuito alla soluzione che abbiamo implementato.

Abbiamo creato una classe che viene utilizzata nei nostri controller utilizzando l'approccio Mixin. Uno dei metodi che espone il mixin è il metodo withObject. Questo metodo prende il domainname dal controller e lo usa come base per il metodo. Questo comportamento può essere sovrascritto, ovviamente!

def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) { 
    assert object 
    def obj = grailsApplication.classLoader.loadClass(object).get(params[id]) 
    if(obj) { 
     c.call obj 
    } else { 
     flash.message = "The object was not found" 
     redirect action: "list" 
    } 
} 

Quindi tutte le risposte hanno contribuito alla soluzione! Molte grazie!

+4

Modifica la domanda e aggiungi un esempio del tuo codice. Al momento la domanda è molto vaga. – ordnungswidrig

risposta

8

ho sempre tirare fuori questo post del blog quando questa domanda viene in su:

http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/

In pratica si hanno un supporto privato di vari domini in vostro controller.

private def withPerson(id="id", Closure c) { 
    def person = Person.get(params[id]) 
    if(person) { 
     c.call person 
    } else { 
     flash.message = "The person was not found." 
     redirect action:"list" 
    } 
} 

Il modo in cui si codifica il getter è molto flessibile e un utilizzo tipico per me (che non è coperto nel blog) è per l'editing etc.

Io di solito il codice in questo modo (mi piace il modello per la sua divisione chiara e la leggibilità):

def editIssue() { 
    withIssue { Issue issue -> 
     def issueTypes = IssueTypeEnum.values().collect {it.text } 
     [issueTypes:issueTypes,activePage:"issue", issue: issue] 
    } 
} 

def doEditIssue(IssueCommand cmd) { 
    if(cmd.validate()) { 
     withIssue { Issue issue -> 
      issue.updateIssue(cmd) 
      redirect(action: "show", id: issue.id) 
     } 
    } 
    else { 
     def issueTypes = IssueTypeEnum.values().collect {it.text } 
     render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"]) 
    } 
} 

Con la mia getter aiutante essere:

private def withIssue(Closure c) { 
    def issue = Issue.get(params.id) 
    if(issue) { 
     c.call issue 
    } 
    else { 
     response.sendError(404) 
    } 
} 

penso che il metodo mixin (molto simile al "estendere un modo comune di controllo astratto" è anche bello, ma in questo modo offre due vantaggi:

  1. Si puoi digitare l'helper, come puoi vedere nella chiusura che ti dà accesso ai metodi ecc in STS/IDEA (non testato Netbeans)
  2. La ripetizione non è molto alta, e la possibilità di cambiare il getter (da usare per esempio BarDomain.findByFoo (params.id) ecc)

nella vista mi legano a modificare() ho appena messo un id="${issue.id}" nel <g:form> e funziona senza problemi.

+0

Grazie per i tuoi commenti ho unito più o meno qualche risposta in questa discussione a una soluzione di lavoro al mio fianco. Uso la strategia di mixin insieme a un metodo withObject generico in qualche modo. Aggiornerò la mia domanda con il codice che sto usando ora. – Marco

0

Implementare controller astratto con metodi comuni (utilizzare la direttiva "protetta") ed estendere i propri controller reali. Non utilizzare le parole 'get' e 'set' all'inizio dei nomi di questo metodo. Non va bene, ma funziona.

+1

Perché suggerisci di non utilizzare le parole 'ottieni' e 'imposta' all'inizio dei nomi di questo metodo? – gotomanners

+0

per evitare di identificarli come 'getter' o 'setter'. – jenk

6

Non consiglierei l'ereditarietà perché non è possibile distribuire metodi generici in più super classi. La tua classe astratta diventerebbe rapidamente disordinata se hai molti controller. Non è possibile utilizzare la composizione (ad esempio utilizzando un servizio) perché non si ha accesso a response, render o params direttamente da lì.

L'approccio che uso è quello di iniettare metodi generici tramite Mixins.

@Mixin(ControllerGenericActions) 
@Mixin(ControllerUtil) 
class BookController { 
    def show = &genericShow.curry(Book) 

    def exists = { 
    render(idExists(Book)) 
    } 
} 

La prima azione show utilizza un metodo generico in ControllerGenericActions.groovy, con un argomento rilegata ad esso. Il secondo utilizzo di un metodo mixin idExists si trova all'interno di un'azione del controller.

Ecco un codice di esempio per src/groovy/ControllerGenericActions.groovy

class ControllerGeneric { 
    def genericShow(Class clazz) { 
    render clazz.get(params.id) as XML 
    } 
} 

e in src/groovy/ControllerUtil.groovy

class ControllerUtil { 
    def idExists (Class clazz) { 
    return clazz.get(params.id) != null 
    } 

Non molto utile in questo caso, ma si ottiene l'idea.

+0

Non sono molto interessato all'uso delle chiusure, quindi per favore perdonami se sto facendo un errore. Ma se viene creato un metodo come "genericShow (Class clazz)" puoi curry quel metodo? Ho sempre avuto l'impressione che un metodo non possa essere curato ma una chiusura può. – Marco

+0

Questo è il motivo dell'uso e prima del nome del metodo: trasforma un metodo in una chiusura. – Antoine

+0

Ho una domanda su questa soluzione: l'unità di mixaggio ControllerGeneric è testabile? Chiedo perché 'rendering' è un metodo fornito da Grails migliorando i controller –