YAT
YAT

Reputation: 466

Dynamic abstract Grails Controller

I'm using Grails 2.4.3 and I'm trying to create an dynamic Abstract Domain Class Controller that contains a few standard Methods, that can be used by every Domain Class.

So I created the DomainClassController

abstract class DomainClassController {
    def domainClassSearchService

 def domainClass = Foo
    ApplicationContext context = ServletContextHolder.servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT) as ApplicationContext
    ConfigObject config = context.getBean(GrailsApplication).config

    def index() { 

        if (!domainClass)
            return render(text: 'fehler', status: INTERNAL_SERVER_ERROR)
        def list = domainClassSearchService.list(params, domainClass, params.max, params.offset, params.sort, params.order)
        Integer count = domainClassSearchService.count(params, domainClass)
        render view: 'index', model: [list: list, count: count]
    }

    def search() {

        if (!domainClass)
            return render(text: 'fehler', status: INTERNAL_SERVER_ERROR)

        def list = domainClassSearchService.list(params, domainClass, params.max, params.offset, params.sort, params.order)
        Integer count = domainClassSearchService.count(params, domainClass)

        render template: 'list', model: [list: list, count: count, params: params]
    }

}

Now I want an BarController that extends the DomainClasscontroller:

class BarController extends DomainClassController {
    def domainClass = Bar
}

How can I set the domainClass in each Controller that the abstract controller can use it for the index and search method?

EDIT

I did it like it's described in the answer to get it work. But now I want to make the create Method dynamic so I added this:

def create(){
    def domainClassObject = getDomainClass()?.newInstance()
    domainClassObject.properties = params

    return render(view: getViewPath() + 'create', model: [domainClass: domainClassObject])
}

This work also at itself but I don't want to use in the GSP the property domainClass. I want to use the Class name in Lower Cas so f.e. foo for class Foo and bar for the Class Bar in the view.

How can i set the model name to the ClassName in lowerCase?

Upvotes: 0

Views: 401

Answers (2)

Jeff Scott Brown
Jeff Scott Brown

Reputation: 27255

You could do this the same way that RestfulController does. See https://github.com/grails/grails-core/blob/v2.5.4/grails-plugin-rest/src/main/groovy/grails/rest/RestfulController.groovy.

A Class property named resource is defined at https://github.com/grails/grails-core/blob/d45c00be6d8fdcce3edd21e16b50e30df9151b58/grails-plugin-rest/src/main/groovy/grails/rest/RestfulController.groovy#L37. The newInstance() method is invoked on that Class to create a new instance. See https://github.com/grails/grails-core/blob/d45c00be6d8fdcce3edd21e16b50e30df9151b58/grails-plugin-rest/src/main/groovy/grails/rest/RestfulController.groovy#L267.

class RestfulController<T> {

    Class<T> resource
    String resourceName
    String resourceClassName
    boolean readOnly

    // ...

    RestfulController(Class<T> resource) {
        this(resource, false)
    }

    RestfulController(Class<T> resource, boolean readOnly) {
        this.resource = resource
        this.readOnly = readOnly
        resourceClassName = resource.simpleName
        resourceName = GrailsNameUtils.getPropertyName(resource)
    }

    // ...

    /**
     * Lists all resources up to the given maximum
     *
     * @param max The maximum
     * @return A list of resources
     */
    def index(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        respond listAllResources(params), model: [("${resourceName}Count".toString()): countResources()]
    }

    /**
     * Creates a new instance of the resource.  If the request
     * contains a body the body will be parsed and used to
     * initialize the new instance, otherwise request parameters
     * will be used to initialized the new instance.
     *
     * @return The resource instance
     */
    protected T createResource() {
        T instance = resource.newInstance()
        bindData instance, getObjectToBind()
        instance
    }

    /**
     * List all of resource based on parameters
     *
     * @return List of resources or empty if it doesn't exist
     */
    protected List<T> listAllResources(Map params) {
        resource.list(params)
    }

    /**
     * Counts all of resources
     *
     * @return List of resources or empty if it doesn't exist
     */
    protected Integer countResources() {
        resource.count()
    }
}

Upvotes: 1

Emmanuel Rosa
Emmanuel Rosa

Reputation: 9885

You can set the domain class in each controller (subclass), and make it accessible to the abstract class, by implementing it as an abstract method:

DomainClassController.groovy

abstract class DomainClassController {
    def domainClassSearchService

    ApplicationContext context = ServletContextHolder.servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT) as ApplicationContext
    ConfigObject config = context.getBean(GrailsApplication).config

    abstract Class getDomainClass()

    def index() { 

        if (!domainClass)
            return render(text: 'fehler', status: INTERNAL_SERVER_ERROR)
        def list = domainClassSearchService.list(params, domainClass, params.max, params.offset, params.sort, params.order)
        Integer count = domainClassSearchService.count(params, domainClass)
        render view: 'index', model: [list: list, count: count]
    }

    def search() {

        if (!domainClass)
            return render(text: 'fehler', status: INTERNAL_SERVER_ERROR)

        def list = domainClassSearchService.list(params, domainClass, params.max, params.offset, params.sort, params.order)
        Integer count = domainClassSearchService.count(params, domainClass)

        render template: 'list', model: [list: list, count: count, params: params]
    }

}

BarController.groovy

class BarController extends DomainClassController {
    Class getDomainClass() {
        Bar    
    }
}

Upvotes: 1

Related Questions