Grails UrlMappings for unknown number of variables - grails

I'm using url mappings to translate the URL directory structure into categories within a site, currently using:
class UrlMappings {
static excludes = ['/css/*','/images/*', '/js/*', '/favicon.ico']
static mappings = {
"/$category1?/$category2?/$category3?/"(controller: 'category')
"500"(view:'/error')
"404"(view:'/notFound')
}
}
At present this supports categories three levels deep. I'd like to be able to support categories N levels deep where N >= 1.
How can this be achieved?

The asterisk, either single or double, is used for wilcard url mapping.
A single asterisk will match anything at the given level:
static mappings = {
"/images/*.jpg"(controller:"image")
}
// Matches /images/logo.jpg, images/header.jpg and so on
A double asterisk will match anything on more than one level:
static mappings = {
"/images/**.jpg"(controller:"image")
}
// Matches /images/logo.jpg, /images/other/item.jpg and so on
Combined with the ? for optional mapping matches, the following will work in the context of the question:
class UrlMappings {
static excludes = ['/css/*','/images/*', '/js/*', '/favicon.ico', '/WEB-INF/*']
static mappings = {
"/**?"(controller: 'category')
"500"(view:'/error')
"404"(view:'/notFound')
}
}

This question is ancient but in the url mappings you can also do this
"/categories/$categories**?"(controller:categories)
this will load the rest of the uri into param variable
/categories/animals/dogs/retrievers
///goes to categories controller and has...
params.categories //= "animals/dogs/retrievers"
You could then use that to do various dynamic things

Related

put namespace in url

