annotate core/modules/views/src/Entity/View.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\views\Entity;
Chris@0 4
Chris@0 5 use Drupal\Component\Utility\NestedArray;
Chris@0 6 use Drupal\Core\Cache\Cache;
Chris@0 7 use Drupal\Core\Config\Entity\ConfigEntityBase;
Chris@0 8 use Drupal\Core\Entity\ContentEntityTypeInterface;
Chris@0 9 use Drupal\Core\Entity\EntityStorageInterface;
Chris@0 10 use Drupal\Core\Entity\FieldableEntityInterface;
Chris@0 11 use Drupal\Core\Language\LanguageInterface;
Chris@0 12 use Drupal\views\Plugin\DependentWithRemovalPluginInterface;
Chris@0 13 use Drupal\views\Views;
Chris@0 14 use Drupal\views\ViewEntityInterface;
Chris@0 15
Chris@0 16 /**
Chris@0 17 * Defines a View configuration entity class.
Chris@0 18 *
Chris@0 19 * @ConfigEntityType(
Chris@0 20 * id = "view",
Chris@0 21 * label = @Translation("View", context = "View entity type"),
Chris@17 22 * label_collection = @Translation("Views", context = "View entity type"),
Chris@17 23 * label_singular = @Translation("view", context = "View entity type"),
Chris@17 24 * label_plural = @Translation("views", context = "View entity type"),
Chris@17 25 * label_count = @PluralTranslation(
Chris@17 26 * singular = "@count view",
Chris@17 27 * plural = "@count views",
Chris@17 28 * context = "View entity type",
Chris@17 29 * ),
Chris@0 30 * admin_permission = "administer views",
Chris@0 31 * entity_keys = {
Chris@0 32 * "id" = "id",
Chris@0 33 * "label" = "label",
Chris@0 34 * "status" = "status"
Chris@0 35 * },
Chris@0 36 * config_export = {
Chris@0 37 * "id",
Chris@0 38 * "label",
Chris@0 39 * "module",
Chris@0 40 * "description",
Chris@0 41 * "tag",
Chris@0 42 * "base_table",
Chris@0 43 * "base_field",
Chris@0 44 * "core",
Chris@0 45 * "display",
Chris@0 46 * }
Chris@0 47 * )
Chris@0 48 */
Chris@0 49 class View extends ConfigEntityBase implements ViewEntityInterface {
Chris@0 50
Chris@0 51 /**
Chris@0 52 * The name of the base table this view will use.
Chris@0 53 *
Chris@0 54 * @var string
Chris@0 55 */
Chris@0 56 protected $base_table = 'node';
Chris@0 57
Chris@0 58 /**
Chris@0 59 * The unique ID of the view.
Chris@0 60 *
Chris@0 61 * @var string
Chris@0 62 */
Chris@0 63 protected $id = NULL;
Chris@0 64
Chris@0 65 /**
Chris@0 66 * The label of the view.
Chris@0 67 */
Chris@0 68 protected $label;
Chris@0 69
Chris@0 70 /**
Chris@0 71 * The description of the view, which is used only in the interface.
Chris@0 72 *
Chris@0 73 * @var string
Chris@0 74 */
Chris@0 75 protected $description = '';
Chris@0 76
Chris@0 77 /**
Chris@0 78 * The "tags" of a view.
Chris@0 79 *
Chris@0 80 * The tags are stored as a single string, though it is used as multiple tags
Chris@0 81 * for example in the views overview.
Chris@0 82 *
Chris@0 83 * @var string
Chris@0 84 */
Chris@0 85 protected $tag = '';
Chris@0 86
Chris@0 87 /**
Chris@0 88 * The core version the view was created for.
Chris@0 89 *
Chris@0 90 * @var string
Chris@0 91 */
Chris@0 92 protected $core = \Drupal::CORE_COMPATIBILITY;
Chris@0 93
Chris@0 94 /**
Chris@0 95 * Stores all display handlers of this view.
Chris@0 96 *
Chris@0 97 * An array containing Drupal\views\Plugin\views\display\DisplayPluginBase
Chris@0 98 * objects.
Chris@0 99 *
Chris@0 100 * @var array
Chris@0 101 */
Chris@0 102 protected $display = [];
Chris@0 103
Chris@0 104 /**
Chris@0 105 * The name of the base field to use.
Chris@0 106 *
Chris@0 107 * @var string
Chris@0 108 */
Chris@0 109 protected $base_field = 'nid';
Chris@0 110
Chris@0 111 /**
Chris@0 112 * Stores a reference to the executable version of this view.
Chris@0 113 *
Chris@0 114 * @var \Drupal\views\ViewExecutable
Chris@0 115 */
Chris@0 116 protected $executable;
Chris@0 117
Chris@0 118 /**
Chris@0 119 * The module implementing this view.
Chris@0 120 *
Chris@0 121 * @var string
Chris@0 122 */
Chris@0 123 protected $module = 'views';
Chris@0 124
Chris@0 125 /**
Chris@0 126 * {@inheritdoc}
Chris@0 127 */
Chris@0 128 public function getExecutable() {
Chris@0 129 // Ensure that an executable View is available.
Chris@0 130 if (!isset($this->executable)) {
Chris@0 131 $this->executable = Views::executableFactory()->get($this);
Chris@0 132 }
Chris@0 133
Chris@0 134 return $this->executable;
Chris@0 135 }
Chris@0 136
Chris@0 137 /**
Chris@0 138 * {@inheritdoc}
Chris@0 139 */
Chris@0 140 public function createDuplicate() {
Chris@0 141 $duplicate = parent::createDuplicate();
Chris@0 142 unset($duplicate->executable);
Chris@0 143 return $duplicate;
Chris@0 144 }
Chris@0 145
Chris@0 146 /**
Chris@0 147 * {@inheritdoc}
Chris@0 148 */
Chris@0 149 public function label() {
Chris@0 150 if (!$label = $this->get('label')) {
Chris@0 151 $label = $this->id();
Chris@0 152 }
Chris@0 153 return $label;
Chris@0 154 }
Chris@0 155
Chris@0 156 /**
Chris@0 157 * {@inheritdoc}
Chris@0 158 */
Chris@0 159 public function addDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
Chris@0 160 if (empty($plugin_id)) {
Chris@0 161 return FALSE;
Chris@0 162 }
Chris@0 163
Chris@0 164 $plugin = Views::pluginManager('display')->getDefinition($plugin_id);
Chris@0 165
Chris@0 166 if (empty($plugin)) {
Chris@0 167 $plugin['title'] = t('Broken');
Chris@0 168 }
Chris@0 169
Chris@0 170 if (empty($id)) {
Chris@0 171 $id = $this->generateDisplayId($plugin_id);
Chris@0 172
Chris@0 173 // Generate a unique human-readable name by inspecting the counter at the
Chris@0 174 // end of the previous display ID, e.g., 'page_1'.
Chris@0 175 if ($id !== 'default') {
Chris@0 176 preg_match("/[0-9]+/", $id, $count);
Chris@0 177 $count = $count[0];
Chris@0 178 }
Chris@0 179 else {
Chris@0 180 $count = '';
Chris@0 181 }
Chris@0 182
Chris@0 183 if (empty($title)) {
Chris@0 184 // If there is no title provided, use the plugin title, and if there are
Chris@0 185 // multiple displays, append the count.
Chris@0 186 $title = $plugin['title'];
Chris@0 187 if ($count > 1) {
Chris@0 188 $title .= ' ' . $count;
Chris@0 189 }
Chris@0 190 }
Chris@0 191 }
Chris@0 192
Chris@0 193 $display_options = [
Chris@0 194 'display_plugin' => $plugin_id,
Chris@0 195 'id' => $id,
Chris@0 196 // Cast the display title to a string since it is an object.
Chris@0 197 // @see \Drupal\Core\StringTranslation\TranslatableMarkup
Chris@0 198 'display_title' => (string) $title,
Chris@0 199 'position' => $id === 'default' ? 0 : count($this->display),
Chris@0 200 'display_options' => [],
Chris@0 201 ];
Chris@0 202
Chris@0 203 // Add the display options to the view.
Chris@0 204 $this->display[$id] = $display_options;
Chris@0 205 return $id;
Chris@0 206 }
Chris@0 207
Chris@0 208 /**
Chris@0 209 * Generates a display ID of a certain plugin type.
Chris@0 210 *
Chris@0 211 * @param string $plugin_id
Chris@0 212 * Which plugin should be used for the new display ID.
Chris@0 213 *
Chris@0 214 * @return string
Chris@0 215 */
Chris@0 216 protected function generateDisplayId($plugin_id) {
Chris@0 217 // 'default' is singular and is unique, so just go with 'default'
Chris@0 218 // for it. For all others, start counting.
Chris@0 219 if ($plugin_id == 'default') {
Chris@0 220 return 'default';
Chris@0 221 }
Chris@0 222 // Initial ID.
Chris@0 223 $id = $plugin_id . '_1';
Chris@0 224 $count = 1;
Chris@0 225
Chris@0 226 // Loop through IDs based upon our style plugin name until
Chris@0 227 // we find one that is unused.
Chris@0 228 while (!empty($this->display[$id])) {
Chris@0 229 $id = $plugin_id . '_' . ++$count;
Chris@0 230 }
Chris@0 231
Chris@0 232 return $id;
Chris@0 233 }
Chris@0 234
Chris@0 235 /**
Chris@0 236 * {@inheritdoc}
Chris@0 237 */
Chris@0 238 public function &getDisplay($display_id) {
Chris@0 239 return $this->display[$display_id];
Chris@0 240 }
Chris@0 241
Chris@0 242 /**
Chris@0 243 * {@inheritdoc}
Chris@0 244 */
Chris@0 245 public function duplicateDisplayAsType($old_display_id, $new_display_type) {
Chris@0 246 $executable = $this->getExecutable();
Chris@0 247 $display = $executable->newDisplay($new_display_type);
Chris@0 248 $new_display_id = $display->display['id'];
Chris@0 249 $displays = $this->get('display');
Chris@0 250
Chris@0 251 // Let the display title be generated by the addDisplay method and set the
Chris@0 252 // right display plugin, but keep the rest from the original display.
Chris@0 253 $display_duplicate = $displays[$old_display_id];
Chris@0 254 unset($display_duplicate['display_title']);
Chris@0 255 unset($display_duplicate['display_plugin']);
Chris@14 256 unset($display_duplicate['new_id']);
Chris@0 257
Chris@0 258 $displays[$new_display_id] = NestedArray::mergeDeep($displays[$new_display_id], $display_duplicate);
Chris@0 259 $displays[$new_display_id]['id'] = $new_display_id;
Chris@0 260
Chris@0 261 // First set the displays.
Chris@0 262 $this->set('display', $displays);
Chris@0 263
Chris@0 264 // Ensure that we just copy display options, which are provided by the new
Chris@0 265 // display plugin.
Chris@0 266 $executable->setDisplay($new_display_id);
Chris@0 267
Chris@0 268 $executable->display_handler->filterByDefinedOptions($displays[$new_display_id]['display_options']);
Chris@0 269 // Update the display settings.
Chris@0 270 $this->set('display', $displays);
Chris@0 271
Chris@0 272 return $new_display_id;
Chris@0 273 }
Chris@0 274
Chris@0 275 /**
Chris@0 276 * {@inheritdoc}
Chris@0 277 */
Chris@0 278 public function calculateDependencies() {
Chris@0 279 parent::calculateDependencies();
Chris@0 280
Chris@0 281 // Ensure that the view is dependant on the module that implements the view.
Chris@0 282 $this->addDependency('module', $this->module);
Chris@0 283
Chris@0 284 $executable = $this->getExecutable();
Chris@0 285 $executable->initDisplay();
Chris@0 286 $executable->initStyle();
Chris@0 287
Chris@0 288 foreach ($executable->displayHandlers as $display) {
Chris@0 289 // Calculate the dependencies each display has.
Chris@0 290 $this->calculatePluginDependencies($display);
Chris@0 291 }
Chris@0 292
Chris@0 293 return $this;
Chris@0 294 }
Chris@0 295
Chris@0 296 /**
Chris@0 297 * {@inheritdoc}
Chris@0 298 */
Chris@0 299 public function preSave(EntityStorageInterface $storage) {
Chris@0 300 parent::preSave($storage);
Chris@0 301
Chris@0 302 $displays = $this->get('display');
Chris@0 303
Chris@0 304 $this->fixTableNames($displays);
Chris@0 305
Chris@0 306 // Sort the displays.
Chris@0 307 ksort($displays);
Chris@0 308 $this->set('display', ['default' => $displays['default']] + $displays);
Chris@0 309
Chris@0 310 // @todo Check whether isSyncing is needed.
Chris@0 311 if (!$this->isSyncing()) {
Chris@0 312 $this->addCacheMetadata();
Chris@0 313 }
Chris@0 314 }
Chris@0 315
Chris@0 316 /**
Chris@0 317 * Fixes table names for revision metadata fields of revisionable entities.
Chris@0 318 *
Chris@0 319 * Views for revisionable entity types using revision metadata fields might
Chris@0 320 * be using the wrong table to retrieve the fields after system_update_8300
Chris@0 321 * has moved them correctly to the revision table. This method updates the
Chris@0 322 * views to use the correct tables.
Chris@0 323 *
Chris@0 324 * @param array &$displays
Chris@0 325 * An array containing display handlers of a view.
Chris@0 326 *
Chris@0 327 * @deprecated in Drupal 8.3.0, will be removed in Drupal 9.0.0.
Chris@16 328 *
Chris@16 329 * @see https://www.drupal.org/node/2831499
Chris@0 330 */
Chris@0 331 private function fixTableNames(array &$displays) {
Chris@0 332 // Fix wrong table names for entity revision metadata fields.
Chris@0 333 foreach ($displays as $display => $display_data) {
Chris@0 334 if (isset($display_data['display_options']['fields'])) {
Chris@0 335 foreach ($display_data['display_options']['fields'] as $property_name => $property_data) {
Chris@0 336 if (isset($property_data['entity_type']) && isset($property_data['field']) && isset($property_data['table'])) {
Chris@0 337 $entity_type = $this->entityTypeManager()->getDefinition($property_data['entity_type']);
Chris@0 338 // We need to update the table name only for revisionable entity
Chris@0 339 // types, otherwise the view is already using the correct table.
Chris@0 340 if (($entity_type instanceof ContentEntityTypeInterface) && is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class) && $entity_type->isRevisionable()) {
Chris@0 341 $revision_metadata_fields = $entity_type->getRevisionMetadataKeys();
Chris@0 342 // @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout()
Chris@0 343 $revision_table = $entity_type->getRevisionTable() ?: $entity_type->id() . '_revision';
Chris@0 344
Chris@0 345 // Check if this is a revision metadata field and if it uses the
Chris@0 346 // wrong table.
Chris@0 347 if (in_array($property_data['field'], $revision_metadata_fields) && $property_data['table'] != $revision_table) {
Chris@0 348 $displays[$display]['display_options']['fields'][$property_name]['table'] = $revision_table;
Chris@0 349 }
Chris@0 350 }
Chris@0 351 }
Chris@0 352 }
Chris@0 353 }
Chris@0 354 }
Chris@0 355 }
Chris@0 356
Chris@0 357 /**
Chris@0 358 * Fills in the cache metadata of this view.
Chris@0 359 *
Chris@0 360 * Cache metadata is set per view and per display, and ends up being stored in
Chris@0 361 * the view's configuration. This allows Views to determine very efficiently:
Chris@0 362 * - the max-age
Chris@0 363 * - the cache contexts
Chris@0 364 * - the cache tags
Chris@0 365 *
Chris@0 366 * In other words: this allows us to do the (expensive) work of initializing
Chris@0 367 * Views plugins and handlers to determine their effect on the cacheability of
Chris@0 368 * a view at save time rather than at runtime.
Chris@0 369 */
Chris@0 370 protected function addCacheMetadata() {
Chris@0 371 $executable = $this->getExecutable();
Chris@0 372
Chris@0 373 $current_display = $executable->current_display;
Chris@0 374 $displays = $this->get('display');
Chris@0 375 foreach (array_keys($displays) as $display_id) {
Chris@0 376 $display =& $this->getDisplay($display_id);
Chris@0 377 $executable->setDisplay($display_id);
Chris@0 378
Chris@0 379 $cache_metadata = $executable->getDisplay()->calculateCacheMetadata();
Chris@0 380 $display['cache_metadata']['max-age'] = $cache_metadata->getCacheMaxAge();
Chris@0 381 $display['cache_metadata']['contexts'] = $cache_metadata->getCacheContexts();
Chris@0 382 $display['cache_metadata']['tags'] = $cache_metadata->getCacheTags();
Chris@0 383 // Always include at least the 'languages:' context as there will most
Chris@0 384 // probably be translatable strings in the view output.
Chris@0 385 $display['cache_metadata']['contexts'] = Cache::mergeContexts($display['cache_metadata']['contexts'], ['languages:' . LanguageInterface::TYPE_INTERFACE]);
Chris@0 386 }
Chris@0 387 // Restore the previous active display.
Chris@0 388 $executable->setDisplay($current_display);
Chris@0 389 }
Chris@0 390
Chris@0 391 /**
Chris@0 392 * {@inheritdoc}
Chris@0 393 */
Chris@0 394 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
Chris@0 395 parent::postSave($storage, $update);
Chris@0 396
Chris@0 397 // @todo Remove if views implements a view_builder controller.
Chris@0 398 views_invalidate_cache();
Chris@0 399 $this->invalidateCaches();
Chris@0 400
Chris@14 401 // Rebuild the router if this is a new view, or its status changed.
Chris@0 402 if (!isset($this->original) || ($this->status() != $this->original->status())) {
Chris@0 403 \Drupal::service('router.builder')->setRebuildNeeded();
Chris@0 404 }
Chris@0 405 }
Chris@0 406
Chris@0 407 /**
Chris@0 408 * {@inheritdoc}
Chris@0 409 */
Chris@0 410 public static function postLoad(EntityStorageInterface $storage, array &$entities) {
Chris@0 411 parent::postLoad($storage, $entities);
Chris@0 412 foreach ($entities as $entity) {
Chris@0 413 $entity->mergeDefaultDisplaysOptions();
Chris@0 414 }
Chris@0 415 }
Chris@0 416
Chris@0 417 /**
Chris@0 418 * {@inheritdoc}
Chris@0 419 */
Chris@0 420 public static function preCreate(EntityStorageInterface $storage, array &$values) {
Chris@0 421 parent::preCreate($storage, $values);
Chris@0 422
Chris@0 423 // If there is no information about displays available add at least the
Chris@0 424 // default display.
Chris@0 425 $values += [
Chris@0 426 'display' => [
Chris@0 427 'default' => [
Chris@0 428 'display_plugin' => 'default',
Chris@0 429 'id' => 'default',
Chris@0 430 'display_title' => 'Master',
Chris@0 431 'position' => 0,
Chris@0 432 'display_options' => [],
Chris@0 433 ],
Chris@17 434 ],
Chris@0 435 ];
Chris@0 436 }
Chris@0 437
Chris@0 438 /**
Chris@0 439 * {@inheritdoc}
Chris@0 440 */
Chris@0 441 public function postCreate(EntityStorageInterface $storage) {
Chris@0 442 parent::postCreate($storage);
Chris@0 443
Chris@0 444 $this->mergeDefaultDisplaysOptions();
Chris@0 445 }
Chris@0 446
Chris@0 447 /**
Chris@0 448 * {@inheritdoc}
Chris@0 449 */
Chris@0 450 public static function preDelete(EntityStorageInterface $storage, array $entities) {
Chris@0 451 parent::preDelete($storage, $entities);
Chris@0 452
Chris@0 453 // Call the remove() hook on the individual displays.
Chris@0 454 /** @var \Drupal\views\ViewEntityInterface $entity */
Chris@0 455 foreach ($entities as $entity) {
Chris@0 456 $executable = Views::executableFactory()->get($entity);
Chris@0 457 foreach ($entity->get('display') as $display_id => $display) {
Chris@0 458 $executable->setDisplay($display_id);
Chris@0 459 $executable->getDisplay()->remove();
Chris@0 460 }
Chris@0 461 }
Chris@0 462 }
Chris@0 463
Chris@0 464 /**
Chris@0 465 * {@inheritdoc}
Chris@0 466 */
Chris@0 467 public static function postDelete(EntityStorageInterface $storage, array $entities) {
Chris@0 468 parent::postDelete($storage, $entities);
Chris@0 469
Chris@14 470 $tempstore = \Drupal::service('tempstore.shared')->get('views');
Chris@0 471 foreach ($entities as $entity) {
Chris@0 472 $tempstore->delete($entity->id());
Chris@0 473 }
Chris@0 474 }
Chris@0 475
Chris@0 476 /**
Chris@0 477 * {@inheritdoc}
Chris@0 478 */
Chris@0 479 public function mergeDefaultDisplaysOptions() {
Chris@0 480 $displays = [];
Chris@0 481 foreach ($this->get('display') as $key => $options) {
Chris@0 482 $options += [
Chris@0 483 'display_options' => [],
Chris@0 484 'display_plugin' => NULL,
Chris@0 485 'id' => NULL,
Chris@0 486 'display_title' => '',
Chris@0 487 'position' => NULL,
Chris@0 488 ];
Chris@0 489 // Add the defaults for the display.
Chris@0 490 $displays[$key] = $options;
Chris@0 491 }
Chris@0 492 $this->set('display', $displays);
Chris@0 493 }
Chris@0 494
Chris@0 495 /**
Chris@0 496 * {@inheritdoc}
Chris@0 497 */
Chris@0 498 public function isInstallable() {
Chris@0 499 $table_definition = \Drupal::service('views.views_data')->get($this->base_table);
Chris@0 500 // Check whether the base table definition exists and contains a base table
Chris@0 501 // definition. For example, taxonomy_views_data_alter() defines
Chris@0 502 // node_field_data even if it doesn't exist as a base table.
Chris@0 503 return $table_definition && isset($table_definition['table']['base']);
Chris@0 504 }
Chris@0 505
Chris@0 506 /**
Chris@0 507 * {@inheritdoc}
Chris@0 508 */
Chris@0 509 public function __sleep() {
Chris@0 510 $keys = parent::__sleep();
Chris@0 511 unset($keys[array_search('executable', $keys)]);
Chris@0 512 return $keys;
Chris@0 513 }
Chris@0 514
Chris@0 515 /**
Chris@0 516 * Invalidates cache tags.
Chris@0 517 */
Chris@0 518 public function invalidateCaches() {
Chris@0 519 // Invalidate cache tags for cached rows.
Chris@0 520 $tags = $this->getCacheTags();
Chris@0 521 \Drupal::service('cache_tags.invalidator')->invalidateTags($tags);
Chris@0 522 }
Chris@0 523
Chris@0 524 /**
Chris@0 525 * {@inheritdoc}
Chris@0 526 */
Chris@0 527 public function onDependencyRemoval(array $dependencies) {
Chris@0 528 $changed = FALSE;
Chris@0 529
Chris@0 530 // Don't intervene if the views module is removed.
Chris@0 531 if (isset($dependencies['module']) && in_array('views', $dependencies['module'])) {
Chris@0 532 return FALSE;
Chris@0 533 }
Chris@0 534
Chris@0 535 // If the base table for the View is provided by a module being removed, we
Chris@0 536 // delete the View because this is not something that can be fixed manually.
Chris@0 537 $views_data = Views::viewsData();
Chris@0 538 $base_table = $this->get('base_table');
Chris@0 539 $base_table_data = $views_data->get($base_table);
Chris@0 540 if (!empty($base_table_data['table']['provider']) && in_array($base_table_data['table']['provider'], $dependencies['module'])) {
Chris@0 541 return FALSE;
Chris@0 542 }
Chris@0 543
Chris@0 544 $current_display = $this->getExecutable()->current_display;
Chris@0 545 $handler_types = Views::getHandlerTypes();
Chris@0 546
Chris@0 547 // Find all the handlers and check whether they want to do something on
Chris@0 548 // dependency removal.
Chris@0 549 foreach ($this->display as $display_id => $display_plugin_base) {
Chris@0 550 $this->getExecutable()->setDisplay($display_id);
Chris@0 551 $display = $this->getExecutable()->getDisplay();
Chris@0 552
Chris@0 553 foreach (array_keys($handler_types) as $handler_type) {
Chris@0 554 $handlers = $display->getHandlers($handler_type);
Chris@0 555 foreach ($handlers as $handler_id => $handler) {
Chris@0 556 if ($handler instanceof DependentWithRemovalPluginInterface) {
Chris@0 557 if ($handler->onDependencyRemoval($dependencies)) {
Chris@0 558 // Remove the handler and indicate we made changes.
Chris@0 559 unset($this->display[$display_id]['display_options'][$handler_types[$handler_type]['plural']][$handler_id]);
Chris@0 560 $changed = TRUE;
Chris@0 561 }
Chris@0 562 }
Chris@0 563 }
Chris@0 564 }
Chris@0 565 }
Chris@0 566
Chris@0 567 // Disable the View if we made changes.
Chris@0 568 // @todo https://www.drupal.org/node/2832558 Give better feedback for
Chris@0 569 // disabled config.
Chris@0 570 if ($changed) {
Chris@0 571 // Force a recalculation of the dependencies if we made changes.
Chris@0 572 $this->getExecutable()->current_display = NULL;
Chris@0 573 $this->calculateDependencies();
Chris@0 574 $this->disable();
Chris@0 575 }
Chris@0 576
Chris@0 577 $this->getExecutable()->setDisplay($current_display);
Chris@0 578 return $changed;
Chris@0 579 }
Chris@0 580
Chris@0 581 }