Data object views

Overview

Preside provides a feature that allows you to autowire your data model to your views, completely bypassing hand written handlers and service layer objects. Rendering one of these views looks like this:

#renderView(
      view          = "events/preview"
    , presideObject = "event"
    , filter        = { event_category = rc.category }
)#

In the example above, the /views/events/preview.cfm view will get rendered for each event record that matches the supplied filter, { event_category = rc.category }. Each rendered view will be passed the database fields that it needs as individual arguments.

In order for the renderView() function to know what fields to select for your view, the view itself must declare what fields it requires. It does this using the <cf_presideparam> custom tag. Using our "event preview" example from above, our view file might look something like this:

<cf_presideparam name="args.label"                                  /><!-- I need the 'label' field -->
<cf_presideparam name="args.teaser"                                 /><!-- I need the 'teaser' field -->
<cf_presideparam name="args.image"                                  /><!-- I need the 'image' field -->
<cf_presideparam name="args.event_type_id" field="event_type"       /><!-- I need the 'event_type' field, but aliased to 'event_type_id' -->
<cf_presideparam name="args.event_type"    field="event_type.label" /><!-- I need the 'label' field from the relatated object, event_type, aliased to 'event_type' -->

<cfparam name="_counter" type="numeric" /><!-- current row in the recordset being rendered -->
<cfparam name="_records" type="numeric" /><!-- total records in the recordset being rendered -->

<cfoutput>
    <div class="preview-pane">
        <h3>#args.label#</h3>
        <p class="event-type">
            <a href="#event.buildLink( pageId=args.event_type_id )#">
                #args.event_type#
            </a>
        </p>

        #renderAsset( assetId=args.image, context="previewPane" )#

        <p>#args.teaser#</p>
    </div>
</cfoutput>

Info

We introduced the <cf_presideparam custom tag in Preside 10.2.4. Prior to this, we used the <cfparam tag for this feature. The <cfparam tag approach will continue to work in version 10 but we may decide to drop this support in future versions. This change is due to an unforeseen incompatibility with Adobe ColdFusion.

Given the examples above, the SQL you would expect to be automatically generated and executed for you would look something like this:

select     event.label
         , event.teaser
         , event.image
         , event.event_type as event_type_id
         , event_type.label as event_type

from       pobj_event      event
inner join pobj_event_type event_type on event_type.id = event.event_type

where      event.event_category = :event_category

Filtering the records to display

Any arguments that you pass to the renderView() method will be passed on to the Preside Object selectData() method when retrieving the records to be rendered.

This means that you can specify any number of valid selectData() arguments to filter and sort the records for display. e.g.

rendered = renderView(
      view          = "event/detail"
    , presideObject = "event"
    , id            = eventId
);

rendered = renderView(
      view          = "event/preview"
    , presideObject = "event"
    , filter        = "event_type != :event_type or comment_count < :comment_count"
    , filterParams  = { event_type=rc.type, comment_count=10 }
    , startRow      = 11
    , maxRows       = 10
    , orderBy       = "datepublished desc"
);

Declaring fields for your view

As seen in the examples above, the <cf_presideparam> tag is used by your view to specify what fields it needs to render. Any variable that is declared that starts with "args." will be considered a field on your preside object by default.

If we are rendering a view for a news object, the following param will lead to news.headline being retrieved from the database:

<cf_presideparam name="args.headline" />

Aliases

You may find that you need to have a different variable name to the field that you need to select from the data object. To achieve this, you can use the field attribute to specify the name of the field:

<cf_presideparam name="args.headline" field="news.label" />

You can use the same technique to do aggregate fields and any other SQL select goodness that you want:

<cf_presideparam name="args.headline"      field="news.label" />
<cf_presideparam name="args.comment_count" field="Count( comments.id )" />

Getting fields from other objects

For one to many style relationships, where your object is the many side, you can easily select fields from the related object using the field attribute shown above. Simply prefix the column name with the name of the foreign key field on your object. For example, if our news object has a single news_category field that is a foreign key to a category lookup, we could get the title of the category with:

<cf_presideparam name="args.headline" field="news.label" />
<cf_presideparam name="args.category" field="news_category.label" />

Front end editing

If you would like a field to be editable in the front end website, you can set the editable attribute to true:

<cf_presideparam name="args.label" editable="true" />

Accepting arguments that do not come from the database

Your view may need some variables that do not come from the database. For example, in the code below, the view is being passed the showComments argument that does not exist in the database.

#renderView( view="myview", presideObject="news", args={ showComments=false } )#

To allow this to work, you can specify field="false", so:

<cf_presideparam name="args.headline" field="news.label" />
<cf_presideparam name="args.category" field="news_category.label" />
<cfparam name="args.showComments"     field="false" type="boolean" />

This looks as though it should not be necessary because we are using the <cfparam tag to state that we expect the args.showComments variable to be available. However, the cfparam tag is still supported here for backward compatibility with versions of Preside prior to 10.2.4. As an alternative approach, one can use something like:

<cf_presideparam name="args.headline" field="news.label" />
<cf_presideparam name="args.category" field="news_category.label" />

<cfset showComments = IsTrue( args.showComments ?: "" ) />

Defining renderers

Each of the fields fetch from the database for your view will be pre-rendered using the default renderer for that field. So fields that use a richeditor will have their Widgets and embedded assets all ready rendered for you. To specify a different renderer, or to specify renderers on calculated fields, do:

<cf_presideparam name="args.comment_count" field="Count( comments.id )" renderer="myNumberFormatter" />

Caching

You can opt to cache your preside data object views by passing in caching arguments to the renderView() method. A minimal example:

rendered = renderView(
      view          = "event/detail"
    , presideObject = "event"
    , id            = eventId
    , cache         = true     // cache with sensible default settings
);

See the renderView() method documentation for details on all the possible arguments.