Commit f725e303 authored by catch's avatar catch

Issue #2538228 by alexpott, amateescu: Document that Config save/delete/rename...

Issue #2538228 by alexpott, amateescu: Document that Config save/delete/rename events may be dispatched during hook_update_N(), what that means for subscribers, and fix core subscribers accordingly
parent 7595aa78
......@@ -21,11 +21,16 @@ final class ConfigEvents {
* object is saved. The event listener method receives a
* \Drupal\Core\Config\ConfigCrudEvent instance.
*
* See hook_update_N() documentation for safe configuration API usage and
* restrictions as this event will be fired when configuration is saved by
* hook_update_N().
*
* @Event
*
* @see \Drupal\Core\Config\ConfigCrudEvent
* @see \Drupal\Core\Config\Config::save()
* @see \Drupal\Core\Config\ConfigFactory::onConfigSave()
* @see hook_update_N()
*
* @var string
*/
......@@ -38,11 +43,16 @@ final class ConfigEvents {
* object is deleted. The event listener method receives a
* \Drupal\Core\Config\ConfigCrudEvent instance.
*
* See hook_update_N() documentation for safe configuration API usage and
* restrictions as this event will be fired when configuration is deleted by
* hook_update_N().
*
* @Event
*
* @see \Drupal\Core\Config\ConfigCrudEvent
* @see \Drupal\Core\Config\Config::delete()
* @see \Drupal\Core\Config\ConfigFactory::onConfigDelete()
* @see hook_update_N()
*
* @var string
*/
......@@ -55,10 +65,15 @@ final class ConfigEvents {
* object's name is changed. The event listener method receives a
* \Drupal\Core\Config\ConfigRenameEvent instance.
*
* See hook_update_N() documentation for safe configuration API usage and
* restrictions as this event will be fired when configuration is renamed by
* hook_update_N().
*
* @Event
*
* @see \Drupal\Core\Config\ConfigRenameEvent
* @see \Drupal\Core\Config\ConfigFactoryInterface::rename().
* @see \Drupal\Core\Config\ConfigFactoryInterface::rename()
* @see hook_update_N()
*
* @var string
*/
......
......@@ -74,7 +74,7 @@ protected function filterOverride(Config $config, StorableConfigBase $override)
}
elseif ($changed) {
// Otherwise set the filtered override values back.
$override->setData($override_data)->save();
$override->setData($override_data)->save(TRUE);
}
}
......
......@@ -466,11 +466,30 @@ function hook_install_tasks_alter(&$tasks, $install_state) {
* In order to call a function from your mymodule.module or an include file,
* you need to explicitly load that file first.
*
* During database updates the schema of any module could be out of date. For
* this reason, caution is needed when using any API function within an update
* Implementations must ensure that APIs used are safe during updates. During
* database updates the schema of any module could be out of date. For this
* reason, caution is needed when using any API function within an update
* function - particularly CRUD functions, functions that depend on the schema
* (for example by using drupal_write_record()), and any functions that invoke
* hooks.
* (for example by using \Drupal\Core\Entity\Entity::save()), and any functions
* that invoke hooks.
*
* The following actions are examples that are safe:
* - Cache invalidation.
* - Using \Drupal::configFactory()->getEditable() and \Drupal::config().
* Implementations must:
* - Not make any assumption that the config data is valid.
* - Use the correct data type when changing configuration values as specified
* by its configuration schema at the time the update hook is written. If
* the data type changes in a subsequent code change, a subsequent update
* hook is responsible for ensuring the final data type aligns with the
* configuration schema.
* - Use the $has_trusted_data argument for \Drupal\Core\Config\Config::save()
* so that configuration schemas are not used whilst saving configuration.
* - Marking a container for rebuild.
*
* The following actions are examples that are unsafe:
* - Loading, saving, or performing any other operation on an entity.
* - Rebuilding the router using \Drupal::service('router.builder')->rebuild().
*
* The $sandbox parameter should be used when a multipass update is needed, in
* circumstances where running the whole update at once could cause PHP to
......
......@@ -9,7 +9,7 @@ services:
- [initLanguageManager]
language.config_subscriber:
class: Drupal\language\EventSubscriber\ConfigSubscriber
arguments: ['@language_manager', '@language.default']
arguments: ['@language_manager', '@language.default', '@config.factory']
tags:
- { name: event_subscriber }
language.config_factory_override:
......
......@@ -7,6 +7,8 @@
namespace Drupal\language\EventSubscriber;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageDefault;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Config\ConfigCrudEvent;
......@@ -33,6 +35,13 @@ class ConfigSubscriber implements EventSubscriberInterface {
*/
protected $languageDefault;
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Constructs a new class object.
*
......@@ -40,27 +49,55 @@ class ConfigSubscriber implements EventSubscriberInterface {
* The language manager.
* @param \Drupal\Core\Language\LanguageDefault $language_default
* The default language.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory.
*/
public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default) {
public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default, ConfigFactoryInterface $config_factory) {
$this->languageManager = $language_manager;
$this->languageDefault = $language_default;
$this->configFactory = $config_factory;
}
/**
* Causes the container to be rebuilt on the next request.
*
* This event subscriber assumes that the new default langcode and old default
* langcode are valid langcodes. If the schema definition of either
* system.site:default_langcode or language.negotiation::url.prefixes changes
* then this event must be changed to work with both the old and new schema
* definition so this event is update safe.
*
* @param ConfigCrudEvent $event
* The configuration event.
*/
public function onConfigSave(ConfigCrudEvent $event) {
$saved_config = $event->getConfig();
if ($saved_config->getName() == 'system.site' && $event->isChanged('default_langcode')) {
$language = $this->languageManager->getLanguage($saved_config->get('default_langcode'));
$new_default_langcode = $saved_config->get('default_langcode');
$default_language = $this->configFactory->get('language.entity.' . $new_default_langcode);
// During an import the language might not exist yet.
if ($language) {
$this->languageDefault->set($language);
if (!$default_language->isNew()) {
$this->languageDefault->set(new Language($default_language->get()));
$this->languageManager->reset();
language_negotiation_url_prefixes_update();
// Directly update language negotiation settings instead of calling
// language_negotiation_url_prefixes_update() to ensure that the code
// obeys the hook_update_N() restrictions.
$negotiation_config = $this->configFactory->getEditable('language.negotiation');
$negotiation_changed = FALSE;
$url_prefixes = $negotiation_config->get('url.prefixes');
$old_default_langcode = $saved_config->getOriginal('default_langcode');
if (empty($url_prefixes[$old_default_langcode])) {
$negotiation_config->set('url.prefixes.' . $old_default_langcode, $old_default_langcode);
$negotiation_changed = TRUE;
}
if (empty($url_prefixes[$new_default_langcode])) {
$negotiation_config->set('url.prefixes.' . $new_default_langcode, '');
$negotiation_changed = TRUE;
}
if ($negotiation_changed) {
$negotiation_config->save(TRUE);
}
}
// Trigger a container rebuild on the next request by invalidating it.
ConfigurableLanguageManager::rebuildServices();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment