You are here

class LanguageHierarchyConfigFactoryOverride in Language Hierarchy 2.x

Same name and namespace in other branches
  1. 8 src/Config/LanguageHierarchyConfigFactoryOverride.php \Drupal\language_hierarchy\Config\LanguageHierarchyConfigFactoryOverride

Provides language overrides for the configuration factory, with fallbacks.

Hierarchy

Expanded class hierarchy of LanguageHierarchyConfigFactoryOverride

File

src/Config/LanguageHierarchyConfigFactoryOverride.php, line 11

Namespace

Drupal\language_hierarchy\Config
View source
class LanguageHierarchyConfigFactoryOverride extends LanguageConfigFactoryOverride {

  /**
   * The chain of language codes to fallback through, with most specific first.
   *
   * @var string[]|null
   */
  protected $fallbackChain;

  /**
   * {@inheritdoc}
   */
  public function loadOverrides($names) {

    // Skip to using the parent implementation if there is no fallback chain or
    // language. If the fallback chain just hasn't been computed yet, read it
    // directly from base storage to avoid a circular dependency on the
    // configuration factory.
    if ($this->language && (!isset($this->fallbackChain) || !empty($this->fallbackChain))) {
      $storage = $this
        ->getStorage($this->language
        ->getId());
      $loaded = $storage
        ->readMultiple($names);
      if ($missing = array_diff($names, array_keys($loaded))) {

        // Try fallback languages.
        if (!isset($this->fallbackChain)) {
          $this->fallbackChain = $this
            ->getFallbackChainFromBaseConfig($this->language
            ->getId());
        }
        $ancestors = $this->fallbackChain;
        while ($missing && ($ancestor = array_shift($ancestors))) {
          $fallback_storage = $this
            ->getStorage($ancestor);
          $loaded += $fallback_storage
            ->readMultiple($missing);
          $missing = array_diff($names, array_keys($loaded));
        }
      }
      return $loaded;
    }
    else {
      return parent::loadOverrides($names);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getOverride($langcode, $name) {
    $source_storage = $target_storage = $this
      ->getStorage($langcode);
    $data = $source_storage
      ->read($name);
    if (empty($data)) {

      // Try fallback languages.
      if ($ancestors = $this
        ->getFallbackChainFromConfigEntities($langcode)) {
        while (empty($data) && ($ancestor = array_shift($ancestors))) {
          $fallback_storage = $this
            ->getStorage($ancestor);
          if ($data = $fallback_storage
            ->read($name)) {
            $source_storage = $fallback_storage;
          }
        }
        $override = new LanguageHierarchyConfigOverride($name, $langcode, $source_storage, $target_storage, $this->typedConfigManager, $this->eventDispatcher);
        if (!empty($data)) {
          $override
            ->initWithData($data);
        }
        return $override;
      }
    }

    // If there is data for the specified language, or there are no fallbacks,
    // use the original implementation of ::getOverride().
    return parent::getOverride($langcode, $name);
  }

  /**
   * {@inheritdoc}
   */
  public function setLanguage(LanguageInterface $language = NULL) {
    $this->fallbackChain = $this
      ->getFallbackChainFromConfigEntities($language
      ->getId());
    return parent::setLanguage($language);
  }

  /**
   * Compute the fallback chain for a language code.
   *
   * Before the config factory is available for us to use to get proper language
   * configuration entities, the base storage and raw configuration values have
   * to be used directly to compute the fallback chain.
   */
  protected function getFallbackChainFromBaseConfig($langcode) {

    // The config prefix here has to be hardcoded because it would come from the
    // entity type definition, but the entity type manager cannot return that
    // yet as that indirectly depends on this config factory override service.
    $language_config = $this->baseStorage
      ->read('language.entity.' . $langcode);
    $fallbacks = [];
    while (!empty($language_config['third_party_settings']['language_hierarchy']['fallback_langcode'])) {
      $ancestor_langcode = $language_config['third_party_settings']['language_hierarchy']['fallback_langcode'];

      // Protect against infinite recursion due to unexpected configuration.
      if (in_array($ancestor_langcode, $fallbacks, TRUE)) {
        break;
      }
      else {
        $fallbacks[] = $ancestor_langcode;
        $language_config = $this->baseStorage
          ->read('language.entity.' . $ancestor_langcode);
      }
    }
    return $fallbacks;
  }

  /**
   * Compute the fallback chain for a language code.
   *
   * The entity type manager and language configuration entities should be
   * available by the time this is needed, as language overrides are loaded
   * during the Kernel request event. We cannot inject the entity type manager
   * as a dependency because that would create a circular dependency.
   *
   * @see \Drupal\language\EventSubscriber\LanguageRequestSubscriber::getSubscribedEvents()
   */
  protected function getFallbackChainFromConfigEntities($langcode) {

    /** @var \Drupal\language\ConfigurableLanguageInterface $language_config */
    $language_config = \Drupal::entityTypeManager()
      ->getStorage('configurable_language')
      ->load($langcode);
    if ($language_config) {
      $fallbacks = language_hierarchy_get_ancestors($language_config);
      $fallbacks = array_keys($fallbacks);
    }
    else {
      $fallbacks = [];
    }
    return $fallbacks;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ConfigFactoryOverrideBase::filterNestedArray protected function Filters data in nested arrays.
ConfigFactoryOverrideBase::filterOverride protected function Filters data in the override based on what is currently in configuration.
ConfigFactoryOverrideBase::getSubscribedEvents public static function
LanguageConfigCollectionNameTrait::createConfigCollectionName protected function Creates a configuration collection name based on a language code.
LanguageConfigCollectionNameTrait::getLangcodeFromCollectionName protected function Converts a configuration collection name to a language code.
LanguageConfigFactoryOverride::$baseStorage protected property The configuration storage.
LanguageConfigFactoryOverride::$eventDispatcher protected property An event dispatcher instance to use for configuration events.
LanguageConfigFactoryOverride::$language protected property The language object used to override configuration data.
LanguageConfigFactoryOverride::$storages protected property An array of configuration storages keyed by langcode.
LanguageConfigFactoryOverride::$typedConfigManager protected property The typed config manager.
LanguageConfigFactoryOverride::addCollections public function Reacts to the ConfigEvents::COLLECTION_INFO event. Overrides ConfigFactoryOverrideBase::addCollections
LanguageConfigFactoryOverride::createConfigObject public function Creates a configuration object for use during install and synchronization. Overrides ConfigFactoryOverrideInterface::createConfigObject
LanguageConfigFactoryOverride::getCacheableMetadata public function Gets the cacheability metadata associated with the config factory override. Overrides ConfigFactoryOverrideInterface::getCacheableMetadata
LanguageConfigFactoryOverride::getCacheSuffix public function The string to append to the configuration static cache name. Overrides ConfigFactoryOverrideInterface::getCacheSuffix
LanguageConfigFactoryOverride::getLanguage public function Gets the language object used to override configuration data. Overrides LanguageConfigFactoryOverrideInterface::getLanguage
LanguageConfigFactoryOverride::getStorage public function Returns the storage instance for a particular langcode. Overrides LanguageConfigFactoryOverrideInterface::getStorage
LanguageConfigFactoryOverride::installLanguageOverrides public function Installs available language configuration overrides for a given langcode. Overrides LanguageConfigFactoryOverrideInterface::installLanguageOverrides
LanguageConfigFactoryOverride::onConfigDelete public function Actions to be performed to configuration override on configuration delete. Overrides ConfigFactoryOverrideBase::onConfigDelete
LanguageConfigFactoryOverride::onConfigRename public function Actions to be performed to configuration override on configuration rename. Overrides ConfigFactoryOverrideBase::onConfigRename
LanguageConfigFactoryOverride::onConfigSave public function Actions to be performed to configuration override on configuration save. Overrides ConfigFactoryOverrideBase::onConfigSave
LanguageConfigFactoryOverride::__construct public function Constructs the LanguageConfigFactoryOverride object.
LanguageHierarchyConfigFactoryOverride::$fallbackChain protected property The chain of language codes to fallback through, with most specific first.
LanguageHierarchyConfigFactoryOverride::getFallbackChainFromBaseConfig protected function Compute the fallback chain for a language code.
LanguageHierarchyConfigFactoryOverride::getFallbackChainFromConfigEntities protected function Compute the fallback chain for a language code.
LanguageHierarchyConfigFactoryOverride::getOverride public function Get language override for given language and configuration name. Overrides LanguageConfigFactoryOverride::getOverride
LanguageHierarchyConfigFactoryOverride::loadOverrides public function Returns config overrides. Overrides LanguageConfigFactoryOverride::loadOverrides
LanguageHierarchyConfigFactoryOverride::setLanguage public function Sets the language to be used in configuration overrides. Overrides LanguageConfigFactoryOverride::setLanguage