Virtual controller?

82 views
Skip to first unread message

Servo

unread,
Oct 22, 2008, 12:00:00 AM10/22/08
to ColdFusion on Wheels
I have a need to create what I can only describe as a "dynamic" or
"virtual" controller. A "catch-all" controller, if you will, that can
act on paths that don't really exist.

My site has URL rewriting enabled. And say I have all my products in a
database, maintained by a content maintainer. But I want to build my
website to be dynamically aware of those products, so that an end user
could enter URLs like:

http://www.mysite.com/product1/
and
http://www.mysite.com/product2/

and they would get wheels views (without me having to create a
controller for "product1" and "product2"), most likely living at
something like:
www.mysite.com/products/view?id=11111
and
www.mysite.com/products/view?id=22222

respectively.

My design is such that this "catch-all" controller would see that a
controller for "product1" does not exist, then would check the
database for matches of productname='product1'. If there's a match, it
would hand off to the view action in the products controller, passing
the proper id. If there's not a match, it would pass-through to the
standard wheels error page(s).

I can certainly accomplish this with a lot of tweaking in the wheels
\dispatch\request.cfm, but I'm hoping someone might be able to think
of a way to do it without editing core code.

Anyone have any ideas?

Thanks in advance!
-Carl

Per Djurner

unread,
Oct 22, 2008, 4:38:07 AM10/22/08
to cfwh...@googlegroups.com
Here's what you can do:

First, add a new route to config/routes.cfm that catches your products
like this:
<cfset addRoute(name="product", pattern="[title]",
controller="product", action="index")>
Now when you type in "http://localhost/keyboard" this route will be
triggered and the Index action will be called on the Product
controller with params.title set to "keyboard".

The problem with that is that this will break any of your normal
controllers though so you'll have to add them specifically before this
route.
You'll end up with a config/routes.cfm file looking something like this:
<cfset addRoute(name="main", pattern="main/[action]",
controller="main", action="index")>
<cfset addRoute(name="cart", pattern="cart/[action]",
controller="cart", action="index")>
<cfset addRoute(name="product", pattern="[title]",
controller="product", action="index")>
<cfset addRoute(name="home", pattern="", controller="main", action="index")>

"main" and "cart" are your normal controllers and by adding them to
the top of the routes file Wheels looks for them first.

As for rendering a different view depending on the product the user
wants to see I suggest calling renderPage in the Index action of the
Product controller like this:
<cfset renderPage(action=params.title)>

Good luck :)

/ Per

Servo

unread,
Oct 22, 2008, 11:27:46 PM10/22/08
to ColdFusion on Wheels
Beautiful, Per, thank you much!

I have edited the request dispatch in the interim, adding the
following after the <!--- Create request controller --->... but I know
it's not a sound solution. I offer it here just for discussion:


<!--- Create requested controller --->
<cftry>
<cfset loc.controller = $controller(loc.params.controller).
$createControllerObject(loc.params)>
<cfcatch>
<cfif fileExists(expandPath("#application.wheels.controllerPath#/
#loc.params.controller#.cfc"))>
<cfrethrow>
<cfelse>
<!--- CAS EDIT - added check for product urls --->
<cfquery name="qryProductIntercept"
datasource="#application.settings.database.datasource#">
SELECT id FROM products WHERE
LOWER(productname)=<cfqueryparam cfsqltype="cf_sql_varchar"
value="#LCase(loc.params.controller)#">
</cfquery>
<cfif qryProductIntercept.RecordCount EQ 1>
<cfset loc.params["controller"]="products">
<cfset loc.params["action"]="view">
<cfset loc.params["id"]=qryProductIntercept.id>
<cfset loc.controller =
$controller(loc.params.controller).
$createControllerObject(loc.params)>
<cfelse>
<!--- / CAS EDIT - added check for product urls --->
<cfif application.settings.showErrorInformation>
<cfthrow type="Wheels.ControllerNotFound" message="Could not find
the <tt>#loc.params.controller#</tt> controller" extendedInfo="Create
a file named <tt>#loc.params.controller#.cfc</tt> in the
<tt>controllers</tt> directory containing this code:
<pre><code>#htmlEditFormat('<cfcomponent extends="wheels.controller"></
cfcomponent>')#</code></pre>">
<cfelse>
<cfinclude template="../../#application.wheels.eventPath#/
onmissingtemplate.cfm">
<cfabort>
</cfif>
<!--- CAS EDIT - added check for product urls --->
</cfif>
<!--- / CAS EDIT - added check for product urls --->
</cfif>
</cfcatch>
</cftry>

I'll undoubtedly go with the route solution so I can restore the core,
but for now it works quite well

Derek Lin

unread,
Feb 9, 2015, 12:44:34 AM2/9/15
to cfwh...@googlegroups.com
Hi,

I am thinking of this dynamic/virtual controller idea and see if it has been documented anywhere.

On my site, I want people to sign up with usernames like john or mary.  And when people browse to http://[mysite.com]/john, it will be serving content from http://[mysite.com]/home/welcome/user/john.

And I will have other URL's within http://[mysite.com]/john, like http://[mysite.com]/john/contact, http://[mysite.com]/john/search, etc.  And they will serve content from http://[mysite.com]/contact/user/john, http://[mysite.com]/search/user/john, etc (I am still not 100% about all the actual URL's).

But the key thing is the john is dynamic and it doesn't really exist.

Is the approach mentioned this this discussion thread the right approach?  Or should I do something else?

Thanks,

Derek

Tom King

unread,
Feb 9, 2015, 4:00:31 AM2/9/15
to cfwh...@googlegroups.com
Personally, I'd do this via routes:

// Example Static Route - it's above the user bit so will get precedence
addRoute(name="about",  pattern="/about", controller="default", action="about");

// Here's our user routes: from this point on, anything like "/foo" will get interpreted as user = foo etc.
addRoute(name="user", pattern="/[user]/[controller]/[action]/[key]");
addRoute(name="user",  pattern="/[user]/[controller]/[action]/");
addRoute(name="user",  pattern="/[user]/[controller]/", action="index");
addRoute(name="user",  pattern="/[user]/", controller="default", action="index");

etc.

Now when you do a link, you just need to remember to always pass in the user variable:

#linkTo(route="user", user="Joe", controller="contact", action="form", text="Contact Joe");

etc.

Derek Lin

unread,
Feb 9, 2015, 10:56:02 AM2/9/15
to cfwh...@googlegroups.com
Hi Tom,

With this approach, when a new user signs up, do I have to manually add another route?  Or is everything automated?

Thanks,

Derek

Tom King

unread,
Feb 9, 2015, 11:02:07 AM2/9/15
to cfwh...@googlegroups.com
[user] is just a placeholder for params.user essentially.
So no, it’s a variable!
T
--
You received this message because you are subscribed to the Google Groups "ColdFusion on Wheels" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cfwheels+u...@googlegroups.com.
To post to this group, send email to cfwh...@googlegroups.com.
Visit this group at http://groups.google.com/group/cfwheels.
For more options, visit https://groups.google.com/d/optout.

Derek Lin

unread,
Feb 9, 2015, 8:51:29 PM2/9/15
to cfwh...@googlegroups.com
Tom,

Thanks for the quick reply.  I'll give it a try.

Derek

Derek Lin

unread,
Feb 10, 2015, 1:36:10 AM2/10/15
to cfwh...@googlegroups.com
Hi Tom,

I tried it.  I think it works.  

But I have to use this to get the user in the controller:

variables.params.user

Is that right?

Thanks,

Derek

Per Djurner

unread,
Feb 10, 2015, 3:56:00 AM2/10/15
to cfwh...@googlegroups.com
params.user is enough, no need for variables.

Tom King

unread,
Feb 10, 2015, 5:00:47 AM2/10/15
to cfwh...@googlegroups.com
I've thrown together a very quick example which should get you started.

Derek Lin

unread,
Feb 10, 2015, 7:38:06 PM2/10/15
to cfwh...@googlegroups.com
Hi Tom,

You are super awesome.  This will help me as well as people with similar questions in the future!

Thanks,

Derek
Reply all
Reply to author
Forward
0 new messages