grails 2.3.8 here. When i g:link to certain controller actions, i want to put a namespace in the url in front of the controller name.
For example:
call "app/apple/eat" -> "app/admin/apple/eat"
Since there are many dynamic and static scaffolded controllers involved, I thought i can do this with some UrlMappings expression but i dont know how to do this.
I tried out something like this without no success:
static mappings = {
"/apple/$action?/$id?" (redirect:"/admin/apple/$action?/$id?")
}
using namespace = "admin" in the AppleController doesnt work aswell
thanks for advices
For the example
call "app/apple/eat" -> "app/admin/apple/eat"
you could do it via the grails-app/conf/UrlMappings.groovy with the following configuration (which works than for the whole application):
class UrlMappings {
static mappings = {
"/admin/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
with this configuration, your controllers are reachable via:
http://localhost:8080/apple/admin/apple/eat/1
etc.
Or!
If you would only have apple under the admin prefix, than you could do the following in grails-app/conf/UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/admin/apple/$action?/$id?"(controller : "apple")
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Or!
If you have more redirects, you could group them together:
static mappings = {
group("/admin") {
"/apple/$action?/$id?(.${format})?"(controller: 'apple')
"/peach/$action?/$id?(.${format})?"(controller: 'peach')
}
...

Parameterize UrlMappings in Grails 3

I had this idea that I wanted to assign prefixes to my two categories of URLs. One category would be
/baseurl/api/$controller
for secured REST URLs and then:
/baseurl/register/$action
for the current one category of unsecured URLs
I then thought it would be good to store the prefixes /api and /register in variables so they could be referenced elsewhere if need be (such as my security configuration). However, I can't get this to work in my UrlMappings. None of the variables defining the URL parts seem to be populated. Consider the following UrlMappings.groovy where I've defined a SignupController.groovy to map to registration, and every other controller is API related:
class UrlMappings {
static final API_URL_ROOT = "/api"
static final REGISTER_URL_ROOT = "/register"
static mappings = {
API_URL_ROOT + "/$controller/$action?/$id?(.$format)?" {
constraints {
}
}
REGISTER_URL_ROOT (controller: "signup")
"/"(view: "/index")
"500"(view: '/error')
"404"(view: '/notFound')
}
}
When this runs I can only invoke my controllers through the root URL directly, ie, localhost:8080/login and localhost:8080/signup whereas I want it to be:
localhost:8080/api/v1/login and localhost:8080/register/signup
How do I fix this?
You could use group to separate your different api
http://mrhaki.blogspot.com/2013/11/grails-goodness-grouping-url-mappings.html?m=1
In your case it could become something like that
group("/api") {
"/$controller/$action?/$id?(.$format)?" { constraints { } }
// PUT HERE ALL OTHER API MAPPING
}
group("/register") {
"/$controller" {}
// PUT HERE ALL REGISTER MAPPINGS
}
If you need to take into consideration a version for your api just do this
group("/api") {
"/$namespace/$controller/$action/$id?" {}
// OR ANY OTHER MAPPING YOU NEED. Then in your controller define
// static namespace = 'v1'
// for example for your v1 controllers.
}
Then you can declare you groups as variables if you want.
static API="/api"
group (API) { .... }
Hope that helps if not sorry for the noise

Grails Domain With "First" and "Last" Properties

I recently inherited a Grails code base with a domain class called Name with (among others), the properties first and last to represent the first and last parts of a name, respectively. When writing a unit test which utilized this domain, I ran into some problems stemming from the names of these properties being the same as the first and last methods within Grails. Now, I can fix the problems by renaming the properties, but I was wondering if there is a way within Grails to use the property names first and last.
Namely, the error I was receiving was No signature of method: com.example.Name.first() is applicable for argument types: () values: []
Possible solutions: first(), first(java.lang.String), first(java.util.Map), list(), list(java.util.Map), print(java.lang.Object) when Grails attempts to apply a nullable: true constraint to the properties.
Here's the source of Name:
class Name {
String first
String middle
String last
static belongsTo = [person : Person]
static constraints = {
first(nullable:true)
middle(nullable:true)
last(nullable:true)
}
public static Name findOrCreate(String first, String middle, String last){
def name
name = Name.createCriteria().get{
and{
eq('first', first)
eq('middle', middle)
eq('last', last)
}
if(!name){
name = new Name()
name.first = first
name.middle = middle
name.last = last
}
return name
}
static mapping = {
cache true
}
}
You say that this error happens in the constraints block. In that case you may be able to get it working with an explicit delegate., i.e.
static constraints = {
delegate.first(nullable:true)
// and similarly for last
}
to force the first to be treated as a call into the constraints DSL rather than to the static GORM method.

How to nest Grails controller paths

I know it is possible to do something like this:
Controller foo with action bar can be accessed by (1):
/appname/foo/bar
And it can be rewritten using URL mappings - e.g like this:
"/foobar/foo/$action"(controller: "foo")
And then access it via (2):
/appname/foobar/foo/bar
But it is still possible to access it via (1). This is of course because of the default URL mapping:
"/$controller/$action?/$id?"()
But I would rather not delete this, because this basically means I have to manually write mappings to every other controller/action that follows the default pattern.
It is possible to obtain url-patterns for specific controller/actions like (2) without using URL mappings? If not, is there an easy way to "exclude" controllers from the default mapping closure?
The Solution is to change the default mapping, in a way to exclude the whished special controller URL.
class UrlMappings {
static myExcludes = ["foo"]
static mappings = {
"/foobar/foo/$action"(controller: "foo") // your special Mapping
// the rewritten default mapping rule
"/$aController/$aAction?/$id?"{
controller = { (params.aController in UrlMappings.myExcludes) ? "error" : params.aController }
action = { (params.aController in UrlMappings.myExcludes) ? "notFound" : params.aAction }
constraints {
// apply constraints here
}
}
}
}
For the rewritten default rule you have to prevent usage of default variable names $controller and $action. Instead of error/notFound you are also able to redirect to other locations.
If you can make your rule breaking scenario more specific than the Grails default $controller/$action?$id? pattern, then the default can be left as is and will apply to everything outside of your exception pattern. I made a quick Person domain and performed a generate-all. Then I made a BreakRuleController just by itself.
class UrlMappings {
static mappings = {
"/b/$action?/$someVariable?"(controller: "breakRule")
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
With this UrlMapping, if you access the URI "/b/foo/stackoverflow" it will print "Breaking Rules foo: stackoverflow". If you go to "/b", it will print "Breaking Rules index".
Then if you go to the standard Person URI's, all your default Grails scaffolding works fine also (create, edit, etc.) because that gets mapped to the typical "$controller/$action?/$id?" pattern.
class BreakRuleController {
def index() {
print "Breaking Rules index"
}
def foo(String someVariable) {
print "Breaking Rules foo: " + someVariable
}
}

Grails GORM Query with Multiple Objects?

I am trying to write a query in Grails to return a set of results from a domain class, but within those return the relevant results of a separate class whom have the parentId of the main class.
def query = Cars.where {
(colour == 'red')
}
And then within each list item include the set of parts relating to that CAR ID (as an example of what I'm trying to achieve, I know the code is incorrect though....
query.each{
this car. add(Parts.whereCarID{it.id})
}
If you define your domain model properly, you should get it without a criteria involved.
As far as I understand you need to add static hasMany = [parts: Parts] in your Cars domain class, and static belongsTo = [car:Cars] in your Parts class.
So for example, here how it might look:
class Cars {
string colour
static hasMany = [parts:Parts]
// ... rest of your properties
}
class Parts {
static belongsTo = [car:Cars]
// ... rest of your properties
}
And to get your result just do this:
def cars = Cars.findAllByColour('red')
Then you can do:
cars.each { car->
println car.parts // <-- all the parts for each car is here
}

Resources