class ViewsConfigUpdater

Same name and namespace in other branches
  1. 9 core/modules/views/src/ViewsConfigUpdater.php \Drupal\views\ViewsConfigUpdater
  2. 8.9.x core/modules/views/src/ViewsConfigUpdater.php \Drupal\views\ViewsConfigUpdater
  3. 11.x core/modules/views/src/ViewsConfigUpdater.php \Drupal\views\ViewsConfigUpdater

Provides a BC layer for modules providing old configurations.

@internal

Hierarchy

Expanded class hierarchy of ViewsConfigUpdater

3 files declare their use of ViewsConfigUpdater
views.module in core/modules/views/views.module
Primarily Drupal hooks and global API functions to manipulate views.
views.post_update.php in core/modules/views/views.post_update.php
Post update functions for Views.
ViewsConfigUpdaterTest.php in core/modules/views/tests/src/Kernel/ViewsConfigUpdaterTest.php

File

core/modules/views/src/ViewsConfigUpdater.php, line 20

Namespace

Drupal\views
View source
class ViewsConfigUpdater implements ContainerInjectionInterface {
    
    /**
     * The entity type manager.
     *
     * @var \Drupal\Core\Entity\EntityTypeManagerInterface
     */
    protected $entityTypeManager;
    
    /**
     * The entity field manager.
     *
     * @var \Drupal\Core\Entity\EntityFieldManagerInterface
     */
    protected $entityFieldManager;
    
    /**
     * The typed config manager.
     *
     * @var \Drupal\Core\Config\TypedConfigManagerInterface
     */
    protected $typedConfigManager;
    
    /**
     * The views data service.
     *
     * @var \Drupal\views\ViewsData
     */
    protected $viewsData;
    
    /**
     * The formatter plugin manager service.
     *
     * @var \Drupal\Component\Plugin\PluginManagerInterface
     */
    protected $formatterPluginManager;
    
    /**
     * An array of helper data for the multivalue base field update.
     *
     * @var array
     */
    protected $multivalueBaseFieldsUpdateTableInfo;
    
    /**
     * Flag determining whether deprecations should be triggered.
     *
     * @var bool
     */
    protected $deprecationsEnabled = TRUE;
    
    /**
     * Stores which deprecations were triggered.
     *
     * @var bool
     */
    protected $triggeredDeprecations = [];
    
