- Home
- Blogs
- George Boobyer's blog
- Drupal - My view has a null module dependency
Drupal - My view has a null module dependency
We often get asked to troubleshoot issues on Drupal sites and recently, twice in succession on different Drupal 8 sites, we came across an issue where a configuration file contained the word null in its list of module dependencies.
dependencies: module: - null
This was causing a number of issues and is clearly wrong. When trying to import configuration for example you are faced with a problem because Drupal thinks the configuration relies on a module called null which clearly doesn't, and never did, exist.
So just create a module called 'NULL' - Problem solved :p
There actually is a Drupal contributed module 'null' that is provided specifically to overcome this issue - but it effectively null routes the error by providing a module called null to match the dependency - it addresses the symptoms not the cause - https://www.drupal.org/project/null - if you are in a desperate hurry to move on with your life then that might be for you - if you want to solve the cause read on.
If this does happen to you then you will have problems importing configuration and in our case the view whose configuration file was affected also raised an error when being saved.
Notice: Undefined index: table in Drupal\views\Plugin\views\relationship\RelationshipPluginBase->calculateDependencies() (line 179 of core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php).
Let's investigate
I thought it would be a good opportunity to write up the investigation as I didn't find a lot of similar issue when Googling and I am sure I'll come back here at somepoint myself.
This is relevant to Drupal 8 and Drupal 9
In the scenario that follows the site had a custom module that is used to attach Promotions to line items. The module creates a number of tables for this purpose and declares a schema to describe the tables and their fields. These tables and some of their fields are used in the affected view.
So what is going on! - Why is the configuration writing null as a dependency?
The view's configuration file contained the following excerpt:
uuid: 12345678-1234-5678-ab9ba-9876d54cba32 langcode: en status: true dependencies: module: - null - csv_serialization - mycustom_schema - rest - serialization - user - views_autocomplete_filters - views_data_export - views_date_format_sql
Custom schema tables
The tables in this case are:
- A custom line item table
- A table to contain data about promotions and
- A join table to link them (I have simplified this for illustration)
The view surfaces some of the fields from the Line Item and Promotion tables and it all works ok in the view - but when you save the view you get:
+---------------+ +--------------+ | | | | | Line Item | | Promotion | | | | | +-------+-------+ +-------+------+ | | | | | +-----------------------+ | | | | | +--->| LineItemPromotion <--------+ | | +-----------------------+
Notice: Undefined index: table in Drupal\views\Plugin\views\relationship\RelationshipPluginBase->calculateDependencies() (line 179 of core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php).
But the view works.
Roll up your sleeves
So how do we troubleshoot this and find out what is causing the issues:
- You get an error when you save the view,
- The config for the view has null in the name of a dependency and
- You can't import the configuration
The first thing to check is if the schema of the data model is implemented correctly in the modules install file in
hook_schema()
This is where you define the schema so the tables get created when the module is installed. When investigating this it became apparent that the tables were modified in subsequent and successive updates (adding new fields and indexes etc)
hook_update_N()
As it happens, although the schema was updated by these updates, the module schema itself was not updated - this was not the source of our problems, but would be a problem when the module was installed in the future.
You should always update the schema with such updates.
Plugin Dependencies
But fixing that does not solve our specific issue. Furthermore, our module is present in the list of dependencies for the view and the view works (although it errors when saved and we can't import the configuration). So we need to see why Views can't work out the dependency and places a null in the config.
To do this we need to understand how Views determines the dependencies and see what the actual source of the error is - maybe not our module after all?
When the configuration for a view is written, Views calculates the dependencies of fields and other things (plugins) used in the view. It does this in RelationshipPluginBase:
web/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php abstract class RelationshipPluginBase extends HandlerBase { .... other stuff removed for readability .... 172: /** 173: * {@inheritdoc} 174: */ 175: public function calculateDependencies() { 176: $dependencies = parent::calculateDependencies(); 177: // Add the provider of the relationship's base table to the dependencies. 178: $table_data = $this->getViewsData()->get($this->definition['base']); 179: $dependencies['module'][] = $table_data['table']['provider']; 180: return $dependencies; 181: } 182: 183:} 184: 185:/** 186: * @} 187: */
This is called for all of the plugins (fields in our case) required by the view and this in turn calls getViewData() to find out which module each plugin comes from, to build up a list of dependencies.
When we come to line 178 we go off to web/core/modules/views/src/ViewsData.php which looks at all modules that implement hook_views_data() to see which is the originator of the plugin.
235: protected function getData() { 236: $this->fullyLoaded = TRUE; 237: 238: if ($data = $this->cacheGet($this->baseCid)) { 238: return $data->data; 239: } 240: else { 241: $modules = $this->moduleHandler->getImplementations('views_data'); 242: $data = []; 243: foreach ($modules as $module) { 244: $views_data = $this->moduleHandler->invoke($module, 'views_data'); 245: // Set the provider key for each base table. 246: foreach ($views_data as &$table) { 247: if (isset($table['table']) && !isset($table['table']['provider'])) { 248: $table['table']['provider'] = $module; 249: } 250: } 251: $data = NestedArray::mergeDeep($data, $views_data); 252: } 253: $this->moduleHandler->alter('views_data', $data); 254: 255: $this->processEntityTypes($data); 256: 257: // Keep a record with all data. 258: $this->cacheSet($this->baseCid, $data); 259: 260: return $data; 261: } 262: }
From line 244 on it gets a list of tables for all modules that implement hook_views_data and looks for the table name from the plugin.
If none is found it returns an empty $data and this causes the error:
Undefined index: table
when the RelationshipPluginBase (line 179) is called resulting in 'null' being returned as a dependency.
So in our case we find that when looking for the module that implements the table (mycustom_schema_line_item_promo) we are returned 'null' since it is not found.
The other tables and more importantly the fields they display in the view results are fine.
So we need to investigate the implementation of the hook_views_data in the custom module.
Declare which tables you own
What we find is that the tables (mycustom_schema_line_item and mycustom_schema_promo) whose fields are in the view (and correctly return the module as the dependency) are defined in the module; as are their relationship with the join table. But, the join table itself is not declared in the hook_views_data() and therefore views can't figure out which module it is dependent on.
The following extracts from this hook implementation show how they are declared:
For brevity I have omitted the declarations of all of the fields for the tables (and other custom tables).
// The line item custom table $data['mycustom_schema_line_item']['table']['group'] = t('Custom Line Item'); $data['mycustom_schema_line_item']['table']['base'] = [ 'title' => t('Custom Line Items'), 'help' => t('Custom Line Items provided by the Custom module.'), ]; // ID. $data['mycustom_schema_line_item']['id'] = [ 'title' => t('Line Item ID'), 'help' => t('Line item unique identifier.'), 'field' => [ 'id' => 'numeric', ], 'sort' => [ 'id' => 'standard', ], 'filter' => [ 'id' => 'numeric', ], ]; // The field to link in the promo. $data['mycustom_schema_line_item']['promo'] = [ 'title' => t('Promotion'), 'help' => t('The names of the promotions applied to this order.'), 'relationship' => [ 'base' => 'mycustom_schema_line_item_promo', 'base field' => 'line_item_id', 'field' => 'id', 'id' => 'standard', 'label' => t('Promotion'), ], ]; $data['mycustom_schema_promo']['table']['group'] = t('Custom Promotion'); $data['mycustom_schema_promo']['name'] = [ 'title' => t('Promotion'), 'help' => t('The name of the promotion reference.'), 'field' => [ 'id' => 'standard', ], 'sort' => [ 'id' => 'standard', ], 'filter' => [ 'id' => 'string', ], ]; // the join details to link the join table to the promo table $data['mycustom_schema_promo']['table']['join'] = [ 'mycustom_line_item_promo' => [ 'left_field' => 'promo_id', 'field' => 'id', ], ]; return $data;
So although the relationships are defined here (interestingly in two different ways - but let's not get distracted) there is no definition for the link table (mycustom_line_item_promo) itself.
So when Views calculates the dependencies, it can find the details for the Line item table (mycustom_schema_line_item) and Promotions table (mycustom_schema_promo) and their fields but not for the join table (mycustom_schema_line_item_promo) so can't determine which module it is dependent upon.
We need to add some metadata for that table to the hook_views_data().
.... // in our case all we need to add some table meta data so views can see we 'own' the table $data['mycustom_schema_line_item_promo']['table']['group'] = t('Custom Promotion'); $data['mycustom_schema_line_item_promo']['table']['base'] = [ 'title' => t('Custom Line Items - Link to promotions'), 'help' => t('Custom Line Item promotions provided by the Custom module.'), ]; // why not the fields too? Maybe because we don't want them to be included in views results? $data['mycustom_line_item_promo']['id'] = [ 'title' => t('Line Item Promo ID'), 'help' => t('Line item promo unique identifier.'), 'field' => [ 'id' => 'numeric', ], 'sort' => [ 'id' => 'standard', ], 'filter' => [ 'id' => 'numeric', ], ];
Once this is added and caches are flushed (the dependency data are cached in the ViewsData getData() so this is important) the view can be saved without error, the config no longer contains a null dependency (it already had the dependency for our module obtained from the other tables that were declared) and we can import the config.
Hopefully this will point others in the right direction if they face the same issue. Of course it might not be Views at the heart of your issue - if you have faced this issue let me know in the comments.
Contact Details
Blue-Bag Ltd
- info [at] blue-bag.com
- Telephone: 0843 2894522
- Blue-Bag HQ:
The Garage, Manor Farm
Chilcompton, Radstock
Somerset, BA3 4HP, United Kingdom - Telephone: (+44) 01761 411542
- Blue-Bag Brighton:
Unit 35 Level 6 North, New England House
New England Street, Brighton
BN1 4GH United Kingdom - Telephone: (+44) 07944 938204
- VAT GB 748125034
- UK Company Reg: 3932829