Sep 8 2006

Dynamic sorting with modelglue.GenericList

Posted by Joe Rinehart at 8:07 AM
6 comments
- Categories: Model-Glue

Yesterday on the Model-Glue list, someone asked if it's possible to dynamically sort the resultset returned by the GenericList functionality in the Model-Glue:Unity beta. Yes, it is, but you have to write a wee bit of code to do it.

A bit of background:

The "GenericList" is what's known as a Generic Database Message. These are pre-defined messages within Unity that allow you to List, Edit, Commit, and Delete instances without writing a dedicated controller or listener function. It's possible to "configure" the behavior of each using message arguments.

For example, the default use of GenericList to list records from the WidgetType table looks like this:

<message name="modelglue.GenericList">
<argument name="object" value="WidgetType" />
</message>

That'll create a query named "WidgetTypeQuery" in the viewstate listing all records from the WidgetType table.

If you wanted to only list records where WidgetType.UserID was equal to the UserID in the viewstate (say, URL.UserID) and you wanted to list them in order of WidgetType.Name, and you wanted the resulting query to be named "UserWidgetTypeQuery," you'd use the following:

<message name="modelglue.GenericList">
<argument name="object" value="WidgetType" />
<argument name="criteria" value="UserID" />
<argument name="orderColumn" value="Name" />
<argument name="queryName" value="UserWidgetTypeQuery" />
</message>

Ok, that's fine, but it's still pretty static. What if the query needed to do two subqueries, a join, and some aggregate functions?

Well, at that point, you're beyond the scope of what Reactor will automatically perform. You'd open up your WidgetTypeGateway.cfc, write some custom SQL in a method named something like "listWithComplexStuff", and use the "GatewayMethod" argument of GenericList to instruct it to use that method instead of the generic getByQuery() functionality that Reactor provides. It'd look like this:

<message name="modelglue.GenericList">
<argument name="object" value="WidgetType" />
<argument name="criteria" value="UserID" />
<argument name="orderColumn" value="Name" />
<argument name="queryName" value="UserWidgetTypeQuery" />
<argument name="gatewayMethod" value="listWithComplexStuff" />
</message>

That's OK, but it's now going to ignore the "orderColumn" argument because you're using your own custom SQL.

What's worth noting, though, is that any value listed in the criteria argument is passed to your custom method as a named argument. In other words, you'll have an argument named "arguments.UserID" available withing listWithComplexStuff.

Knowing this, we can revisit our original problem: how do we sort a GenericList based on something like a URL variable?

Now that we know about how criteria work with custom gateway methods, the answer's pretty simple: write a custom gateway method that has arguments like "orderBy" and "ascending" and uses Reactor's sort mechanism to perform the sorting.

If you want to cheat, here's the code (it should work in any Gateway, like widgetTypeGateway.cfc):

<cffunction name="listSorted" returntype="query" output="false">
   <cfargument name="orderBy" type="string" required="true" />
   <cfargument name="ascending" type="boolean" required="false" default="true" />

   <cfset var query = createQuery() />
   <cfset var order = query.getOrder() />
   <cfset var where = query.getWhere() />
   <cfset var field = "" />

   <cftry>   
      <cfloop collection="#arguments#" item="field">
            <cfset where.isEqual(_getAlias(), field, arguments[field]) />
      </cfloop>
      <cfcatch type="reactor.getField.FieldDoesNotExist"></cfcatch>
   </cftry>

   <cfif arguments.ascending>
      <cfset order.setAsc(_getAlias(), arguments.orderBy) />
   <cfelse>
      <cfset order.setDesc(_getAlias(), arguments.orderBy) />
   </cfif>
   
   <cfreturn getByQuery(query) />
</cffunction>

Note that this'll not just allow you to enter url variables like "orderBy" and "ascending," but also pass along additional criteria. With it, we could use the following message broadcast XML:

<message name="ModelGlue.genericList">
   <argument name="criteria" value="orderBy,ascending,userId" />
   <argument name="object" value="widgetType" />
   <argument name="queryName" value="widgetTypeQuery" />
   <argument name="gatewayMethod" value="listSorted" />
</message>

Now, hitting a URL like http://localhost/widget/?event=widgetType.list&orderBy=name&ascending=false&userId=1 will give us a list of WidgetType records where WidgetType.UserID = 1 sorted by WidgetType.Name in a descending manner.

