Skip to content
neoneo edited this page Jan 25, 2012 · 34 revisions

Configuring CFlow not only allows you to describe your application flow, but it also adds some nice features.

First, you get to define tasks for different phases. There are 5 phases, in order of execution: start, before, event, after and end.

  • start: these tasks are executed exactly once during the whole event handling cycle.
  • before: these tasks are executed every time an event is dispatched on this target, but only if the event is not canceled in one of the start tasks.
  • event: these tasks are executed if a matching event is defined here, again only if the event is not canceled in one of the previous phases. If not, then nothing happens unless implicit tasks is set.
  • after: these tasks are executed every time an event is dispatched on this target, with the same condition as the previous phases.
  • end: these tasks are always executed exactly once, regardless of event canceling.

These phases allow you to describe most things that you might want your application to do. This includes aspect oriented things like logging for instance.

All this you can also do programmatically, by using the methods provided by Context.

The second nifty feature you get with configuration, is not available programmatically. You can include definitions from one target in another. These includes can be recursive: a target that is included may itself include yet another target, and so forth. See below at the <include> node description.

The XML dialect

The XML allows you to define events within targets. You can do this in one file, or in separate files within the same directory. If you define multiple targets in one file, the root node you use <targets>. Within it you define one or more <target> child nodes. A <target> node itself can also be the root node, in which case you define one target in that file.

Below all the node types are described, followed by an example.

Attributes:

  • name: the name of the target
  • defaultcontroller: the name of the controller to use for invoke tasks, if no controller is specified there (optional)

<target> can have the following child nodes:

Defines tasks for the start phase.

Defines tasks for the before phase.

Defines an event for this target. Attributes:

  • type: the event type (required)

Defines tasks for the after phase.

Defines tasks for the end phase.

Includes tasks from another target. Attributes:

  • target: the name of the target to include tasks from (required)
  • event: the event type to include (optional). If no event attribute is present, the complete target is included.

Including another target occurs following these rules:

  • If the target already has a definition for the event type, the event is not included.
  • If the target has start tasks, start tasks from the included target are prepended to the existing start tasks.
  • For before tasks, the same goes as for start tasks.
  • After and end tasks are appended to any existing after and end tasks.

Defining tasks within the above nodes (phases) is done with the nodes <invoke>, <dispatch> and <render>.

Invokes a method on a controller. Attributes:

  • controller: the name of the controller (optional). If this attribute is not present, the default controller specified in the <target> node is used instead.
  • method: the name of the method to invoke (required).

This node can contain child task nodes. These are only executed if the event is canceled.

Dispatches an event. Attributes:

  • target: the target to dispatch the event on (optional). If not present, the target is the one this task is defined in.
  • event: the event type

If the dispatched event is canceled, the current event is also canceled.

This node can contain child task nodes. These are only executed if the dispatched event, and therefore the current event, is canceled.

Renders a view by including the corresponding template. Attributes:

  • template: the template to render. Leave out the .cfm extension, it is appended by the task. The task looks for the template using the view mapping (if set), in a directory with the name of the target.

As an example, let's define some tasks for a hello world application that needs authentication. This allows us to use most of the features above.

<?xml version="1.0"?>
<targets>
    <target name="session" defaultcontroller="Session">
        <start>
             <invoke method="authenticate">
                 <dispatch event="failed" />
             </invoke>
        <start>
        <event type="failed">
             <invoke controller="global.Logger" method="write" />
             <render template="failed" />
        </event>
    </target>

    <target name="world" defaultcontroller="World">
        <include target="session" />
        <include target="template" />
        <before>
            <invoke controller="global.Navigation" method="storeCrumbTrail" />
        </before>
        <event type="hello">
            <render template="hello" />
        </event>
        <event type="goodbye">
            <dispatch target="session" event="exit" />
            <render template="goodbye" />
        </event>
    </target>

    <target name="template">
        <start>
            <render template="header" />
        </start>
        <end>
            <render template="footer" />
        </end>
    </target>
</targets>

As you can see this is pretty readable.
By including the session target in the world target, we've put everything that's happening in this target behind authentication. Following the rules above, in the start phase, first the header template is rendered, then the Session controller's authenticate() method is invoked. If authentication fails, it cancels the event. Then the task's child tasks are executed, an event is dispatched. This event defines one render task, which displays the access denied page. After this, the end tasks (rendering the footer) are still executed.
Look at the order of the includes: the last include's start task are executed first, and its end tasks are executed last.

In the goodbye event in the world target, an event 'exit' is dispatched on the session target. This event is not defined in that target, so what happens now depends on whether setImplicitTasks(true) was called on the Context. If so, then two tasks are created and executed, based on the framework convention. The first task is an invoke task that will try to invoke the method exit() on a controller session (the target name, defined as lower case here!), followed by a render task that will try to render the template session/exit.cfm. So you can mix configuration with convention, although it's probably not good practice.

Clone this wiki locally