logo sudovi.com
- X /home/ryan/Grails UrlMappings -- Jun 23, 2008 4:30pm

I am using Grails on one of my current projects in which end-users can enroll in a couple of "different" programs offered by my client.  Behind the scenes, these programs are identical except for the name.  However, the look and feel of the enrollment process will have subtle difference based upon the program that the end-user is enrolling in.

Since the code and the majority of the views are exaclty the same, I want to use one controller and just hold the program name in session and refer to it when I need to specify something about that particular program.  Sounds easy enough right?  Well, it is easy UNLESS you want to maintain completely separate URLs for the process.  My client is very interested in tracking this sort of information in Google Analytics and for some reason the urchin tracker isn't producing the desired results.  

Originally, I was giving two possible entry points into this enrollment process, one for each program, but once inside, everything was referring to the same controller.  So how then do I maintain two separate url schemas and still use the same backend controller?  The answer:  UrlMappings.groovy

I did some sniffing around and though the documentation wasn't entirely clear to me at first, after some playing I was able to produce the desired result.  Here's how:

[b]Background Info[/b] -- Consider we have two products (product1 and product2).  The products are identical except for the name.  You fill out an online application to become a consumer of one of these products depending on your unique situation.  The application process uses product1 as the default controller for the enrollment process.  We don't want to have an identical controller for product2 but we want product2 to have its own unique URLs.  With me so far?  (I hope so...)

[b]Desired Effect[/b] -- UserA wants to enroll in product1 and UserB wants to enroll in product2.  Google Analytics should reflect the two independant URLs even though they are using the same controller.

[b]Do It[/b] -- The secret here is a file called UrlMappings.groovy and its located in /grails-app/conf/UrlMappings.groovy.  This file will take the incoming URLs and map them to a controller and action of your choosing.  So say we have /product1/qualify and we also need product2/qualify.  We simply map /product2/qualify to the product1 controller and that controller's qualify action.  Example:


...grails stuff here...
"/product2/$action/$id?" {
controller = "product1"
constraints {
action(matches:"apply|qualify|thankyou")
}
}
...grails stuff here...



This mapping takes all requests for product2 controller and refers it behind the scenes to the product1 controller/action.  The kicker here is the "action(matches:...)" line.  If we request /product1/somethingelse, this mapping won't match.  (There are certain situations in my project that we had to have some other actions on product2's controller that product1 didn't have.  In this case, I created the product2 controller and just added that action.)  If the action "matches" the mapping takes over and uses product1/action behind the scenes while the URL still reads /product2.  Nifty!!  (NOTE:  Take note of the trailing "?" in the mapping url.  It signifies it as an optional parameter.  If I were to put a question mark after the action, everything under product2 would map to product1 which may not be desireable, and for me, it wasn't).

[b]A Gotcha[/b] -- One thing I ran into however, was that once I added this mapping, I noticed that the links in my product1 views started pointing to product2's controller which wasn't making sense.  So I added another mapping pointing /product1 to its own controller which remedied the problem.  I'm sure if I think about it long and hard enough, I'll figure out why that was needed, but perhaps thats for another entry.  

In closing, I'm finding that the more I ask of Grails, the more pleased I am with the answers it provides.  It took me a while to get the mappings to work quite right, but once I did it worked like a charm.  Perhaps there are better ways to skin this cat, but this solution worked for me and took very little "coding/configuration" to get the desired result.

Please if you have suggestions, or see anything that I should/could have done differenlty, don't hesitate to comment.  I hope this helps anyone else who may be wrestling with a similar siutation and finding the documentation a little lacking.


~
:wq!


- X /home/Christopher M, Judd -- 2008-06-24 14:43:20
Here is a mapping I have tried in the past which might work for you. I think it might solve your product1 mapping issue too.

"/$product/$controller/$id?" {
constraints {
product(inList:["product1","product2"])
}
}
~
:wq!


- X /home/Ryan -- 2008-06-27 10:02:00
@Chris -- Oooo, very nice. I like that solution. Going to have to give it try. Thanks for the info!
~
:wq!


- X Add Comments
All Fields Req'd (I won't display your email)

Bold: [b]your bold text[/b]
Italics: [i]your italic text[/i]
Underline: [u]your underlined text[/u]
Link: [mylink]http://wherever[/mylink]

Copyright 2008 -- all rights reserved