Drupal often makes certain things so easy to do, that we as developers don't take the time to consider the alternatives. Lately I've been giving a lot of thought to the module dependency system and possible alternatives. The main reason for this is that dependencies create a more brittle overall system which in turn makes the code more difficult to maintain and re-use.
Scenario 1
I've seen many install profiles and modules that include modules like overlay or toolbar as a dependency. In almost every case, this was done so that those modules would be installed by default. The problem with using the dependency system is that these modules cannot be removed without modifying the code. If you wanted to use admin_menu in place of toolbar, you would need to first remove the dependency from the info file.
Solution
This one has a simple solution. Rather than using dependencies to install modules, use a simple install hook with module_enable.
* Implements hook_install().
*/
function example_install() {
// Enable optional modules.
module_enable('toolbar', 'overlay');
}
Scenario 2
Say we have a module that creates a basic article node, with a title, body and taxonomy field for tags. Typically we would make taxonomy module a dependency of the article module. In the spirit of reusable code, we want to make our article module as self-contained as possible, so that we can use it on all of our Drupal projects.
But what if we have a project that has no need for taxonomy? Must we always enable the taxonomy module to use our article module, even if there is no need?
Solution
The answer to our problem comes in the form of a couple of new hooks for Drupal 7: hook_modules_enabled and hook_modules_disabled, give us the perfect opportunity to act upon new dependencies being enabled and disabled.
First we will create a helper function for our optional functionality. In this case, we want to create a tags vocabulary and add a taxonomy reference field to the article node type, only if the taxonomy module is available.
* Add the "tags" taxonomy field to a node type.
*/
function article_add_tags_field($type) {
if (module_exists('taxonomy')) {
// create the tags vocabulary and add the field to the bundle, making sure
// to check if they already exist.
}
}
Next, we implement hook_enable to create our article node type. This technique works equally well with hook_install so choose the hook that is most appropriate for your situation. Notice that we are checking the existence of the taxonomy module before calling our helper function. This check could be skipped since it also happens in the helper, but I think checking it here reinforces the idea that this is an optional feature.
* Implements hook_enable().
*/
function article_enable() {
// Borrowed from standard.install.
$type = node_type_set_defaults(array(
'type' => 'article',
'name' => st('Article'),
'base' => 'node_content',
'description' => st('Use <em>articles</em> for time-sensitive content...'),
'custom' => 1,
'modified' => 1,
'locked' => 0,
));
node_type_save($type);
node_add_body_field($type);
if (module_exists('taxonomy')) {
article_add_tags_field($type);
}
}
Lastly, we implement hook_modules_enabled. This hook is called by the module system any time one or more modules are enabled. Here is where we have the opportunity to look for newly enabled modules that our module knows how to integrate with. If the taxonomy module is in the list of modules that have been enabled, we can fire our helper function.
* Implements hook_modules_enabled().
*/
function article_modules_enabled($modules) {
if (in_array('taxonomy', $modules)) {
article_add_tags_field(node_type_load('article'));
}
}
For completeness, you should probably also implement hook_disable and hook_modules_disabled, but I'll leave that as an exercise for you.
We are using these two methods to keep our dependency chain light and add in optional functionality on our Drupal 7 projects. I think there is an opportunity to build true support for these types of optional integrations into Drupal 8. I'd love to hear how others are handling this situation. How are you handling dependencies and integrations in your modules, especially ones meant to be used in a variety of contexts and situations?

How would you solve the problem if you exported the article content type with a taxonomy using features?
Hi Roger,
Thanks for sharing! Interesting post, as we just had a similar debate in house about how to best allow other modules to hook into a new module that we're developing.
After thinking about the "if this module is enabled approach", we decided that discrete submodules that use the dependency system and implement hooks was the most robust.
I see the simplicity of the approach that you are describing and it could cut down on the number of modules enabled on a site. For one-time events like adding a field to a content type on install, I could see us using it.
However, I think that there's fragility in building functionality that could accidentally get "turned off" for one module if another is disabled - particularly when talking about building modules/sites that are intended to be passed off to another developer or client to maintain.
"Hooks and dependencies" seems to be the Drupal way. Might not be the best in all ways, but it's what other developers/configurers expect when then turn things on/off. There are a few really big modules like Views and CTools that do their own thing - but there are different reasons for that then what I think we're talking about.
What do you think about this "fragility" argument?
Cheers,
Sean
make sub module of article called article_taxonomy and make article_taxonomy dependent on taxonomy and article
then when article_taxonomy is installed it adds the tags field to article bundle and when uninstalled removes the field .
@Lars: this is a technique that would not be immediately compatible with features. Features assumes that you want the module to be as-is, so all integrations are assumed to be dependencies.
@Sean: The fragility of functionality "accidentally getting turned off" is already there in all of Drupal. One has to assume that disabling modules is going to disable functionality, and disabling modules must be done with caution. If you follow Drupal's standards of not destroying data until uninstall, then simply disabling a module, like taxonomy in this case, will not destroy the data, so re-enabling the taxonomy module will get both the functionality and the data back. You could also alter the module page to provide a warning on any optional modules.
@yakoub: that was the first method I explored, but dislike having so many modules. Definitely works though.
Just a small correction:
Thanks for the writeup. I went ahead changing my install profile. However, figured out that dependencies[] in install profiles is actually not hard dependencies. Still, I think it makes more sense to enable them in the hook_install(), as only a few modules are really dependencies - but more something which is nice to have.
There is a Default Config module from NodeOne, which is based on Features. They are using it in NodeStream distribution.
Check it out: http://drupal.org/project/defaultconfig
Post new comment