Создание контроллеров Grails более DRY?
Я ищу способы очистки моего кода контроллера 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"
}
}
Таким образом, все ответы способствовали решению! Большое спасибо!
Ответы
Ответ 1
Я всегда вытаскиваю этот пост в блоге, когда возникает этот вопрос:
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"
}
}
То, как вы кодируете получателя, очень гибко и типичное использование для меня (это не описано в блоге) предназначено для редактирования и т.д.
Я обычно кодирую этот путь (мне нравится шаблон для его четкого разделения и читаемости):
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"])
}
}
С моим помощником-получателем:
private def withIssue( Closure c) {
def issue = Issue.get(params.id)
if(issue) {
c.call issue
}
else {
response.sendError(404)
}
}
Я действительно думаю, что метод mixin (очень похожий на метод "продлить общий абстрактный контроллер" ) тоже хорош, но этот способ дает два преимущества:
- Вы можете ввести помощника, как видите, я делаю в закрытии, предоставляя вам доступ к методам и т.д. в STS/IDEA (не протестированные Netbeans).
- Повторение не очень велико и возможность изменять getter (использовать, например, BarDomain.findByFoo(params.id) и т.д.)
В представлении, которое я связываю для редактирования(), я просто помещаю id="${issue.id}"
в <g:form>
, и он работает без проблем.
Ответ 2
Я бы не рекомендовал наследование для этого, так как вы не можете распространять общие методы в нескольких суперклассах. Ваш абстрактный класс быстро станет беспорядочным, если у вас много контроллеров. Вы не можете использовать композицию (например, используя Сервис), потому что у вас нет доступа к response
, render
или params
прямо оттуда.
Подход, который я использую, заключается в том, чтобы вводить общие методы через 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
}
Не очень полезно в этом случае, но вы получаете идею.
Ответ 3
Внедрить абстрактный контроллер с общими методами (используйте "защищенную" директиву) и расширить от него свои настоящие контроллеры. Не используйте слова "get" и "set" в начале этих имен методов. Нехорошо, но это работает.