This post is primarily focused on how to use the Rules module to automate actions on your Drupal site. It discusses the differences between a Rule and Rule Component; how to use Rules to identify, evaluate and modify associated fields of a node or other entity; and how to pass data to a Rule with the help of Views Bulk Operations. This post is based on my own project where certain fields of a node need to be periodically updated based on the data of other fields in the node.
If you’ve spent some time with Drupal you’ve probably come across the Rules module. Rules provides the means to program your website to do actions when certain events occur. It is similar to the Trigger module provided with Drupal Core but provides a greater range of functionality. While you can use Trigger and Rules modules at the same time, you’ll probably want to use Rules for any custom automation or workflow actions in your Drupal site. Make sure you activate both the Rules and Rules UI modules. Rules Scheduler can also be helpful if you want to be able to activate a Rule Component close to a specific date and time.
Rules come in two main flavors, regular “reaction” Rules and Rules Components. The main difference between the two are that “reaction” rules are set to fire on certain “events” that happen on your website while Rule Components only act on data that is passed to them. Reaction Rule events include when a node is published, a user logs in or a comment is made. With these triggered events, the corresponding content entity object, such as a node (title, body, author, custom fields, etc.), is made available to the Rule to act on.
Rule Components, on the other hand, require you identify the kind of data that you expect to be passed to it. This data is usually a node, user or other entity object, or perhaps, a list of fields to be acted on. A Rule Component is usually called by another Rule or within a module. Rule Components can also be scheduled to fire using the Rules Scheduler module, included with Rules.
To help explain Rules, let me explain my recent Rules project which requires the use of both a reaction Rule and Rule Components, as well as the use of Views Bulk Operations. Here is the scenario:
I have a node content type for tracking shipments, it includes fields for a shipment date and whether it is completed or not. For those shipments that are not complete and the shipment date is past the current date, I want to 1) copy the current shipment date to another date field, original shipment date, if the original shipment date is empty, and 2) update the shipment date to the current date. I want this Rule to be automated and not necessarily triggered by a user.
I’ll start by setting up two Rules Components for implementing the changes to the shipment date and original shipment date. Why do I use Rules Components over regular “reaction” Rules? Well, unfortunately, if I set up Rule to be triggered by Rules Scheduler or a periodic System event such as a cron or Drupal initialization, there will be no node object data associated with these events to act on! Thus, I’ll need to get these node objects by another method in order to apply Rules to them and in this particular “pass through” situation only Rules Components are going to work. More on that later.
When setting up a Rules Component, I first need to identify what kind of “Component plugin” I want to use. In this case, I am going to implement a “Rule” which provides both the means to identify and act on the content I want to change. The other plugins may be useful for only content evaluation, actions to change content, or calling other rules and are used lest often.
After selecting the Rule plugin, I then add a name for my Rules Component and optionally add tags to help classify my Rule. My first Rules Component will adjust the shipping date in the node, so I have named the overall Rules Component “Ship Change.”
I then determine which variables I expect to be passed to my Rule Component. Basically, the data types of variables that I might expect are either going to be full entity objects (node, taxonomy, etc) or a particular field type of an expected format (date, ip address, text, integer, etc.). These variables may expected to passed to the Rules Component individually, or as part of a list.
I am expecting my Rules Components to be passed a single, complete node object. Thus, I will identify my variable Data Type as “Entity: Node” and assign “shipnode” as both a label and machine name so I can identify it as I go about creating my Rules Component. I leave the Usage set as “Parameter”
After clicking the “Save Changes” button, I am then presented with two sections that make up my Rules Component, “Conditions” and “Actions.” Conditions provide a means to evaluate elements of an object or field as “true” before firing the associated Actions. However, what is not well documented is that Conditions are also used to “pull in” data that might not otherwise be readily accessible in the variable being passed the the Rule.
For example, even though we are passing the entire node object of a particular shipment, I don’t initially have access to all the fields added to this node’s content type! To get access to them, I need to add Conditions to see if the fields I want to work with are associated with the node, which will then make these fields available to evaluate and act on.
To get access to the field within the shipment content type, I start with adding a Condition: “Entity has field” and select the “shipnode” entity (by itself), which is the node object that I expect to pass to my Rules Condition. I then can select the particular field I want to access. In this case, the shipment date (field-ship-date).
I can now add another Condition to evaluate the data in the shipment date field. I select “Data comparison” and can now select “shipnode:field-ship-date” and click Continue. Since I am checking for whether the ship date is older than the current date, I select the Operator to “is lower than” and enter the relative date of “now” into the Data Value field and click the “Save” button.
With these “Entity has field” and “Data comparison” Conditions I have successfully accessed and evaluated the shipping date field within my shipment node. By using the “Entity has field” Condition, I have also made this field available to be modified by the Actions section of my Rules Component. After adding additional Conditions to access and check to make sure that the shipment is not marked as complete, I can then move on to add an action to my Rules Component.
For my Action, I select “Set a data value” and specify the Data: shipment date to be set to the relative date Value of “now” and click the “Save” button. Now my Rule Component is complete.
When fired, the “Ship Change” Rules Component will check to see that the shipment date is lower than the current date and that the shipment is not complete before acting to change the shipment date to the current date. I also add an additional Rules Component, “Set Original,” to set the original ship date to the current ship date, if the original ship date is empty.
Now that I have my Rules Components set up, I need to 1) find a way to identify the node objects for my Rules Component to act on and 2) add another Rule or Rule Component to trigger them.
Views Bulk Operations (VBO)
As I mentioned above, one of the challenges in working with Rules that are scheduled or trigged by periodic System events is that there is no node object or other entity data readily available to the Rule or Rules Component to evaluate or act on. Thus, we need another way to generate a list of nodes for our Rules to work with. A solution to this problem is the Views Bulk Operations (VBO) module.
VBO is primarily used as an addition to the Views module to provide an interface for a host of bulk operations to act on view results, including changing fields, publishing nodes, deleting entities, etc. In addition, views with VBO are also available as Actions within Rules and, when fired, will pass the entire entity objects of each row returned by view (it doesn’t matter which fields are included in my Views displays, Rules gets the whole object!). Thus, I can create a view with conditions to list shipment nodes that are incomplete and overdue, add a VBO checkbox field, and now have my view available to Rules. Should I choose, I can even use Rules to pass arguments for Conditional Filters to the view.
With my VBO enabled View set to return a list of shipment nodes for my Rules Components to work on, I have two options for triggering my Rules Components. I can either write a “reaction” Rule to be triggered by a System event, such as running cron or initializing Drupal, or I can schedule another Rules Component to fire at a certain time using Rules Scheduler. Either option will work, although there will be less impact on the speed of my site if I opt to use Rules Scheduler rather than with every cron run or initialization.
“Reaction” Rule with Cron Event
When adding a reaction Rule, in addition to naming and tagging the rule, I select the “Cron maintenance are performed” System event for the “React on event” field.
This brings me view the Events, Conditions and Actions sections, of which the Conditions and Actions sections act exactly as they do within a Rules Component. Because I am not acting on any variables passed by the cron event, I will leave the Conditions section blank and will work exclusively within the Actions section.
My first Action to add is “Load a list of entity objects from a VBO View” where I select my view display. For the List of Entries I user the default Variable label, “A list of entries,” and name, “entity_list,” then click the “Save” button.
I then add a loop where I select my default “entity-list” in the Data selector and then, under Current List Item, I use the default Variable label, “Current list item,” and Variable name, “list_item,” then click the “Save” button.
On the line of the Loop, I click the Add action link and select the “Rule: Set Original” action which will call my “Set Original” Rules Component. I then set the Data selector to “list-item” and press the “Save” button. I add another action to call my “Ship Change” Rules Component.
Now, whenever the cron maintenance event is run, my reaction Rule will use VBO to load the resulting node entity objects into a loop and pass them to my Rules Components to be acted on.
If I’d rather schedule my rule to run at a certain time rather than with every cron job, I can activate the Rules Scheduler module which ships with Rules and write a Rules Component, “Ship Check,” with no specified variables and with the same Actions I specified for my reaction Rule. The only difference being that I add an action to reschedule the Rule Component so that it fires the next day. To do this, I add an action, “Schedule component evaluation” and specify the current Rules Component, “Ship Check” and the Schedule Evaluation Date to the relative date, “midnight +1 day.” That way I am sure that the rule will be rescheduled to fire each day.
Rules is a powerful and flexible module, but the challenge is often figuring out how to access the content that you want to act on. Understanding the use of the “Entity has field” Condition to pull in the fields in your content type is critical to being able to use Rules effectively. Utilizing VBO is also necessary for times when you want apply Rules and Rules Components on nodes and other entities that are outside of the context of an event that triggers the Rule. Together, VBO and Rules can be an important toolkit for automating your Drupal website.