Problem/Motivation

When my module is enabled I need some content to be created, so I'm using hook_install() to create it. As content belongs to specific entities bundles I'm adding the required config entities in /config/install.

When installing the module in a running instance all works as expected.

However when module is installed through a configuration import process - either from Config UI or drush ci or config_installer profile - the whole import process while installing the module.

The error - in my case - is "Field field_myfieldname is unknown", which is a field on one of the bundles I create instances for in my hook_install().

I can't find any similar issue in the queue, and this makes me nervous because creating-content-on-module-install looks to me like a common scenario. That let me thinks the system may work as expected and the problem here is hook_install() shouldn't be used for creating content.

Proposed resolution

If this is a real bug, investigate and fix the problem. It may be related to #2451365: ConfigInstaller has the source storage injected by config importer and module installer but it is done incorrectly, although the IS doesn't mention this error as consequence.
If developers shouldn't rely on config/install/* to be available on hook_install(), then this issue should become a Documentation task and we can update the docs about config/install (Include default configuration in your Drupal 8 module & Co.) as well as hook_install() api.

Remaining tasks

  • Confirming if this is a real issue or documentation should be updated
  • Work on a patch

User interface changes

None.

API changes

None(?)

Data model changes

None.

Issue fork drupal-2906107

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

gambry created an issue. See original summary.

gambry’s picture

Issue summary: View changes

From Drupal\Core\Config\ConfigInstaller::installDefaultConfig():

      if (!$this->isSyncing()) {
        $storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION);
        $prefix = '';
      }
      else {
        // The configuration importer sets the source storage on the config
        // installer. The configuration importer handles all of the
        // configuration entity imports. We only need to ensure that simple
        // configuration is created when the extension is installed.
        $storage = $this->getSourceStorage();
        $prefix = $name . '.';
      }

It looks like during syncing only module own simple configuration is supposed to be imported?
This make sense as configuration should already be in the sync storage, but why is not already imported?

bircher’s picture

This is not really a bug in my opinion but rather a documentation issue.

The modules own configuration needs to be installed already when the module is installed since it could be settings etc that the module can expect to always exist. The rest of the default config will be synced along with the (potentially changed) modules configuration later in the config sync process. (Modules need to be installed first so that for example plugins are available that are then in the imported config).

We could also defer the default config installation until after the hook is fired (and install first only the simple config as while syncing) then module authors would never attempt to use the default config in the install hook as it would not work there and other solutions would be used.

gambry’s picture

From IRC chat with bircher, just because there are good tips:

bircher: gambry: yes it should not import the config from config/install when installing the module as part of the config sync.. so this is totally expected
bircher: since it did install the config on the first (ui) install and then you might have changed it, as part of the config sync the configuration is installed too, just after the hook_install are fired since the modules are installed first
bircher: gambry: maybe the confusing part is that through the normal module install the hook is fired after the config/install config is added while during a sync it is the other way around.. since if it had been afterwards also for normal installs you would never have thought that you can "abuse" the hook install for that...

gambry: bircher, I haven't changed the configuration though. What was shipped with the module when first installed is now in config/sync without changes. It worked with `drush en mymodule` but now fails with `drush cim`.
bircher: gambry: yes of course, whether it changed or not is not the issue, the thing is that the module no longer owns the config it shipped with as a default. the site does (in config/sync)

bircher: gambry: the easy solution though for content is to use default_content or create config that depends on certain content specifically and then create the content in a ConfigImporterEvent::IMPORT_MISSING_CONTENT
bircher: (as far as I remember default_content also needs some tricks when syncing config)

gambry: bircher, is ConfigImporterEvent::IMPORT the right way then?
bircher: gambry: yes that would work
bircher: gambry: or use https://www.drupal.org/node/2901418

gambry’s picture

Component: configuration system » documentation
Category: Bug report » Task
Issue tags: +Needs issue summary update

So changing the issue to target a documentation update.

The first piece of doc to be reviewd is https://www.drupal.org/docs/8/creating-custom-modules/include-default-co... . It's enough to mention to not rely on configuration existing from within module's hook_install(), but rather using ConfigImporterEvent::* (or whatever the current best solution is).

Also mentioning on the hook_install() dockblock in module.api.php default config may not be available.

gambry’s picture

Closing this in favour of #2901418: Add hook_post_config_import_NAME after config import. We can polish the documentation of hook_install(), hook_update_N(), https://www.drupal.org/docs/8/creating-custom-modules/include-default-co... , etc. in there.

gambry’s picture

hchonov’s picture

Title: Modules config/install entities are not imported when module is installed through config import » hook_install is invoked before the module's default config is installed during config import
Version: 8.4.x-dev » 9.4.x-dev
Component: documentation » configuration system
Category: Task » Bug report
Status: Closed (duplicate) » Needs review
Issue tags: +consistency
FileSize
5.03 KB

