You are here

class ViewsConfigUpdater in Drupal 8

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

Provides a BC layer for modules providing old configurations.

@internal This class is only meant to fix outdated views configuration and its methods should not be invoked directly. It will be removed once all the public methods have been deprecated and removed.

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 23

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;

  /**
   * An array of helper data for the multivalue base field update.
   *
   * @var array
   */
  protected $multivalueBaseFieldsUpdateTableInfo;

  /**
   * 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.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, TypedConfigManagerInterface $typed_config_manager, ViewsData $views_data) {
    $this->entityTypeManager = $entity_type_manager;
    $this->entityFieldManager = $entity_field_manager;
    $this->typedConfigManager = $typed_config_manager;
    $this->viewsData = $views_data;
  }

  /**
   * {@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'));
  }

  /**
   * 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
        ->processEntityLinkUrlHandler($handler, $handler_type)) {
        $changed = TRUE;
      }
      if ($this
        ->processOperatorDefaultsHandler($handler, $handler_type)) {
        $changed = TRUE;
      }
      if ($this
        ->processMultivalueBaseFieldHandler($handler, $handler_type, $key, $display_id, $view)) {
        $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',
      'argument',
      'sort',
      'relationship',
      'filter',
    ];
    foreach ($displays as $display_id => &$display) {
      foreach ($handler_types as $handler_type) {
        $handler_type_plural = $handler_type . 's';
        if (!empty($display['display_options'][$handler_type_plural])) {
          foreach ($display['display_options'][$handler_type_plural] as $key => &$handler) {
            if ($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 additional settings to the entity link field.
   *
   * @param \Drupal\views\ViewEntityInterface $view
   *   The View to update.
   *
   * @return bool
   *   Whether the view was updated.
   */
  public function needsEntityLinkUrlUpdate(ViewEntityInterface $view) {
    return $this
      ->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) {
      return $this
        ->processEntityLinkUrlHandler($handler, $handler_type);
    });
  }

  /**
   * Processes entity link URL fields.
   *
   * @param array $handler
   *   A display handler.
   * @param string $handler_type
   *   The handler type.
   *
   * @return bool
   *   Whether the handler was updated.
   */
  protected function processEntityLinkUrlHandler(array &$handler, $handler_type) {
    $changed = FALSE;
    if ($handler_type === 'field') {
      if (isset($handler['plugin_id']) && $handler['plugin_id'] === 'entity_link') {

        // Add any missing settings for entity_link.
        if (!isset($handler['output_url_as_text'])) {
          $handler['output_url_as_text'] = FALSE;
          $changed = TRUE;
        }
        if (!isset($handler['absolute'])) {
          $handler['absolute'] = FALSE;
          $changed = TRUE;
        }
      }
      elseif (isset($handler['plugin_id']) && $handler['plugin_id'] === 'node_path') {

        // Convert the use of node_path to entity_link.
        $handler['plugin_id'] = 'entity_link';
        $handler['field'] = 'view_node';
        $handler['output_url_as_text'] = TRUE;
        $changed = TRUE;
      }
    }
    return $changed;
  }

  /**
   * Add additional settings to the entity link field.
   *
   * @param \Drupal\views\ViewEntityInterface $view
   *   The View to update.
   *
   * @return bool
   *   Whether the view was updated.
   */
  public function needsOperatorDefaultsUpdate(ViewEntityInterface $view) {
    return $this
      ->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) {
      return $this
        ->processOperatorDefaultsHandler($handler, $handler_type);
    });
  }

  /**
   * Processes operator defaults.
   *
   * @param array $handler
   *   A display handler.
   * @param string $handler_type
   *   The handler type.
   *
   * @return bool
   *   Whether the handler was updated.
   */
  protected function processOperatorDefaultsHandler(array &$handler, $handler_type) {
    $changed = FALSE;
    if ($handler_type === 'filter') {
      if (!isset($handler['expose']['operator_limit_selection'])) {
        $handler['expose']['operator_limit_selection'] = FALSE;
        $changed = TRUE;
      }
      if (!isset($handler['expose']['operator_list'])) {
        $handler['expose']['operator_list'] = [];
        $changed = TRUE;
      }
    }
    return $changed;
  }

  /**
   * Update field names for multi-value base fields.
   *
   * @param \Drupal\views\ViewEntityInterface $view
   *   The View to update.
   *
   * @return bool
   *   Whether the view was updated.
   */
  public function needsMultivalueBaseFieldUpdate(ViewEntityInterface $view) {
    if ($this
      ->getMultivalueBaseFieldUpdateTableInfo()) {
      return $this
        ->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type, $key, $display_id) use ($view) {
        return $this
          ->processMultivalueBaseFieldHandler($handler, $handler_type, $key, $display_id, $view);
      });
    }
    return FALSE;
  }

  /**
   * Returns the multivalue base fields update table info.
   *
   * @return array
   *   An array of multivalue base field info.
   */
  protected function getMultivalueBaseFieldUpdateTableInfo() {
    $table_info =& $this->multivalueBaseFieldsUpdateTableInfo;
    if (!isset($table_info)) {
      $table_info = [];
      foreach ($this->entityTypeManager
        ->getDefinitions() as $entity_type_id => $entity_type) {
        if ($entity_type
          ->hasHandlerClass('views_data') && $entity_type
          ->entityClassImplements(FieldableEntityInterface::class)) {
          $base_field_definitions = $this->entityFieldManager
            ->getBaseFieldDefinitions($entity_type_id);
          $entity_storage = $this->entityTypeManager
            ->getStorage($entity_type_id);
          $table_mapping = $entity_storage
            ->getTableMapping($base_field_definitions);
          if (!$table_mapping instanceof DefaultTableMapping) {
            continue;
          }
          foreach ($base_field_definitions as $field_name => $base_field_definition) {
            $base_field_storage_definition = $base_field_definition
              ->getFieldStorageDefinition();

            // Skip single value and custom storage base fields.
            if (!$base_field_storage_definition
              ->isMultiple() || $base_field_storage_definition
              ->hasCustomStorage()) {
              continue;
            }

            // Get the actual table, as well as the column for the main property
            // name, so we can perform an update on the views in
            // ::updateFieldNamesForMultivalueBaseFields().
            $table_name = $table_mapping
              ->getFieldTableName($field_name);
            $main_property_name = $base_field_storage_definition
              ->getMainPropertyName();
            $table_info[$table_name][$field_name] = $table_mapping
              ->getFieldColumnName($base_field_storage_definition, $main_property_name);
          }
        }
      }
    }
    return $table_info;
  }

  /**
   * Processes handlers affected by the multivalue base field update.
   *
   * @param array $handler
   *   A display handler.
   * @param string $handler_type
   *   The handler type.
   * @param string $key
   *   The handler key.
   * @param string $display_id
   *   The handler display ID.
   * @param \Drupal\views\ViewEntityInterface $view
   *   The view being updated.
   *
   * @return bool
   *   Whether the handler was updated.
   */
  protected function processMultivalueBaseFieldHandler(array &$handler, $handler_type, $key, $display_id, ViewEntityInterface $view) {
    $changed = FALSE;

    // If there are no multivalue base fields we have nothing to do.
    $table_info = $this
      ->getMultivalueBaseFieldUpdateTableInfo();
    if (!$table_info) {
      return $changed;
    }

    // Only if the wrong field name is set do we process the field. It
    // could already be using the correct field. Like "user__roles" vs
    // "roles_target_id".
    if (isset($handler['table']) && isset($table_info[$handler['table']]) && isset($table_info[$handler['table']][$handler['field']])) {
      $changed = TRUE;
      $original_field_name = $handler['field'];
      $handler['field'] = $table_info[$handler['table']][$original_field_name];
      $handler['plugin_id'] = $this->viewsData
        ->get($handler['table'])[$table_info[$handler['table']][$original_field_name]][$handler_type]['id'];

      // Retrieve type data information about the handler to clean it up
      // reliably. We need to manually create a typed view rather than
      // instantiating the current one, as the schema will be affected by the
      // updated values.
      $id = 'views.view.' . $view
        ->id();
      $path_to_handler = "display.{$display_id}.display_options.{$handler_type}s.{$key}";
      $view_config = $view
        ->toArray();
      $keys = explode('.', $path_to_handler);
      NestedArray::setValue($view_config, $keys, $handler);

      /** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_view */
      $typed_view = $this->typedConfigManager
        ->createFromNameAndData($id, $view_config);

      /** @var \Drupal\Core\Config\Schema\ArrayElement $typed_handler */
      $typed_handler = $typed_view
        ->get($path_to_handler);

      // Filter values we want to convert from a string to an array.
      if ($handler_type === 'filter' && $typed_handler
        ->get('value') instanceof ArrayElement && is_string($handler['value'])) {

        // An empty string cast to an array is an array with one element.
        if ($handler['value'] === '') {
          $handler['value'] = [];
        }
        else {
          $handler['value'] = (array) $handler['value'];
        }
        $handler['operator'] = $this
          ->mapOperatorFromSingleToMultiple($handler['operator']);
      }

      // For all the other fields we try to determine the fields using config
      // schema and remove everything not being defined in the new handler.
      foreach (array_keys($handler) as $handler_key) {
        if (!isset($typed_handler
          ->getDataDefinition()['mapping'][$handler_key])) {
          unset($handler[$handler_key]);
        }
      }
    }
    return $changed;
  }

  /**
   * Maps a single operator to a multiple one, if possible.
   *
   * @param string $single_operator
   *   A single operator.
   *
   * @return string
   *   A multiple operator or the original one if no mapping was available.
   */
  protected function mapOperatorFromSingleToMultiple($single_operator) {
    switch ($single_operator) {
      case '=':
        return 'or';
      case '!=':
        return 'not';
      default:
        return $single_operator;
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ViewsConfigUpdater::$entityFieldManager protected property The entity field manager.
ViewsConfigUpdater::$entityTypeManager protected property The entity type manager.
ViewsConfigUpdater::$multivalueBaseFieldsUpdateTableInfo protected property An array of helper data for the multivalue base field update.
ViewsConfigUpdater::$typedConfigManager protected property The typed config manager.
ViewsConfigUpdater::$viewsData protected property The views data service.
ViewsConfigUpdater::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create
ViewsConfigUpdater::getMultivalueBaseFieldUpdateTableInfo protected function Returns the multivalue base fields update table info.
ViewsConfigUpdater::mapOperatorFromSingleToMultiple protected function Maps a single operator to a multiple one, if possible.
ViewsConfigUpdater::needsEntityLinkUrlUpdate public function Add additional settings to the entity link field.
ViewsConfigUpdater::needsMultivalueBaseFieldUpdate public function Update field names for multi-value base fields.
ViewsConfigUpdater::needsOperatorDefaultsUpdate public function Add additional settings to the entity link field.
ViewsConfigUpdater::processDisplayHandlers protected function Processes all display handlers.
ViewsConfigUpdater::processEntityLinkUrlHandler protected function Processes entity link URL fields.
ViewsConfigUpdater::processMultivalueBaseFieldHandler protected function Processes handlers affected by the multivalue base field update.
ViewsConfigUpdater::processOperatorDefaultsHandler protected function Processes operator defaults.
ViewsConfigUpdater::updateAll public function Performs all required updates.
ViewsConfigUpdater::__construct public function ViewsConfigUpdater constructor.