Comments

byron

byron wrote on 09/08/06 12:12 PM

Thanks, Joe. This is way cool. Exactly what I was looking for. Couple of questions:

What is &quot;_getAlias()&quot;. Is that the queryname?

If I want to do a join, do I put that before the &lt;cfset var order = query.getOrder() /&gt;? Do I need to create a gateway to the other table(s). The table I'm trying to join has a &lt;hasOne&gt; relationship.

Thanks in advance
byron

byron wrote on 09/08/06 4:10 PM

Joe,

Figured out the joins. Really starting to like all this.

Thanks.
Tim

Tim wrote on 09/22/06 11:40 PM

When I try to replicate this, the event-handler just passes the object data to the viewstate and never processes my joins, returnObjectFields from the function. It's like &quot;gatewayMethod&quot; didn't even exist.

I copied the example pretty much verbatim and just changed a couple of field names. My guess is there's an obvious answer.
Tim Archambault

Tim Archambault wrote on 10/02/06 4:02 PM

What do you know. I set MG:U down for a week, come back to it and it works! I almost gave up.
Richard Davies

Richard Davies wrote on 11/03/06 4:46 PM

Joe,

I don't see the gatewayMethod argument documented anywhere in the documentation. You might want to add it to the 'Using ModelGlue.GenericList' page so that people at least know it's available.
RHONDA

RHONDA wrote on 09/29/08 3:48 AM

Now that we know about how criteria work with custom gateway methods, the answer's pretty simple: write a custom gateway method that has arguments like "orderBy" and "ascending" and uses Reactor's sort mechanism to perform the sorting.
http://www.batteryfast.co.uk/acer/btp-550.htm acer btp-550 battery,
http://www.batteryfast.co.uk/acer/batby27l.htm acer batby27l battery,
http://www.batteryfast.co.uk/acer/bat30n3l.htm acer bat30n3l battery,
http://www.batteryfast.co.uk/acer/btp-550p.htm acer btp-550p battery,
http://www.batteryfast.co.uk/acer/batbl50l4.htm acer batbl50l4 battery,
http://www.batteryfast.co.uk/acer/batbl50l6.htm acer batbl50l6 battery,
http://www.batteryfast.co.uk/acer/batbl50l8h.htm acer batbl50l8h battery,
http://www.batteryfast.co.uk/acer/travelmate-4200.htm acer travelmate 4200 battery,
http://www.batteryfast.co.uk/acer/batbcl11.htm acer batbcl11 battery,

http://www.batteryfast.co.uk/acer/travelmate-420.htm acer travelmate 420 battery,
http://www.batteryfast.co.uk/acer/btp-58a1.htm acer btp-58a1 battery,
http://www.batteryfast.co.uk/acer/btp-60a1.htm acer btp-60a1 battery,
http://www.batteryfast.co.uk/acer/travelmate-2000.htm acer travelmate 2000 battery,
http://www.batteryfast.co.uk/acer/travelmate-2500.htm acer travelmate 2500 battery,
http://www.batteryfast.co.uk/acer/btp-42c1.htm acer btp-42c1 battery,
http://www.batteryfast.co.uk/acer/c100.htm acer c100 battery,
http://www.batteryfast.co.uk/acer/c102.htm acer c102 battery,
http://www.batteryfast.co.uk/acer/c104.htm acer c104 battery,

http://www.batteryfast.co.uk/acer/btp-37d1.htm acer btp-37d1 battery,
http://www.batteryfast.co.uk/acer/travelmate-610.htm acer travelmate 610 battery,
http://www.batteryfast.co.uk/acer/btp-63d1.htm acer btp-63d1 battery,
http://www.batteryfast.co.uk/acer/aspire-3020.htm acer aspire 3020 battery,
http://www.batteryfast.co.uk/acer/aspire-3610.htm acer aspire 3610 battery,
http://www.batteryfast.co.uk/acer/aspire-1300.htm acer aspire 1300 battery,
http://www.batteryfast.co.uk/acer/aspire-1310.htm acer aspire 1310 battery,
http://www.batteryfast.co.uk/acer/squ-401.htm acer squ-401 battery,
http://www.batteryfast.co.uk/acer/lcbtp03003.htm acer lcbtp03003 battery,

Write your comment



(it will not be displayed)