diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedBundleField.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedBundleField.php new file mode 100644 index 0000000000000000000000000000000000000000..c97430117e22f33f6929098ba94c6cf03c8f481c --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedBundleField.php @@ -0,0 +1,91 @@ +<?php + +namespace Drupal\entity_test\Entity; + +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\Field\FieldDefinition; +use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\entity_test\FieldStorageDefinition; +use Drupal\entity_test\Plugin\Field\ComputedReferenceTestFieldItemList; +use Drupal\entity_test\Plugin\Field\ComputedTestBundleFieldItemList; +use Drupal\entity_test\Plugin\Field\ComputedTestCacheableStringItemList; +use Drupal\entity_test\Plugin\Field\ComputedTestFieldItemList; + +/** + * An entity used for testing computed bundle field values. + * + * @ContentEntityType( + * id = "entity_test_comp_bund_fld", + * label = @Translation("Entity Test computed bundle field"), + * base_table = "entity_test_comp_bund_fld", + * handlers = { + * "views_data" = "Drupal\entity_test\EntityTestViewsData" + * }, + * entity_keys = { + * "id" = "id", + * "uuid" = "uuid", + * "label" = "name", + * "bundle" = "type", + * }, + * admin_permission = "administer entity_test content", + * ) + */ +class EntityTestComputedBundleField extends EntityTest { + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + $fields['computed_string_field'] = BaseFieldDefinition::create('string') + ->setLabel('Computed Field Test') + ->setComputed(TRUE) + ->setClass(ComputedTestFieldItemList::class); + + $fields['computed_reference_field'] = BaseFieldDefinition::create('entity_reference') + ->setLabel('Computed Reference Field Test') + ->setComputed(TRUE) + ->setSetting('target_type', 'entity_test') + ->setClass(ComputedReferenceTestFieldItemList::class); + + $fields['computed_test_cacheable_string_field'] = BaseFieldDefinition::create('computed_test_cacheable_string_item') + ->setLabel(new TranslatableMarkup('Computed Cacheable String Field Test')) + ->setComputed(TRUE) + ->setClass(ComputedTestCacheableStringItemList::class) + ->setReadOnly(FALSE) + ->setInternal(FALSE); + + return $fields; + } + + /** + * {@inheritdoc} + */ + public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { + $fields = parent::bundleFieldDefinitions($entity_type, $bundle, $base_field_definitions); + + $computed_field_bundles = [ + 'entity_test_comp_bund_fld_bund', + 'entity_test_comp_bund_fld_bund_2', + ]; + + if (in_array($bundle, $computed_field_bundles, TRUE)) { + // @todo Use the proper FieldStorageDefinition class instead + // https://www.drupal.org/node/2280639. + $storageDefinition = FieldStorageDefinition::create('string') + ->setName('computed_bundle_field') + ->setTargetEntityTypeId($entity_type->id()) + ->setComputed(TRUE) + ->setClass(ComputedTestBundleFieldItemList::class); + $fields['computed_bundle_field'] = FieldDefinition::createFromFieldStorageDefinition($storageDefinition) + ->setLabel(t('A computed Bundle Field Test')) + ->setComputed(TRUE) + ->setClass(ComputedTestBundleFieldItemList::class); + } + + return $fields; + } + +} diff --git a/core/modules/system/tests/modules/entity_test/src/EntityTestViewsData.php b/core/modules/system/tests/modules/entity_test/src/EntityTestViewsData.php index 27a1186ada6f4f1da6b05a7550e337909b4e6bcd..54a234e34b1799a2939e492be2b5b8f45a290d50 100644 --- a/core/modules/system/tests/modules/entity_test/src/EntityTestViewsData.php +++ b/core/modules/system/tests/modules/entity_test/src/EntityTestViewsData.php @@ -26,6 +26,16 @@ public function getViewsData() { ], ]; } + if ($this->entityType->id() === 'entity_test_comp_bund_fld') { + $views_data['entity_test_comp_bund_fld']['computed_bundle_field'] = [ + 'title' => $this->t('Computed Bundle Field'), + 'field' => [ + 'id' => 'field', + 'default_formatter' => 'string', + 'field_name' => 'computed_bundle_field', + ], + ]; + } if ($this->entityType->id() != 'entity_test') { return $views_data; diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestBundleFieldItemList.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestBundleFieldItemList.php new file mode 100644 index 0000000000000000000000000000000000000000..c69e36855888782d818819878ca6e7605dd02dfc --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestBundleFieldItemList.php @@ -0,0 +1,24 @@ +<?php + +namespace Drupal\entity_test\Plugin\Field; + +use Drupal\Core\TypedData\ComputedItemListTrait; +use Drupal\Core\Field\FieldItemList; + +/** + * A computed field item list for a bundle field. + */ +class ComputedTestBundleFieldItemList extends FieldItemList { + + use ComputedItemListTrait; + + /** + * Compute the list property from state. + */ + protected function computeValue() { + foreach (\Drupal::state()->get('entity_test_comp_bund_fld_item_list_value', []) as $delta => $item) { + $this->list[$delta] = $this->createItem($delta, $item); + } + } + +} diff --git a/core/modules/views/src/Plugin/views/field/EntityField.php b/core/modules/views/src/Plugin/views/field/EntityField.php index e6bbda450474881e74e23982ae2be1cdb05c890c..0c4eed513d4be01c4f9f42aa63a6ce253c0b293b 100644 --- a/core/modules/views/src/Plugin/views/field/EntityField.php +++ b/core/modules/views/src/Plugin/views/field/EntityField.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; @@ -126,6 +127,13 @@ class EntityField extends FieldPluginBase implements CacheableDependencyInterfac */ protected $entityFieldRenderer; + /** + * The entity type bundle info service. + * + * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface + */ + protected $entityTypeBundleInfo; + /** * The fields that we are actually grouping on. */ @@ -154,8 +162,10 @@ class EntityField extends FieldPluginBase implements CacheableDependencyInterfac * The entity repository. * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager * The entity field manager. + * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info + * The entity type bundle info service. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, FormatterPluginManager $formatter_plugin_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, LanguageManagerInterface $language_manager, RendererInterface $renderer, EntityRepositoryInterface $entity_repository, EntityFieldManagerInterface $entity_field_manager) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, FormatterPluginManager $formatter_plugin_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, LanguageManagerInterface $language_manager, RendererInterface $renderer, EntityRepositoryInterface $entity_repository, EntityFieldManagerInterface $entity_field_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->entityTypeManager = $entity_type_manager; @@ -165,6 +175,11 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->renderer = $renderer; $this->entityRepository = $entity_repository; $this->entityFieldManager = $entity_field_manager; + if ($entity_type_bundle_info === NULL) { + $entity_type_bundle_info = \Drupal::service('entity_type.bundle.info'); + @trigger_error('Calling ' . __CLASS__ . '::__construct() without the $entity_type_bundle_info argument is deprecated in drupal:10.3.0 and is required in drupal:11.0.0. See https://www.drupal.org/node/3380621', E_USER_DEPRECATED); + } + $this->entityTypeBundleInfo = $entity_type_bundle_info; // @todo Unify 'entity field'/'field_name' instead of converting back and // forth. https://www.drupal.org/node/2410779 @@ -188,7 +203,8 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('language_manager'), $container->get('renderer'), $container->get('entity.repository'), - $container->get('entity_field.manager') + $container->get('entity_field.manager'), + $container->get('entity_type.bundle.info') ); } @@ -363,6 +379,19 @@ protected function getFieldStorageDefinition() { if (isset($this->definition['field_name']) && isset($base_fields[$this->definition['field_name']])) { return $base_fields[$this->definition['field_name']]->getFieldStorageDefinition(); } + + // If there is still no field storage definition found, we are dealing with + // a bundle field. Get the storage from the field definition on the first + // bundle we find which has this field. + $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id); + foreach ($bundles as $bundle_id => $bundle) { + $bundle_fields = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle_id); + if (isset($this->definition['field_name']) && isset($bundle_fields[$this->definition['field_name']])) { + return $bundle_fields[$this->definition['field_name']]->getFieldStorageDefinition(); + } + } + + return NULL; } /** diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.computed_bundle_field_view.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.computed_bundle_field_view.yml new file mode 100644 index 0000000000000000000000000000000000000000..9e1130df3d52089140d8048a8478d772227eaa5b --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.computed_bundle_field_view.yml @@ -0,0 +1,234 @@ +langcode: en +status: true +dependencies: + module: + - entity_test +id: computed_bundle_field_view +label: 'Computed Bundled Field View' +module: views +description: '' +tag: '' +base_table: entity_test_comp_bund_fld +base_field: id +display: + default: + display_plugin: default + id: default + display_title: Default + position: 0 + display_options: + access: + type: none + options: { } + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: mini + options: + items_per_page: 10 + offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: ‹‹ + next: ›› + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + row: + type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true + fields: + computed_string_field: + id: computed_string_field + table: entity_test_comp_bund_fld + field: computed_string_field + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_computed_field + plugin_id: field + computed_bundle_field: + id: computed_bundle_field + table: entity_test_comp_bund_fld + field: computed_bundle_field + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_comp_bund_fld + plugin_id: field + filters: { } + sorts: { } + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + tags: { } + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + display_extenders: { } + path: foo + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + tags: { } diff --git a/core/modules/views/tests/src/Kernel/Handler/ComputedBundleFieldTest.php b/core/modules/views/tests/src/Kernel/Handler/ComputedBundleFieldTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cb1fd31d008e5000459eed90d779170737ddc0c0 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/ComputedBundleFieldTest.php @@ -0,0 +1,94 @@ +<?php + +namespace Drupal\Tests\views\Kernel\Handler; + +use Drupal\entity_test\Entity\EntityTestComputedBundleField; +use Drupal\Tests\views\Kernel\ViewsKernelTestBase; +use Drupal\views\Tests\ViewTestData; +use Drupal\views\Views; + +/** + * Provides some integration tests for computed bundle fields. + * + * @see \Drupal\views\Plugin\views\field\EntityField + * @group views + */ +class ComputedBundleFieldTest extends ViewsKernelTestBase { + + /** + * Views to be enabled. + * + * @var array + */ + public static $testViews = ['computed_bundle_field_view']; + + /** + * Modules to enable. + * + * @var array + */ + protected static $modules = ['entity_test']; + + /** + * {@inheritdoc} + */ + protected function setUp($import_test_views = TRUE): void { + // Don't install the test view until the bundles are defined. + parent::setUp(FALSE); + + $this->installEntitySchema('entity_test_comp_bund_fld'); + + // Create a default bundle that has a computed field. + entity_test_create_bundle('entity_test_comp_bund_fld_bund', NULL, 'entity_test_comp_bund_fld'); + + // Create a second bundle that also has a computed field. + entity_test_create_bundle('entity_test_comp_bund_fld_bund_2', NULL, 'entity_test_comp_bund_fld'); + + // Create a bundle that does not have the computed field. + entity_test_create_bundle('entity_test_bundle_no_comp_field', NULL, 'entity_test_comp_bund_fld'); + + ViewTestData::createTestViews(static::class, ['views_test_config']); + + // Create an entity using the default bundle with a computed field. + $entity_with_comp_field = EntityTestComputedBundleField::create([ + 'type' => 'entity_test_comp_bund_fld_bund', + 'name' => 'Entity with bundle field', + ]); + $entity_with_comp_field->save(); + + // Create an entity using the second bundle with a computed field. + $entity_with_comp_field_2 = EntityTestComputedBundleField::create([ + 'type' => 'entity_test_comp_bund_fld_bund_2', + 'name' => 'Entity 2 with bundle field', + ]); + $entity_with_comp_field_2->save(); + + // Create an entity using the third bundle without a computed field. + $entity_no_computed_field = EntityTestComputedBundleField::create([ + 'type' => 'entity_test_bundle_no_comp_field', + 'name' => 'Entity without bundle field', + ]); + $entity_no_computed_field->save(); + } + + /** + * Tests the computed field handler. + */ + public function testComputedFieldHandler() { + \Drupal::state()->set('entity_test_computed_field_item_list_value', ['computed string']); + \Drupal::state()->set('entity_test_comp_bund_fld_item_list_value', ['some other string that is also computed']); + + $view = Views::getView('computed_bundle_field_view'); + $this->executeView($view); + $this->assertCount(3, $view->result, 'The number of returned rows match.'); + + // Entities 1 and 2 should have the computed bundle field. But entity 3 + // should not. + $this->assertStringContainsString('some other string that is also computed', $view->field['computed_bundle_field']->render($view->result[0])); + $this->assertStringContainsString('some other string that is also computed', $view->field['computed_bundle_field']->render($view->result[1])); + $this->assertStringNotContainsString('some other string that is also computed', $view->field['computed_bundle_field']->render($view->result[2])); + + $view->destroy(); + } + +} diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php index b1ad678df2b914818f084355a1db2410706ab834..6cda638d760e6932316ac8214d8ed629b1410cd7 100644 --- a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php @@ -4,6 +4,7 @@ use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Tests\UnitTestCase; @@ -62,6 +63,13 @@ class FieldTest extends UnitTestCase { */ protected $fieldTypePluginManager; + /** + * The entity type bundle info service. + * + * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface + */ + protected $entityTypeBundleInfo; + /** * The renderer. * @@ -84,6 +92,7 @@ protected function setUp(): void { $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class); $this->entityFieldManager = $this->createMock(EntityFieldManagerInterface::class); + $this->entityTypeBundleInfo = $this->createMock(EntityTypeBundleInfoInterface::class); $this->entityRepository = $this->createMock(EntityRepositoryInterface::class); $this->formatterPluginManager = $this->getMockBuilder('Drupal\Core\Field\FormatterPluginManager') ->disableOriginalConstructor() @@ -121,7 +130,7 @@ public function testConstruct() { // provides it. 'entity field' => 'title', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $this->assertEquals('title', $handler->definition['field_name']); } @@ -134,7 +143,7 @@ public function testDefineOptionsWithNoOptions() { 'entity_type' => 'test_entity', 'field_name' => 'title', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); // Setup the entity field manager to allow fetching the storage definitions. $title_storage = $this->getBaseFieldStorage(); @@ -163,7 +172,7 @@ public function testDefineOptionsWithDefaultFormatterOnFieldDefinition() { 'default_formatter' => 'test_example', 'default_formatter_settings' => ['link_to_entity' => TRUE], ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); // Setup the entity field manager to allow fetching the storage definitions. $title_storage = $this->getBaseFieldStorage(); @@ -190,7 +199,7 @@ public function testDefineOptionsWithDefaultFormatterOnFieldType() { 'field_name' => 'title', 'default_formatter_settings' => ['link_to_entity' => TRUE], ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); // Setup the entity field manager to allow fetching the storage definitions. $title_storage = $this->getBaseFieldStorage(); @@ -216,7 +225,7 @@ public function testCalculateDependenciesWithBaseField() { 'entity_type' => 'test_entity', 'field_name' => 'title', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $title_storage = $this->getBaseFieldStorage(); $this->entityFieldManager->expects($this->atLeastOnce()) @@ -238,7 +247,7 @@ public function testCalculateDependenciesWithConfiguredField() { 'entity_type' => 'test_entity', 'field_name' => 'body', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $body_storage = $this->getConfigFieldStorage(); $this->entityFieldManager->expects($this->atLeastOnce()) @@ -264,7 +273,7 @@ public function testAccess() { 'entity_type' => 'test_entity', 'field_name' => 'title', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $handler->view = $this->executable; $handler->setViewsData($this->viewsData); @@ -315,7 +324,7 @@ public function testClickSortWithOutConfiguredColumn($order) { 'entity_type' => 'test_entity', 'field_name' => 'title', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $handler->view = $this->executable; $this->entityFieldManager->expects($this->never()) @@ -337,7 +346,7 @@ public function testClickSortWithBaseField($order) { 'entity_type' => 'test_entity', 'field_name' => 'title', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $handler->view = $this->executable; $field_storage = $this->getBaseFieldStorage(); @@ -397,7 +406,7 @@ public function testClickSortWithConfiguredField($order) { 'entity_type' => 'test_entity', 'field_name' => 'body', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $handler->view = $this->executable; $field_storage = $this->getConfigFieldStorage(); @@ -452,7 +461,7 @@ public function testQueryWithGroupByForBaseField() { 'entity_type' => 'test_entity', 'field_name' => 'title', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $handler->view = $this->executable; $handler->view->field = [$handler]; @@ -514,7 +523,7 @@ public function testQueryWithGroupByForConfigField() { 'entity_type' => 'test_entity', 'field_name' => 'body', ]; - $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new EntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $handler->view = $this->executable; $handler->view->field = [$handler]; @@ -578,7 +587,7 @@ public function testPrepareItemsByDelta(array $options, array $expected_values) 'entity_type' => 'test_entity', 'field_name' => 'integer', ]; - $handler = new FieldTestEntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager); + $handler = new FieldTestEntityField([], 'field', $definition, $this->entityTypeManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer, $this->entityRepository, $this->entityFieldManager, $this->entityTypeBundleInfo); $handler->view = $this->executable; $handler->view->field = [$handler]; diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php index 49eb06513edb29c1facfd6fa6e90fd828a2c4f06..503ef9064d5ce68f6f0cb8cf029f999f0a60f4c3 100644 --- a/core/modules/views/views.api.php +++ b/core/modules/views/views.api.php @@ -420,6 +420,25 @@ function hook_views_data() { ], ]; + // Computed field example. Computed fields are not associated with actual data + // tables and fields, and therefore have no schema. Instead, they are computed + // when the value is read from the entity. Here's the definition of a computed + // field that exists for a particular entity type. The value of the field will + // be calculated by a defined class. If the defined class for the computed + // fields differs between multiple bundles of the same entity type, then each + // of those fields should be added separately. + // @see \Drupal\Core\TypedData\DataDefinitionInterface::setComputed(). + // @see \Drupal\Core\TypedData\DataDefinitionInterface::setClass(). + $data['example_table']['computed_bundle_field'] = [ + 'title' => t('Computed Bundle Field'), + 'help' => t('The computed bundle field'), + 'field' => [ + 'id' => 'field', + 'default_formatter' => 'string', + 'field_name' => 'computed_bundle_field', + ], + ]; + // Area example. Areas are not generally associated with actual data // tables and fields. This example is from views_views_data(), which defines // the "Global" table (not really a table, but a group of Fields, Filters, diff --git a/core/phpstan-baseline.neon b/core/phpstan-baseline.neon index 6840667cb8885295d1af6790c1f4bdf4979cf8c2..ba653f410f80dbb3b1a49596149eb8f9e9db114a 100644 --- a/core/phpstan-baseline.neon +++ b/core/phpstan-baseline.neon @@ -2716,11 +2716,6 @@ parameters: count: 9 path: modules/views/src/Plugin/views/field/Date.php - - - message: "#^Method Drupal\\\\views\\\\Plugin\\\\views\\\\field\\\\EntityField\\:\\:getFieldStorageDefinition\\(\\) should return Drupal\\\\Core\\\\Field\\\\FieldStorageDefinitionInterface but return statement is missing\\.$#" - count: 1 - path: modules/views/src/Plugin/views/field/EntityField.php - - message: "#^Method Drupal\\\\views\\\\Plugin\\\\views\\\\field\\\\EntityField\\:\\:renderItems\\(\\) should return string but return statement is missing\\.$#" count: 1