11 votes

Rendre les contrôleurs Grails plus DRY ?

Je cherche des moyens de nettoyer le code de mon contrôleur Grails. Dans plusieurs contrôleurs, j'ai plus ou moins la même logique

  • obtenir l'objet
  • vérifier s'il existe
  • etc.

Existe-t-il une suggestion pour que les actions du contrôleur réutilisent un code commun ?

--- solution ---

Toutes les réponses à cette question ont contribué à la solution que nous avons mise en œuvre.

Nous avons créé une classe qui est utilisée dans nos contrôleurs en utilisant l'approche Mixin. L'une des méthodes exposées par le mixin est la méthode withObject. Cette méthode prend le nom de domaine du contrôleur et l'utilise comme base pour la méthode. Ce comportement peut bien sûr être modifié !

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"
    }
}

Toutes les réponses ont donc contribué à la solution ! Merci beaucoup !

8voto

Oliver Tynes Points 944

Je sors toujours cet article de blog lorsque cette question se pose :

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

En fait, vous avez une aide privée pour différents domaines dans vos contrôleurs.

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"
    }
}

La façon dont vous codez le getter est très flexible et une utilisation typique pour moi (qui n'est pas couverte dans le blog) est pour l'édition, etc.

Je code normalement de cette façon (j'aime le modèle pour sa division claire et sa lisibilité) :

 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"])
    }
}

Avec mon aide de getter étant :

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

Je pense que la méthode des mixins (très similaire à la méthode "étendre un contrôleur abstrait commun") est également intéressante, mais cette méthode présente deux avantages :

  1. Vous pouvez taper l'aide, comme vous voyez que je le fais dans la fermeture vous donnant accès aux méthodes etc dans STS/IDEA (pas testé Netbeans).
  2. La répétition n'est pas très élevée, et la possibilité de changer le getter (pour utiliser par exemple BarDomain.findByFoo(params.id) etc)

Dans la vue que je lie à edit(), j'ai juste mis un fichier id="${issue.id}" dans le <g:form> et cela fonctionne parfaitement.

6voto

Antoine Points 2887

Je ne recommanderais pas l'héritage pour cela, car vous ne pouvez pas répartir les méthodes génériques dans plusieurs super classes. Votre classe abstraite deviendrait rapidement désordonnée si vous avez beaucoup de contrôleurs. Vous ne pouvez pas utiliser la composition (par exemple en utilisant un Service) parce que vous n'avez pas accès à response , render ou params directement à partir de là.

L'approche que j'utilise consiste à injecter des méthodes génériques via des Mixins.

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

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

La première action show utilise une méthode générique dans ControllerGenericActions.groovy avec un argument qui lui est lié. La deuxième utilisation d'un mixin idExists se trouve dans une action du contrôleur.

Voici un exemple de code pour src/groovy/ControllerGenericActions.groovy

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

et en src/groovy/ControllerUtil.groovy

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

Pas très utile dans ce cas, mais vous voyez l'idée.

0voto

jenk Points 1033

Implémentez un contrôleur abstrait avec des méthodes communes (utilisez la directive 'protected') et étendez vos contrôleurs réels à partir de celui-ci. N'utilisez pas les mots 'get' et 'set' au début des noms de ces méthodes. Ce n'est pas bon, mais ça marche.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X