让 Grails 控制器更加 DRY?

发布于 2024-12-17 17:37:58 字数 663 浏览 0 评论 0原文

我正在寻找清理 Grails 控制器代码的方法。在各种控制器中,我或多或少具有相同的逻辑..

  • 获取对象
  • 检查它是否存在
  • 等..

是否有建议的方法使控制器操作重用通用代码?

--- 解决方案 ---

问题的所有答案都有助于我们实施的解决方案。

我们使用 Mixin 方法创建了一个在控制器中使用的类。 mixin 公开的方法之一是 withObject 方法。此方法从控制器获取域名并将其用作该方法的基础。当然可以覆盖此行为!

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

所以所有的答案都有助于解决问题!多谢!

I am looking for ways on how to cleanup my Grails controller code. In various controllers i more or less have the same logic..

  • get the object
  • check if it exists
  • etc..

Is there a suggested way on making controller actions reuse common code?

--- solution ---

All answers to the question have contributed to the solution we have implemented.

We created a class that is used in our controllers using the Mixin approach. One of the methods that the mixin exposes is the withObject method. This method takes the domainname from the controller and uses this a base for the method. This behaviour can be overridden of course!

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

So all answers have contributed to the solution! Thanks a lot!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

吾性傲以野 2024-12-24 17:37:58

当这个问题出现时,我总是拿出这篇博客文章:

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

基本上你在控制器中为各个域提供一个私人助手。

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

对getter 进行编码的方式非常灵活,对我来说(博客中未介绍)的典型用途是编辑等。

这种方式进行编码(我喜欢这种模式,因为它具有清晰的划分和可读性):

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

我通常以 helper 是:

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

我确实认为 mixin 方法(与“扩展通用抽象控制器”方式非常相似)也很好,但这种方式有两个优点:

  1. 您可以输入 helper,就像您在闭包中看到的那样您可以访问方法等STS/IDEA(未测试 Netbeans)
  2. 重复率不是很高,并且能够更改 getter(例如使用 BarDomain.findByFoo(params.id) 等)

在我绑定到 edit() 的视图中,我只是放置了一个 中的 id="${issue.id}" 并且可以无缝运行。

I always pull out this blog post when this question comes up:

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

Basically you have a private helper for various domains in your controllers.

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

The way you code the getter is very flexible and a typical use for me (that is not covered in the blog) is for editing etc.

I normally code this way (i like the pattern for its clear division and readability):

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

With my getter helper being:

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

I do think that the mixin method (very similar to the 'extend a common abstract controller' way) is nice too, but this way gives two advantages:

  1. You can type the helper, like you see I do in the closure giving you access to the methods etc in STS/IDEA (not tested Netbeans)
  2. The repetition is not very high, and the ability to change the getter (to use for example BarDomain.findByFoo(params.id) etc)

In the view I bind to edit() I just put an id="${issue.id}" in the <g:form> and it works seamlessly.

蓝海 2024-12-24 17:37:58

我不建议为此使用继承,因为您不能在多个超类中传播通用方法。如果您有很多控制器,您的抽象类很快就会变得混乱。您无法使用组合(例如使用服务),因为您无法直接从以下位置访问 responserenderparams那里。

我使用的方法是通过 Mixins 注入通用方法。

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

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

第一个操作 show 使用 ControllerGenericActions.groovy 中的通用方法,并绑定了一个参数。 mixin idExists 方法的第二次使用是在控制器操作内部。

下面是 src/groovy/ControllerGenericActions.groovy

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

和 src/groovy/ControllerUtil.groovy 中

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

的示例代码,在这种情况下不是很有用,但您明白了。

I wouldn't recommend inheritance for that, as you can't spread generic methods in several super classes. Your abstract class would quickly become messy if you have many controllers. You can't use composition (for instance using a Service) because you don't have access to response, render, or params directly from there.

The approach I use is to inject generic methods via Mixins.

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

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

The first action show uses a generic method in ControllerGenericActions.groovy, with an argument binded to it. The second use of a mixin idExists method is inside a controller action.

Here is an example code for src/groovy/ControllerGenericActions.groovy

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

and in src/groovy/ControllerUtil.groovy

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

Not very useful in this case, but you get the idea.

千纸鹤 2024-12-24 17:37:58

使用通用方法实现抽象控制器(使用“受保护”指令)并从中扩展您的真实控制器。不要在此方法名称的开头使用“get”和“set”单词。不好,但它有效。

Implement abstract controller with common methods (use 'protected' directive) and extend from it your real controllers. Do not use 'get' and 'set' words at the beginning of this method's names. Not good, but it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文