Drupal Site Builder Patterns - The State Machine
In this new series for my blog, I'll be documenting some common design patterns for
Drupal site builds. This first post is about the State Machine pattern, which is
something I've used on several sites recently.
First, let me explain what I mean by "pattern". If you are already familiar with
design patterns in Object-oriented software then you can probably skip this
bit, but I think it's useful for context.
Design patterns?
Here's a quote from the original Gang of Four book on
design patterns. That book is about design of object-oriented software, but I think it
applies to Drupal development too.
The quote is from p.1 of the book, apologies if I offend anyone by bastardising it.
I've taken the liberty of substituting
the words "Designing object-oriented software" with "building Drupal sites",
and a few other substitutions to make my point...
[Building large maintainable Drupal sites] is hard... Your design should be
specific to the problem at hand but also general enough to address future
problems and requirements [and be maintainable]... Experienced
[Drupal site builders] will tell you that a reusable and flexible design
is difficult if not impossible to get "right" the first time.Yet experienced [Drupal site builders] do make good designs. Meanwhile
new [site builders] are overwhelmed by the options available and tend to fall
back on non-[Drupal] techniques they've used before. It takes a long time for
novices to learn what good [Drupal site building] is all about.
Experienced [site builders] evidently know something inexperienced ones don't.
What is it?One thing expert [site builders] know NOT to do is solve every problem from
first principles. Rather, they reuse solutions that have worked for them in
the past. When they find a good solution, they use it again and again.
Such experience is part of what makes them experts.
So I've been looking at what these "good solutions" are that I might have been
using, and as I identify them I've been documenting them along the same lines
of the original design patterns from the Gang of Four book:
- Pattern name - the handle we use to describe the problem
- Problem - explain the problem and its context, and when you might want to
use this pattern - Solution - describe the elements that make up the solution, in my case how the
pattern can be best implemented in Drupal - Consequences - results and trade-offs of using the pattern, in this case I
also consider further issues that many need to be considered as a result of
using the pattern.
So, first let's look at what a state machine is, and what problems it solves,
before going on to look at how to configure it in Drupal.
State Machine
A state machine is a theoretical computer science concept that provides a
mathematical basis for modelling computation. But don't worry, the kind of state machines we'll be
using don't require a degree in computer science to understand.
All you really need to know is that the state machine (or more correctly a
Finite State Machine) has
a finite number of "states" it can be in and the machine is only ever in one
of these states at a time, it's current state. The state machine can
change from one state to another triggered by an event or condition. This change
of state is called a transition. A state machine is typically visualised
using a state machine diagram, for example:
As you can see the states are represented by an ellipse with the name of the
state inside, the arrows denote the possible transitions. You can also see
how the entry point and exit point would be notated.
Here's a (very simplified) example of a ticket in an agile issue queue. In
reality this would probably have several other states but for the sake of this
example, here's a simple state machine for the ticket:
A state machine is defined by the list of possible states and the event/condition
that triggers each transition.
If you're reading this and thinking "Events", "Conditions", sounds a bit like
Drupal Rules, then you've already worked out how we're going to
implement this in Drupal!
In this simple ticket example the states are "In progress", "Approval", and
"Finished". The transitions are "Completed", "Rejected", "Accepted".
When to use it?
It might be useful to think that in business speak, when they say "business
processes" they are actually talking about state machines. Here are some
cases when you might want to think about state machines:
- If you've ever had to model a "state" or "status" field, then you've got a good
candidate for a state machine. - If you've ever wanted to anything
more complex than just published and unpublished nodes then you have a good
candidate for using a state machine. - If you have boolean fields in your content model called things like "paid/unpaid".
- If you have records that need to expire after a specific period of time
Drawing out a state machine diagram to model this kinds of problems can be
really useful to help identify any "edge-case" scenarios you may not have
thought of, and capture them early in the design process. It also shows you
exactly what you need to test further along in the site build.
Let's build it
As with anything in Drupal there are several ways to achieve this functionality,
in fact there's even a State Machine
module, but that relies on creating custom plugins.
If you're a developer you might want to take a look at this module.
Workbench Moderation and
various other workflow modules include a state machine implementation for a
specific purpose.
The approach documented here is suitable for site builders, is flexible,
and provides a neat solution that can be configured using the following
contributed modules:
I said before that the state machine is defined by it's set of possible states
and set of transitions. In Drupal we'll be using a simple list field to store
the list of possible states for the node.
In a recent post
on Drupalize.me they mention the addition of the ability to hide form fields
in Drupal 8 core. In Drupal 7 we need a module to help us do this. In this case we are adding
a field that will never be directly edited by the user so we just deny access
to edit that field using the Field Permissions
module.
For the simple ticket example, we have 3 states. So use an integer list field with
the following allowed values:
- 0|In progress
- 1|Awaiting approval
- 2|Finished
I said that the state machine was defined by the set of possible states
(implemented by our list field), and a set of transitions. These transitions
can be implemented using the Rules Link
module.
Using the Rules Link module you can add a button to the ticket node which
manipulates the "state" value preventing the user from actually editing the
value in the state field directly, and thus enforcing the workflow defined
in our state machine.
Each "Rules link" is configured in two parts. First you define the conditions
for when the link should be visible using standard Rules conditions. Secondly,
you use the rules reaction to set the value of the state field to the new
value (and perform any other actions that you want as a side effect of the
transition).
Considerations
It's good to follow a principle of audit-ability, so you probably need to keep
the transition history. A simple solution might be to
add a timestamp field such as "confirmed at" to mark when it went to confirmed state.
If using node, you could log revisions to track state changes in the revision
log for the node. Or you could look at Messages module to log messages when state
changes happen.
More patterns
If you're interested in learning more from my 7 years of Drupal experience
(and if you're based in London) why not
join me for Everything I Know About Drupal an
intensive 2-day Drupal training I've been working on. It's taken a lot of
preparation, and there's still a small number of tickets available.
You can
find more information on my blog post
about it
or grab a ticket on the Eventbrite page.