    /**
     * ViewsConfigUpdater constructor.
     *
     * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
     *   The entity type manager.
     * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
     *   The entity field manager.
     * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
     *   The typed config manager.
     * @param \Drupal\views\ViewsData $views_data
     *   The views data service.
     * @param \Drupal\Component\Plugin\PluginManagerInterface $formatter_plugin_manager
     *   The formatter plugin manager service.
     */
    public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, TypedConfigManagerInterface $typed_config_manager, ViewsData $views_data, PluginManagerInterface $formatter_plugin_manager) {
        $this->entityTypeManager = $entity_type_manager;
        $this->entityFieldManager = $entity_field_manager;
        $this->typedConfigManager = $typed_config_manager;
        $this->viewsData = $views_data;
        $this->formatterPluginManager = $formatter_plugin_manager;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function create(ContainerInterface $container) {
        return new static($container->get('entity_type.manager'), $container->get('entity_field.manager'), $container->get('config.typed'), $container->get('views.views_data'), $container->get('plugin.manager.field.formatter'));
    }
    
    /**
     * Sets the deprecations enabling status.
     *
     * @param bool $enabled
     *   Whether deprecations should be enabled.
     */
    public function setDeprecationsEnabled($enabled) {
        $this->deprecationsEnabled = $enabled;
    }
    
    /**
     * Performs all required updates.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View to update.
     *
     * @return bool
     *   Whether the view was updated.
     */
    public function updateAll(ViewEntityInterface $view) {
        return $this->processDisplayHandlers($view, FALSE, function (&$handler, $handler_type, $key, $display_id) use ($view) {
            $changed = FALSE;
            if ($this->processResponsiveImageLazyLoadFieldHandler($handler, $handler_type, $view)) {
                $changed = TRUE;
            }
            if ($this->processTimestampFormatterTimeDiffUpdateHandler($handler, $handler_type)) {
                $changed = TRUE;
            }
            if ($this->processRevisionFieldHyphenFix($view)) {
                $changed = TRUE;
            }
            if ($this->processDefaultArgumentSkipUrlUpdate($handler, $handler_type)) {
                $changed = TRUE;
            }
            if ($this->processDefaultPagerHeadingUpdate($handler, $handler_type)) {
                $changed = TRUE;
            }
            if ($this->addLabelIfMissing($view)) {
                $changed = TRUE;
            }
            return $changed;
        });
    }
    
    /**
     * Adds a label to views which don't have one.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The view to update.
     *
     * @return bool
     *   Whether the view was updated.
     */
    public function addLabelIfMissing(ViewEntityInterface $view) : bool {
        if (!$view->get('label')) {
            $view->set('label', $view->id());
            return TRUE;
        }
        return FALSE;
    }
    
    /**
     * Add lazy load options to all responsive_image type field configurations.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View to update.
     *
     * @return bool
     *   Whether the view was updated.
     */
    public function needsResponsiveImageLazyLoadFieldUpdate(ViewEntityInterface $view) : bool {
        return $this->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
            return $this->processResponsiveImageLazyLoadFieldHandler($handler, $handler_type, $view);
        });
    }
    
    /**
     * Processes responsive_image type fields.
     *
     * @param array $handler
     *   A display handler.
     * @param string $handler_type
     *   The handler type.
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View being updated.
     *
     * @return bool
     *   Whether the handler was updated.
     */
    protected function processResponsiveImageLazyLoadFieldHandler(array &$handler, string $handler_type, ViewEntityInterface $view) : bool {
        $changed = FALSE;
        // Add any missing settings for lazy loading.
        if ($handler_type === 'field' && isset($handler['plugin_id'], $handler['type']) && $handler['plugin_id'] === 'field' && $handler['type'] === 'responsive_image' && !isset($handler['settings']['image_loading'])) {
            $handler['settings']['image_loading'] = [
                'attribute' => 'eager',
            ];
            $changed = TRUE;
        }
        return $changed;
    }
    
    /**
     * Processes all display handlers.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View to update.
     * @param bool $return_on_changed
     *   Whether processing should stop after a change is detected.
     * @param callable $handler_processor
     *   A callback performing the actual update.
     *
     * @return bool
     *   Whether the view was updated.
     */
    protected function processDisplayHandlers(ViewEntityInterface $view, $return_on_changed, callable $handler_processor) {
        $changed = FALSE;
        $displays = $view->get('display');
        $handler_types = [
            'field' => 'fields',
            'argument' => 'arguments',
            'sort' => 'sorts',
            'relationship' => 'relationships',
            'filter' => 'filters',
            'pager' => 'pager',
        ];
        $compound_display_handlers = [
            'pager',
        ];
        foreach ($displays as $display_id => &$display) {
            foreach ($handler_types as $handler_type => $handler_type_lookup) {
                if (!empty($display['display_options'][$handler_type_lookup])) {
                    if (in_array($handler_type_lookup, $compound_display_handlers)) {
                        if ($handler_processor($display['display_options'][$handler_type_lookup], $handler_type, NULL, $display_id)) {
                            $changed = TRUE;
                            if ($return_on_changed) {
                                return $changed;
                            }
                        }
                        continue;
                    }
                    foreach ($display['display_options'][$handler_type_lookup] as $key => &$handler) {
                        if (is_array($handler) && $handler_processor($handler, $handler_type, $key, $display_id)) {
                            $changed = TRUE;
                            if ($return_on_changed) {
                                return $changed;
                            }
                        }
                    }
                }
            }
        }
        if ($changed) {
            $view->set('display', $displays);
        }
        return $changed;
    }
    
    /**
     * Add eager load option to all oembed type field configurations.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View to update.
     *
     * @return bool
     *   Whether the view was updated.
     */
    public function needsOembedEagerLoadFieldUpdate(ViewEntityInterface $view) {
        return $this->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
            return $this->processOembedEagerLoadFieldHandler($handler, $handler_type, $view);
        });
    }
    
    /**
     * Processes oembed type fields.
     *
     * @param array $handler
     *   A display handler.
     * @param string $handler_type
     *   The handler type.
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View being updated.
     *
     * @return bool
     *   Whether the handler was updated.
     */
    protected function processOembedEagerLoadFieldHandler(array &$handler, string $handler_type, ViewEntityInterface $view) : bool {
        $changed = FALSE;
        // Add any missing settings for lazy loading.
        if ($handler_type === 'field' && isset($handler['plugin_id'], $handler['type']) && $handler['plugin_id'] === 'field' && $handler['type'] === 'oembed' && !array_key_exists('loading', $handler['settings'])) {
            $handler['settings']['loading'] = [
                'attribute' => 'eager',
            ];
            $changed = TRUE;
        }
        $deprecations_triggered =& $this->triggeredDeprecations['3212351'][$view->id()];
        if ($this->deprecationsEnabled && $changed && !$deprecations_triggered) {
            $deprecations_triggered = TRUE;
            @trigger_error(sprintf('The oEmbed loading attribute update for view "%s" is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Profile, module and theme provided configuration should be updated. See https://www.drupal.org/node/3275103', $view->id()), E_USER_DEPRECATED);
        }
        return $changed;
    }
    
    /**
     * Updates the timestamp fields settings by adding time diff and tooltip.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View to update.
     *
     * @return bool
     *   Whether the view was updated.
     */
    public function needsTimestampFormatterTimeDiffUpdate(ViewEntityInterface $view) : bool {
        return $this->processDisplayHandlers($view, TRUE, function (array &$handler, string $handler_type) : bool {
            return $this->processTimestampFormatterTimeDiffUpdateHandler($handler, $handler_type);
        });
    }
    
    /**
     * Processes timestamp fields settings by adding time diff and tooltip.
     *
     * @param array $handler
     *   A display handler.
     * @param string $handler_type
     *   The handler type.
     *
     * @return bool
     *   Whether the handler was updated.
     */
    protected function processTimestampFormatterTimeDiffUpdateHandler(array &$handler, string $handler_type) : bool {
        if ($handler_type === 'field' && isset($handler['type'])) {
            $plugin_definition = $this->formatterPluginManager
                ->getDefinition($handler['type'], FALSE);
            // Check also potential plugins extending TimestampFormatter.
            if (!$plugin_definition || !is_a($plugin_definition['class'], TimestampFormatter::class, TRUE)) {
                return FALSE;
            }
            if (!isset($handler['settings']['tooltip']) || !isset($handler['settings']['time_diff'])) {
                $handler['settings'] += $plugin_definition['class']::defaultSettings();
                // Existing timestamp formatters don't have tooltip.
                $handler['settings']['tooltip'] = [
                    'date_format' => '',
                    'custom_date_format' => '',
                ];
                return TRUE;
            }
        }
        return FALSE;
    }
    
    /**
     * Replaces hyphen on historical data (revision) fields.
     *
     * This replaces hyphens with double underscores in twig assertions.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The view entity.
     *
     * @return bool
     *   Whether the handler was updated.
     *
     * @see https://www.drupal.org/project/drupal/issues/2831233
     */
    public function processRevisionFieldHyphenFix(ViewEntityInterface $view) : bool {
        // Regex to search only for token with machine name '-revision_id'.
        $old_part = '/{{([^}]+)(-revision_id)/';
        $new_part = '{{$1__revision_id';
        $old_field = '-revision_id';
        $new_field = '__revision_id';
        
        /** @var \Drupal\views\ViewEntityInterface $view */
        $is_update = FALSE;
        $displays = $view->get('display');
        foreach ($displays as &$display) {
            if (isset($display['display_options']['fields'])) {
                foreach ($display['display_options']['fields'] as $field_name => $field) {
                    if (!empty($field['alter']['text'])) {
                        // Fixes replacement token references in rewritten fields.
                        $alter_text = $field['alter']['text'];
                        if (preg_match($old_part, $alter_text) === 1) {
                            $is_update = TRUE;
                            $field['alter']['text'] = preg_replace($old_part, $new_part, $alter_text);
                        }
                    }
                    if (!empty($field['alter']['path'])) {
                        // Fixes replacement token references in link paths.
                        $alter_path = $field['alter']['path'];
                        if (preg_match($old_part, $alter_path) === 1) {
                            $is_update = TRUE;
                            $field['alter']['path'] = preg_replace($old_part, $new_part, $alter_path);
                        }
                    }
                    if (str_contains($field_name, $old_field)) {
                        // Replaces the field name and the view id.
                        $is_update = TRUE;
                        $field['id'] = str_replace($old_field, $new_field, $field['id']);
                        $field['field'] = str_replace($old_field, $new_field, $field['field']);
                        // Replace key with save order.
                        $field_name_update = str_replace($old_field, $new_field, $field_name);
                        $fields = $display['display_options']['fields'];
                        $keys = array_keys($fields);
                        $keys[array_search($field_name, $keys)] = $field_name_update;
                        $display['display_options']['fields'] = array_combine($keys, $fields);
                        $display['display_options']['fields'][$field_name_update] = $field;
                    }
                }
            }
        }
        if ($is_update) {
            $view->set('display', $displays);
        }
        return $is_update;
    }
    
    /**
     * Checks each display in a view to see if it needs the hyphen fix.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The view entity.
     *
     * @return bool
     *   TRUE if the view has any displays that needed to be updated.
     */
    public function needsRevisionFieldHyphenFix(ViewEntityInterface $view) : bool {
        return $this->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
            return $this->processRevisionFieldHyphenFix($view);
        });
    }
    
    /**
     * Checks for each view if default_argument_skip_url needs to be removed.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The view entity.
     *
     * @return bool
     *   TRUE if the view has any arguments that need to have
     *   default_argument_skip_url removed.
     */
    public function needsDefaultArgumentSkipUrlUpdate(ViewEntityInterface $view) {
        return $this->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) {
            return $this->processDefaultArgumentSkipUrlUpdate($handler, $handler_type);
        });
    }
    
    /**
     * Processes arguments and removes the default_argument_skip_url setting.
     *
     * @param array $handler
     *   A display handler.
     * @param string $handler_type
     *   The handler type.
     *
     * @return bool
     *   Whether the handler was updated.
     */
    public function processDefaultArgumentSkipUrlUpdate(array &$handler, string $handler_type) : bool {
        if ($handler_type === 'argument' && isset($handler['default_argument_skip_url'])) {
            unset($handler['default_argument_skip_url']);
            return TRUE;
        }
        return FALSE;
    }
    
    /**
     * Removes user context from all views using term filter configurations.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View to update.
     *
     * @return bool
     *   Whether the view was updated.
     */
    public function needsTaxonomyTermFilterUpdate(ViewEntityInterface $view) : bool {
        return $this->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
            return $this->processTaxonomyTermFilterHandler($handler, $handler_type, $view);
        });
    }
    
    /**
     * Processes taxonomy_index_tid type filters.
     *
     * @param array $handler
     *   A display handler.
     * @param string $handler_type
     *   The handler type.
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View being updated.
     *
     * @return bool
     *   Whether the handler was updated.
     */
    protected function processTaxonomyTermFilterHandler(array &$handler, string $handler_type, ViewEntityInterface $view) : bool {
        $changed = FALSE;
        // Force view resave if using taxonomy id filter.
        $plugin_id = $handler['plugin_id'] ?? '';
        if ($handler_type === 'filter' && $plugin_id === 'taxonomy_index_tid') {
            // This cannot be done in View::preSave() due to trusted data.
            $executable = $view->getExecutable();
            $displays = $view->get('display');
            foreach ($displays as $display_id => &$display) {
                $executable->setDisplay($display_id);
                $cache_metadata = $executable->getDisplay()
                    ->calculateCacheMetadata();
                $display['cache_metadata']['contexts'] = $cache_metadata->getCacheContexts();
                // Always include at least the 'languages:' context as there will most
                // probably be translatable strings in the view output.
                $display['cache_metadata']['contexts'] = Cache::mergeContexts($display['cache_metadata']['contexts'], [
                    'languages:' . LanguageInterface::TYPE_INTERFACE,
                ]);
                sort($display['cache_metadata']['contexts']);
            }
            $view->set('display', $displays);
            $changed = TRUE;
        }
        return $changed;
    }
    
    /**
     * Checks for each view if pagination_heading_level needs to be added.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The view entity.
     *
     * @return bool
     *   TRUE if the view has any displays that need to have
     *   pagination_heading_level added.
     */
    public function needsPagerHeadingUpdate(ViewEntityInterface $view) : bool {
        return $this->processDisplayHandlers($view, FALSE, function (&$handler, $handler_type) {
            return $this->processDefaultPagerHeadingUpdate($handler, $handler_type);
        });
    }
    
    /**
     * Processes displays and adds pagination_heading_level if necessary.
     *
     * @param $compound_handler
     *   A compound display handler.
     * @param string $handler_type
     *   The handler type.
     *
     * @return bool
     *   Whether the handler was updated.
     */
    public function processDefaultPagerHeadingUpdate(array &$compound_handler, string $handler_type) : bool {
        $allow_pager_type_update = [
            'mini',
            'full',
        ];
        if ($handler_type === 'pager' && in_array($compound_handler['type'], $allow_pager_type_update) && !isset($compound_handler['options']['pagination_heading_level'])) {
            $compound_handler['options']['pagination_heading_level'] = 'h4';
            return TRUE;
        }
        return FALSE;
    }
    
    /**
     * Checks for entity view display cache tags from rendered entity fields.
     *
     * @param \Drupal\views\ViewEntityInterface $view
     *   The View to update.
     *
     * @return bool
     *   TRUE if view has rendered_entity fields.
     */
    public function needsRenderedEntityFieldUpdate(ViewEntityInterface $view) : bool {
        return $this->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) {
            return $this->processRenderedEntityFieldHandler($handler, $handler_type);
        });
    }
    
    /**
     * Processes rendered_entity type fields.
     *
     * @param array $handler
     *   A display handler.
     * @param string $handler_type
     *   The handler type.
     *
     * @return bool
     *   Whether the handler was updated.
     */
    protected function processRenderedEntityFieldHandler(array &$handler, string $handler_type) : bool {
        // Force view re-save if using rendered entity field.
        $plugin_id = $handler['plugin_id'] ?? '';
        return $handler_type === 'field' && $plugin_id === 'rendered_entity';
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
ViewsConfigUpdater::$deprecationsEnabled protected property Flag determining whether deprecations should be triggered.
ViewsConfigUpdater::$entityFieldManager protected property The entity field manager.
ViewsConfigUpdater::$entityTypeManager protected property The entity type manager.
ViewsConfigUpdater::$formatterPluginManager protected property The formatter plugin manager service.
ViewsConfigUpdater::$multivalueBaseFieldsUpdateTableInfo protected property An array of helper data for the multivalue base field update.
ViewsConfigUpdater::$triggeredDeprecations protected property Stores which deprecations were triggered.
ViewsConfigUpdater::$typedConfigManager protected property The typed config manager.
ViewsConfigUpdater::$viewsData protected property The views data service.
ViewsConfigUpdater::addLabelIfMissing public function Adds a label to views which don't have one.
ViewsConfigUpdater::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create
ViewsConfigUpdater::needsDefaultArgumentSkipUrlUpdate public function Checks for each view if default_argument_skip_url needs to be removed.
ViewsConfigUpdater::needsOembedEagerLoadFieldUpdate public function Add eager load option to all oembed type field configurations.
ViewsConfigUpdater::needsPagerHeadingUpdate public function Checks for each view if pagination_heading_level needs to be added.
ViewsConfigUpdater::needsRenderedEntityFieldUpdate public function Checks for entity view display cache tags from rendered entity fields.
ViewsConfigUpdater::needsResponsiveImageLazyLoadFieldUpdate public function Add lazy load options to all responsive_image type field configurations.
ViewsConfigUpdater::needsRevisionFieldHyphenFix public function Checks each display in a view to see if it needs the hyphen fix.
ViewsConfigUpdater::needsTaxonomyTermFilterUpdate public function Removes user context from all views using term filter configurations.
ViewsConfigUpdater::needsTimestampFormatterTimeDiffUpdate public function Updates the timestamp fields settings by adding time diff and tooltip.
ViewsConfigUpdater::processDefaultArgumentSkipUrlUpdate public function Processes arguments and removes the default_argument_skip_url setting.
ViewsConfigUpdater::processDefaultPagerHeadingUpdate public function Processes displays and adds pagination_heading_level if necessary.
ViewsConfigUpdater::processDisplayHandlers protected function Processes all display handlers.
ViewsConfigUpdater::processOembedEagerLoadFieldHandler protected function Processes oembed type fields.
ViewsConfigUpdater::processRenderedEntityFieldHandler protected function Processes rendered_entity type fields.
ViewsConfigUpdater::processResponsiveImageLazyLoadFieldHandler protected function Processes responsive_image type fields.
ViewsConfigUpdater::processRevisionFieldHyphenFix public function Replaces hyphen on historical data (revision) fields.
ViewsConfigUpdater::processTaxonomyTermFilterHandler protected function Processes taxonomy_index_tid type filters.
ViewsConfigUpdater::processTimestampFormatterTimeDiffUpdateHandler protected function Processes timestamp fields settings by adding time diff and tooltip.
ViewsConfigUpdater::setDeprecationsEnabled public function Sets the deprecations enabling status.
ViewsConfigUpdater::updateAll public function Performs all required updates.
ViewsConfigUpdater::__construct public function ViewsConfigUpdater constructor.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.