Unfortunatelly the linked issues have gotten in a different direction and there is still no solution for this issue, which is why I am reopening it.

@bircher has summurized that pretty good:

maybe the confusing part is that through the normal module install the hook is fired after the config/install config is added while during a sync it is the other way around.. since if it had been afterwards also for normal installs you would never have thought that you can "abuse" the hook install for that...

However I do not agree that modules are "abusing" the hook_install, which is documented as follows:

* Perform setup tasks when the module is installed.
 *
 * If the module implements hook_schema(), the database tables will
 * be created before this hook is fired.
 *
 * If the module provides a MODULE.routing.yml or alters routing information
 * these changes will not be available when this hook is fired. If up-to-date
 * router information is required, for example to use \Drupal\Core\Url, then
 * (preferably) use hook_modules_installed() or rebuild the router in the
 * hook_install() implementation.
 *
 * Implementations of this hook are by convention declared in the module's
 * .install file. The implementation can rely on the .module file being loaded.
 * The hook will only be called when a module is installed. The module's schema
 * version will be set to the module's greatest numbered update hook. Because of
 * this, any time a hook_update_N() is added to the module, this function needs
 * to be updated to reflect the current version of the database schema.
 *
 * See the @link https://www.drupal.org/node/146843 Schema API documentation
 * @endlink for details on hook_schema and how database tables are defined.
 *
 * Note that since this function is called from a full bootstrap, all functions
 * (including those in modules enabled by the current page request) are
 * available when this hook is called. Use cases could be displaying a user
 * message, or calling a module function necessary for initial setup, etc.
 *
 * Please be sure that anything added or modified in this function that can
 * be removed during uninstall should be removed with hook_uninstall().

This documentation does not state that all of the module's dependent configuration will be present in hook_install, but it states that anything else will be made ready for the module. Why not the configuration it depends on then? I think that the config import should match the default module install behavior, otherwise any single module realying on that behavior already will be breaking a config import where that module is installed.

Of course we could also agree on the correct behavior to be that hook_install is called only after its simple configuration is installed and before its third-party provided configuration, but that would require the introduction of a new hook that is common for both cases of enabling a module -

  1. installing a module in a regular way and
  2. installing a module through a config import.

A rewrite of any module out there that that leverages the behavior of a regular module install will be required if a new hook is introduced. On the other side I think it is much more easier to simply delay the hook_install invocation until we can ensure that all the configuration a module is dependent on is available. I cannot currently think of a reason why that would be something we would not want to do. I might be missing something though.

Further we've lost the ability to react on the event that module Z and X have been installed together - e.g. hook_modules_installed will be fired for each module individually during config import instead of together so we have two calls hook_modules_installed([Z]) and hook_modules_installed([X]) during config import versus one call hook_modules_installed([Z, X]) during normal module installation. However I am not sure if this is really a concern we should be thinking about.

For sure we have quite the inconsistency now and no matter which behavior is declared the correct one both approaches of enabling an extension should have the same behavior.

I am attaching a patch proposal showing how hook_install could be delayed to match the normal module installation behavior as much as possible.

ranjith_kumar_k_u’s picture

Status: Needs review » Needs work

The last submitted patch, 9: 2906107-9.patch, failed testing. View results

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

semiaddict’s picture

I just stumbled on this issue as I am also creating content in hook_install for a custom entity type which has bundles.
The bundles are being created by module configs, which also end up in config/sync.

If the module is installed via a sync, the bundles don't exist when hook_install is called.
I managed to work around this by creating missing bundles in hook_install before creating the entities, but this seems more like a hack.

During a sync, wouldn't it make more sense to:

  1. isntall the module and all its configs
  2. override the configs with those in config/sync

This would insure that module configs have been imported before calling hook_install, and would likely result in having the production site mimic more closely what usually happens in development.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

ressa made their first commit to this issue’s fork.

ressa’s picture

Here's a re-roll on Gitlab for 11.x based on @hchonov’s patch in #8.

Wim Leers’s picture

Title: hook_install is invoked before the module's default config is installed during config import » hook_install() is invoked before the module's default config is installed during config import
Issue tags: +DX (Developer Experience)

Ran into this too yesterday in https://www.drupal.org/project/automatic_updates. We had to resort to using hook_modules_installed().

Looks like some tests still need to have their expectations updated?

DamienMcKenna’s picture

I think it'd be useful if Drupal core had an expanded deployment process built in that could be executed via update.php (or similar) rather than replying on Drush (see #2901418: Add hook_post_config_import_NAME after config import), which is only available when using Drush.