Using the ECA module to replace a not-Drupal-10-ready contrib module (Termcase)
I've been a big fan of the Termcase module for a long time - it is my go-to module for sites that employ free-tagging taxonomies. Termcase allows site-builders to force consistency on tag names (all lowercase, all uppercase, first-letter uppercase, etc…) It's one of those modules that I was surprised that not everyone used (or had even heard of.) While working to update a Drupal 9 site that uses Termcase to Drupal 10, I became curious if free-tagging was still a commonly-used technique on modern Drupal sites.
In an effort to satisfy my curiosity, I performed a 100% not scientific poll recently on various social media platforms to see how prevalent the use of free-tagging vocabularies are; the results weren't very enlightening; of the 60 responses I received, the vote was about split.
Poll: do you have any free-tagging term reference fields on a modern Drupal (8/9/10) site you own/manage/built?
This didn't really satisfy my curiosity about why not everyone is/was using the Termcase module. Surely, I'm not the only person who is adamant about their free-tagging terms all having the same capitalization scheme… 🤨
Motivation
Officially, there's no Drupal 8 or 9 version of Termcase, but it has been limping along with a two patches that allowed me (and a few others) to keep using it in Drupal 8 and 9. While there is a Drupal 10 compatibility patch available, it seems pretty clear that the listed maintainers have no interest/bandwidth in updating the module, so I figured it was time to consider an alternative that would provide a solid solution in Drupal 9, 10, and beyond. 🚀
Again, I went to social media for answers (ChatGPT wasn't much help) and one of the maintainers of the ECA module, Jürgen Haas, quickly responded that ECA could help (I'm pretty sure that is Jürgen's default answer to most Drupal questions these days 😀). He pointed me toward the Convert case action of the ECA Tamper module. Excellent, now all I needed to do is learn how to use the ECA module 👍🏼
ECA module 101
The ECA module is the modern-day successor to the formerly widely-used Rules module that was a popular solution for pre-Drupal 8 sites.
Disclaimer: I did have a little bit of prior experience in experimenting with ECA, but this would be my first real implementation for a client using the module.
I'm not going to go into all the details of the basics of installing and using ECA, as the existing documentation is quite robust, if not yet completely geared toward beginners (more on that later.)
To get started, I used Composer and Drush to install ECA and the necessary modules:
$ composer require drupal/eca drupal/eca_tamper drupal/bpmn_io$ drush en bpmn_io eca_tamper eca_content
This results in the following modules being enabled: bpmn_io, eca_modeller_bpmn, eca_ui, eca_tamper, eca, and tamper.
Next, I had to create a new ECA model (for those of you familiar with the Rules module, an ECA model is similar to a rule).
One counter-intuitive aspect about the ECA module is its name and the nouns it uses in its interface. ECA is an acronym for the nouns Events, Conditions, and Actions. But, when using the BPMN.iO (Business Process Model and Notation standard) interface module, the nouns used are StartEvent, Sequence flow, Gateway, and Task. For folks new to this type of module, this can be confusing. I am confident that this is due to the design of the BPMN.iO interface, and not a purposeful decision by the ECA maintainers just to confuse us. StartEvent and Task map pretty cleanly to Event and Action. A Sequence flow is a connector between StartEvents, Gateways, and Actions. A Sequence flow can optionally have a condition. A Gateway can be added after a Sequence flow to allow for one or more Sequence flows with conditions to be evaluted.
- Event = StartEvent
- Condition = Optionally attached to a SequenceFlow
- Action = Task
For example, consider the model where a "Batman message" is displayed if an article node has a "Batman" term associated with it.
In this model, the Gateway just provides a point where the two potential Sequence flows ("Batman?" and "No Batman") can be evaluated. The Gateway doesn't usually have any non-trivial properties - it is usually a branch point.
Replacing Termcase with an ECA model
For this model, in non-ECA speak, the goal is to lower-case all terms of the Tag vocabulary prior to each being saved. Therefore, our Event/StartEvent will be Presave content entity. Then we will need to perform the Convert Case action (from the ECA Tamper module) on the term name, and finally we will need to update the name value of the about-to-be-saved term to the updated (lowercased) term name. For good measure, when developing a new model (as well as a new rule back in the day) I generally also add a temporary Drupal message action (task) to confirm that things are working.
I'll get into the details of each of the aforementioned steps, but here's a quick preview of what the completed model looks like:
lowercase terms model details
Taking a not-quite comprehensive look at each aspect of the model:
Presave tag
After dragging a new StartEvent to the layout area, the first step is generally to select a Template. Now, this is not a theming template, but rather the event for which this model will be triggered on. In this case, the Presave content entity template was used (which is provided by the eca_content module.) Each StartEvent, Gateway and Task has both an ID and a Name - both customizable by the model author. I generally leave the ID at the default value, but populate the non-required Name field with something descriptive. In this case, I used Presave tag. Each template generally has a set of Custom properties. Not surprisingly, different templates generally have different Custom properties. In this case, it was pretty obvious that I needed to set the Type (and bundle) Custom property to Taxonomy term: Tags.
Lowercase
Next, I dragged a new Task to the layout area, named it Lowercase, and selected the Tamper: Convert case template. The list of available templates is quite impressive (IMHO), these are provided by the various installed ECA-related modules. Since the ECA Tamper module is installed, we have access to all of its templates.
Setting the Custom properties of the template (again, think of this like the configuration of the task/action) is a bit more tricky - especially for folks new to the ECA module. The ECA module utilizes Drupal's Token system extensively. In many cases, it also relies a bit on the model author's knowledge of Drupal data structures.
In this case, I used the following Custom properties (configuration):
- Data: [term:name]
- Token name: my-tag
- How to convert case: Convert to lowercase
This is where I think the ECA module needs some DX (developer experience) tweaks. For the uninitiated, knowing that Data is the input and Token name is the output is not obvious, but that is exactly what is going on here. The Presave tag StartEvent provides the term to be saved as a Drupal token. Since we want to lowercase the term name, we need to set Data (incoming) to the [term:name] token.
The result of the Task (action) is then saved to a new custom token whose name we specify in the Token name field. The lowercased term name is not automatically saved back to the term name field which is why we need to save the result of this task as a new custom token. We will save the updated term name back to the Term entity in the next Task.
Based on an online discussion with Jürgen regarding how this would probably be difficult for beginners to pick-up on quickly, he opened (and acted on) an issue.
Set updated tag name
The final Task (action) is then to set the new (lowercased) value of the term name back to the about-to-be-saved term entity. This is done with the Entity: Set field value template. There are quite a few Custom properties for this template, but in this case, the only ones I set to non-default values were:
- Field name: name
- Field value: [my-tag]
- Entity: taxonomy_term
This is where the DX of the ECA module could again be much improved (IMHO.) The Field name value requires a bit of knowledge that not all Drupal site-builders have - that is the machine name of the taxonomy term entity's Name/Label field. Granted, it is a pretty obvious value (name,) but it easily could also be label or title - it would be amazeballs if the user was presented with a select list of potential values instead of a text field in the UI, but I do appreciate the amount of effort that would entail.
The Field value field is a bit better, but again, not entirely intuitive. This is the input to the task - that is - the value to which the field will be set to. It is a bit tricky to know that in the Lowercase action only the token name is needed (my-tag), but in this task the full token (including square brackets) is required ([my-tag]).
The Entity field also requires knowledge that not all Drupal site-builders may have - the machine name of the entity type to which the field value is being set for. In this case, the proper value is taxonomy_term, but I'll admit I was a bit surprised that this isn't something that the Task can figure out on its own - or at least provide a select list for. Again, I suspect (and appreciate) that it has to do with how each Task is written completely independently (and without dependencies) on other aspects of the ECA model.
I've opened up a new issue in the ECA module's queue with these thoughts.
For me, one of the more significant challenges of using the ECA module is understanding what each Custom property is actually asking for and determining if it is input or output of the task. I think there is some room for improvement.
Something else that originally tripped me up in this task was the Save entity setting. It defaults to no, and I mistakenly thought to myself, "of course I want to save the entity" and set it to yes; which led to a fatal PHP error. Again, Jürgen was very responsive and set me straight by explaining that since the StartEvent is Presave content entity, there is no need to set Save entity to yes, the term is already on its way to being saved. By selecting yes, the model will effectively try to save the same term twice; leading to the fatal error.
Temporary confirmation message
As mentioned previously, I often include a temporary task/action to output something that tells me that the model is actually running and achieving its goal. Once I am confident in the model, I often remove this task.
In this case, the Display a message to the user task template was used with the following Custom property:
- Message: Tag updated: [my-tag]
Final "gotcha"
Maybe it is just me, but there's one thing that I keep doing in ECA's BPMN.iO's interface that can be very confusing at first - I often find myself not paying attention to what is selected in the layout area when looking at the "inspector" (I'm not really sure what else to call it) so that I get a bit confused with the configuration options. Somehow, I frequently have an arrow connector selected (a Sequence flow in BPMN.iO parlance) when I think I have a task selected (Sequence flows have properties as well!) So, my advice is to be methodical and pay attention to what is selected in the layout area when looking at configuration options.
I'm not sure there's anything that the ECA maintainers can do about this. I liken it to a similar habit I have of moving too quickly when configuring multi-display Views and overriding/not-overriding values incorrectly. My advice: Move slowly and methodically in the interface.
Summary
This was a good exercise and excuse for me to take a (slightly) deeper dive into the ECA module. Based on its documentation and videos about the module that I've seen, this article barely scratches the surface of what ECA can do. I look forward to finding more reasons to use it!
It is pretty clear that the maintainers are super-active and super-responsive to feedback and requests for support; take advantage of them. 😃