////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
////

Scaffolding lets you generate some basic CRUD interfaces for a domain class, including:

* The necessary link:theWebLayer.html#gsp[views]
* Controller actions for create/read/update/delete (CRUD) operations

The way for an application to express a dependency on the scaffolding plugin is by including the following in `build.gradle`.

[source,groovy]
----
dependencies {
        // ...
        implementation "org.apache.grails:grails-scaffolding"
        // ...
    }
----


==== Dynamic Scaffolding

The simplest way to get started with scaffolding is to use the `@Scaffold` annotation on a controller:

[source,groovy]
----
import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Book)
class BookController {
}
----

With this configured, when you start your application the actions and views will be autogenerated at runtime. The following actions are dynamically implemented by default by the runtime scaffolding mechanism:

* index
* show
* edit
* delete
* create
* save
* update

A CRUD interface will also be generated. To access this open `http://localhost:8080/book` in a browser.

If you prefer to keep your domain model in Java and https://hibernate.org/[mapped with Hibernate] you can still use scaffolding, simply import the domain class and use it in the `@Scaffold` annotation.

===== Legacy Static Scaffold Property

The older `static scaffold = Book` syntax is still supported but the `@Scaffold` annotation is preferred:

[source,groovy]
----
class BookController {
    static scaffold = Book  // Legacy syntax - @Scaffold annotation is preferred
}
----

NOTE: The `static scaffold = true` form is not supported in Grails 3.0 and above.

===== Scaffolded Services

In addition to controllers, you can also scaffold services using the `@Scaffold` annotation:

[source,groovy]
----
import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Book)
class BookService {
}
----

This generates a service that extends `grails.plugin.scaffolding.GormService` and provides standard CRUD operations:

* `get(id)` - Retrieve by ID
* `list(Map args)` - List with pagination/sorting
* `count(Map args)` - Count entities
* `save(instance)` - Create or update
* `delete(id)` - Delete by ID

===== Service-Backed Controllers

For better separation of concerns, you can create controllers that delegate to a scaffolded service using `RestfulServiceController`:

[source,groovy]
----
import grails.plugin.scaffolding.annotation.Scaffold
import grails.plugin.scaffolding.RestfulServiceController

@Scaffold(RestfulServiceController<Book>)
class BookController {
}
----

This controller will automatically locate and use the corresponding `BookService` for all data operations.

===== Extending Custom Classes

You can specify a custom class to extend for your scaffolded controllers or services:

[source,groovy]
----
import grails.plugin.scaffolding.annotation.Scaffold
import com.example.MyCustomService

@Scaffold(MyCustomService<Book>)
class BookService {
}
----

===== Read-Only Mode

You can create read-only scaffolded controllers or services:

[source,groovy]
----
import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(domain = Book, readOnly = true)
class BookController {
}
----

===== Adding Custom Actions

You can add new actions to a scaffolded controller:

[source,groovy]
----
import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Book)
class BookController {

    def changeAuthor() {
        def b = Book.get(params.id)
        b.author = Author.get(params["author.id"])
        b.save()

        // redirect to a scaffolded action
        redirect(action: 'show')
    }
}
----

You can also override the scaffolded actions:

[source,groovy]
----
import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Book)
class BookController {

    // overrides scaffolded action to return both authors and books
    def index() {
        [bookInstanceList: Book.list(),
         bookInstanceTotal: Book.count(),
         authorInstanceList: Author.list()]
    }

    def show() {
        def book = Book.get(params.id)
        log.error("{}", book)
        [bookInstance: book]
    }
}
----

All of this is what is known as "dynamic scaffolding" where the CRUD interface is generated dynamically at runtime.

NOTE: By default, the size of text areas in scaffolded views is defined in the CSS, so adding 'rows' and 'cols' attributes will have no effect.

Also, the standard scaffold views expect model variables of the form `<propertyName>InstanceList` for collections and `<propertyName>Instance` for single instances. It's tempting to use properties like 'books' and 'book', but those won't work.


==== Static Scaffolding

Grails lets you generate a controller and the views used to create the above interface from the command line.

===== Generating Controllers and Views

To generate a controller type:

[source,bash]
----
grails generate-controller Book
----

or to generate the views:

[source,bash]
----
grails generate-views Book
----

or to generate everything:

[source,bash]
----
grails generate-all Book
----

If you have a domain class in a package or are generating from a https://hibernate.org[Hibernate mapped class] remember to include the fully qualified package name:

[source,bash]
----
grails generate-all com.bookstore.Book
----

===== Generating Scaffolded Controllers and Services

To generate a scaffolded controller (using `@Scaffold` annotation):

[source,bash]
----
grails create-scaffold-controller Book
----

To generate a scaffolded service:

[source,bash]
----
grails create-scaffold-service Book
----

To generate both a scaffolded service and controller together:

[source,bash]
----
grails generate-scaffold-all Book
----

===== Command Options

The `create-scaffold-controller` command supports the following options:

* `--force` - Overwrite existing files
* `--namespace` - Set the controller namespace
* `--service` - Use `grails.plugin.scaffolding.RestfulServiceController` instead of `grails.rest.RestfulController`
* `--extends` - Specify a custom class to extend (default: `grails.rest.RestfulController`)

The `create-scaffold-service` command supports:

* `--force` - Overwrite existing files
* `--extends` - Specify a custom class to extend (default: `grails.plugin.scaffolding.GormService`)

The `generate-scaffold-all` command supports:

* `--force` - Overwrite existing files
* `--namespace` - Set the controller namespace
* `--serviceExtends` - Specify a custom class to extend for the service (default: `grails.plugin.scaffolding.GormService`)
* `--controllerExtends` - Specify a custom class to extend for the controller (default: `grails.plugin.scaffolding.RestfulServiceController`)

Examples:

[source,bash]
----
# Generate a scaffolded controller that uses a service
grails create-scaffold-controller Book --service

# Generate with a custom class to extend
grails create-scaffold-service Book --extends=com.example.MyCustomService

# Generate both with namespace
grails generate-scaffold-all Book --namespace=api
----


==== Customizing the Generated Views


The views adapt to link:validation.html[Validation constraints]. For example you can change the order that fields appear in the views simply by re-ordering the constraints in the builder:

[source,groovy]
----
def constraints = {
    title()
    releaseDate()
}
----

You can also get the generator to generate lists instead of text inputs if you use the `inList` constraint:

[source,groovy]
----
def constraints = {
    title()
    category(inList: ["Fiction", "Non-fiction", "Biography"])
    releaseDate()
}
----

Or if you use the `range` constraint on a number:

[source,groovy]
----
def constraints = {
    age(range:18..65)
}
----

Restricting the size with a constraint also effects how many characters can be entered in the d view:

[source,groovy]
----
def constraints = {
    name(size:0..30)
}
----


==== The Fields Plugin


The Grails scaffolding templates make use of the link:theWebLayer.html#fields[Fields Plugin]. Once you've generated the scaffold views, you can customize the forms and tables using the `Taglib` provided by the plugin (see the link:theWebLayer.html#fields[Fields plugin docs] for details).

[source,xml]
----
<%-- Generate an HTML table from bookInstanceList, showing only 'title' and 'category' columns --%>
<f:table collection="bookInstanceList" properties="['title', 'category']"/>
----


==== Customizing the Scaffolding templates


The templates used by Grails to generate the controller and views can be customized by installing the templates with the link:{commandLineRef}install-templates.html[install-templates] command.

