Skip to content
Snippets Groups Projects
Verified Commit da5ea0ae authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3057545 by acbramley, hchonov, bbrala, bradjones1, larowlan,...

Issue #3057545 by acbramley, hchonov, bbrala, bradjones1, larowlan, yogeshmpawar, Leon Kessler, gease, joachim, gabesullice, kfritsche, jibran, Wim Leers, Berdir, smustgrave, alexpott, catch: ResourceTypeRepository wrongly assumes that all entity reference fields have the setting "target_type"
parent 0733716c
Branches
Tags
44 merge requests!12227Issue #3181946 by jonmcl, mglaman,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4594Applying patch for Views Global Text area field to allow extra HTML tags. As video, source and iframe tag is not rendering. Due to which Media embedded video and remote-video not rendering in Views Global Text area field.,!3878Removed unused condition head title for views,!38582585169-10.1.x,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3668Resolve #3347842 "Deprecate the trusted",!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3546refactored dialog.pcss file,!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3502Issue #3335308: Confusing behavior with FormState::setFormState and FormState::setMethod,!3478Issue #3337882: Deleted menus are not removed from content type config,!3452Issue #3332701: Refactor Claro's tablesort-indicator stylesheet,!3451Issue #2410579: Allows setting the current language programmatically.,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3226Issue #2987537: Custom menu link entity type should not declare "bundle" entity key,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3147Issue #3328457: Replace most substr($a, $i) where $i is negative with str_ends_with(),!3146Issue #3328456: Replace substr($a, 0, $i) with str_starts_with(),!3133core/modules/system/css/components/hidden.module.css,!31312878513-10.1.x,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2614Issue #2981326: Replace non-test usages of \Drupal::logger() with IoC injection,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!877Issue #2708101: Default value for link text is not saved,!844Resolve #3036010 "Updaters",!673Issue #3214208: FinishResponseSubscriber could create duplicate headers,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer
Showing
with 252 additions and 29 deletions
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
use Drupal\Core\Entity\TypedData\EntityDataDefinition; use Drupal\Core\Entity\TypedData\EntityDataDefinition;
use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldException; use Drupal\Core\Field\FieldException;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface; use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
...@@ -42,7 +41,7 @@ ...@@ -42,7 +41,7 @@
* list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList", * list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
* ) * )
*/ */
class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterface, PreconfiguredFieldUiOptionsInterface { class EntityReferenceItem extends EntityReferenceItemBase implements OptionsProviderInterface, PreconfiguredFieldUiOptionsInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -778,4 +777,19 @@ public static function getPreconfiguredOptions() { ...@@ -778,4 +777,19 @@ public static function getPreconfiguredOptions() {
return $options; return $options;
} }
/**
* {@inheritdoc}
*/
public static function getReferenceableBundles(FieldDefinitionInterface $field_definition): array {
$settings = $field_definition->getSettings();
$target_type_id = $settings['target_type'];
$handler_settings = $settings['handler_settings'];
$has_target_bundles = isset($handler_settings['target_bundles']) && !empty($handler_settings['target_bundles']);
$target_bundles = $has_target_bundles
? $handler_settings['target_bundles']
: array_keys(\Drupal::service('entity_type.bundle.info')->getBundleInfo($target_type_id));
return [$target_type_id => $target_bundles];
}
} }
<?php
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
/**
* Base class for field items referencing other entities.
*
* Any field type that is an entity reference should extend from this class in
* order to remain backwards compatible with any changes added in the future
* to EntityReferenceItemInterface.
*/
abstract class EntityReferenceItemBase extends FieldItemBase implements EntityReferenceItemInterface {
}
<?php
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
/**
* Interface definition for field items referencing other entities.
*
* Field items should extend \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemBase.
*/
interface EntityReferenceItemInterface {
/**
* Returns the referenceable entity types and bundles.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition for which to retrieve the referenceable entity
* types and bundles.
*
* @return array
* An array of referenceable bundles where the array is keyed by the entity
* type ID, with values an array of bundle names. (It is a single-value
* array with the entity type ID if the entity type does not implement
* bundles.)
*/
public static function getReferenceableBundles(FieldDefinitionInterface $field_definition): array;
}
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemInterface;
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
use Drupal\jsonapi\Access\EntityAccessChecker; use Drupal\jsonapi\Access\EntityAccessChecker;
use Drupal\jsonapi\Context\FieldResolver; use Drupal\jsonapi\Context\FieldResolver;
use Drupal\jsonapi\Exception\EntityAccessDeniedHttpException; use Drupal\jsonapi\Exception\EntityAccessDeniedHttpException;
...@@ -137,11 +138,32 @@ protected function resolveIncludeTree(array $include_tree, Data $data, Data $inc ...@@ -137,11 +138,32 @@ protected function resolveIncludeTree(array $include_tree, Data $data, Data $inc
$includes = IncludedData::merge($includes, new IncludedData([$exception])); $includes = IncludedData::merge($includes, new IncludedData([$exception]));
continue; continue;
} }
$target_type = $field_list->getFieldDefinition()->getFieldStorageDefinition()->getSetting('target_type'); if (is_subclass_of($field_list->getItemDefinition()->getClass(), EntityReferenceItemInterface::class)) {
assert(!empty($target_type)); foreach ($field_list as $field_item) {
foreach ($field_list as $field_item) { if (!($field_item->getDataDefinition()->getPropertyDefinition('entity') instanceof DataReferenceDefinitionInterface)) {
assert($field_item instanceof EntityReferenceItem); continue;
$references[$target_type][] = $field_item->get($field_item::mainPropertyName())->getValue(); }
if (!($field_item->entity instanceof EntityInterface)) {
continue;
}
// Support entity reference fields that don't have the referenced
// target type stored in settings.
$references[$field_item->entity->getEntityTypeId()][] = $field_item->get($field_item::mainPropertyName())->getValue();
}
}
else {
@trigger_error(
sprintf('Entity reference field items not implementing %s is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3279140', EntityReferenceItemInterface::class),
E_USER_DEPRECATED
);
$target_type = $field_list->getFieldDefinition()->getFieldStorageDefinition()->getSetting('target_type');
if (!empty($target_type)) {
foreach ($field_list as $field_item) {
$references[$target_type][] = $field_item->get($field_item::mainPropertyName())->getValue();
}
}
} }
} }
foreach ($references as $target_type => $ids) { foreach ($references as $target_type => $ids) {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Installer\InstallerKernel; use Drupal\Core\Installer\InstallerKernel;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemInterface;
use Drupal\Core\TypedData\DataReferenceTargetDefinition; use Drupal\Core\TypedData\DataReferenceTargetDefinition;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException;
...@@ -439,29 +440,45 @@ protected function calculateRelatableResourceTypes(ResourceType $resource_type, ...@@ -439,29 +440,45 @@ protected function calculateRelatableResourceTypes(ResourceType $resource_type,
protected function getRelatableResourceTypesFromFieldDefinition(FieldDefinitionInterface $field_definition, array $resource_types) { protected function getRelatableResourceTypesFromFieldDefinition(FieldDefinitionInterface $field_definition, array $resource_types) {
$item_definition = $field_definition->getItemDefinition(); $item_definition = $field_definition->getItemDefinition();
$entity_type_id = $item_definition->getSetting('target_type'); $entity_type_id = $item_definition->getSetting('target_type');
$handler_settings = $item_definition->getSetting('handler_settings');
$target_bundles = empty($handler_settings['target_bundles']) ? $this->getAllBundlesForEntityType($entity_type_id) : $handler_settings['target_bundles'];
$relatable_resource_types = []; $relatable_resource_types = [];
$item_class = $item_definition->getClass();
if (is_subclass_of($item_class, EntityReferenceItemInterface::class)) {
$target_type_bundles = $item_class::getReferenceableBundles($field_definition);
}
else {
@trigger_error(
sprintf('Entity reference field items not implementing %s is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3279140', EntityReferenceItemInterface::class),
E_USER_DEPRECATED
);
$handler_settings = $item_definition->getSetting('handler_settings');
foreach ($target_bundles as $target_bundle) { $has_target_bundles = isset($handler_settings['target_bundles']) && !empty($handler_settings['target_bundles']);
if ($resource_type = static::lookupResourceType($resource_types, $entity_type_id, $target_bundle)) { $target_bundles = $has_target_bundles ? $handler_settings['target_bundles'] : $this->getAllBundlesForEntityType($entity_type_id);
$relatable_resource_types[] = $resource_type; $target_type_bundles = [$entity_type_id => $target_bundles];
} }
// Do not warn during the site installation since system integrity
// is not guaranteed in this period and the warnings may pop up falsy, foreach ($target_type_bundles as $entity_type_id => $target_bundles) {
// adding confusion to the process. foreach ($target_bundles as $target_bundle) {
elseif (!InstallerKernel::installationAttempted()) { if ($resource_type = static::lookupResourceType($resource_types, $entity_type_id, $target_bundle)) {
trigger_error( $relatable_resource_types[] = $resource_type;
sprintf( continue;
'The "%s" at "%s:%s" references the "%s:%s" entity type that does not exist. Please take action.', }
$field_definition->getName(), // Do not warn during site installation since system integrity
$field_definition->getTargetEntityTypeId(), // is not guaranteed during this period and may cause confusing and
$field_definition->getTargetBundle(), // unnecessary warnings.
$entity_type_id, if (!InstallerKernel::installationAttempted()) {
$target_bundle trigger_error(
), sprintf(
E_USER_WARNING 'The "%s" at "%s:%s" references the "%s:%s" entity type that does not exist. Please take action.',
); $field_definition->getName(),
$field_definition->getTargetEntityTypeId(),
$field_definition->getTargetBundle(),
$entity_type_id,
$target_bundle
),
E_USER_WARNING
);
}
} }
} }
......
name: 'JSON API test deprecated reference field types'
type: module
package: Testing
<?php
/**
* @file
* Contains hook implementations for the jsonapi_test_reference_types module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Implements hook_entity_base_field_info().
*/
function jsonapi_test_reference_types_entity_base_field_info(EntityTypeInterface $entity_type) {
// Add a field of the deprecated reference type to nodes.
if ($entity_type->id() === 'node') {
$fields = [];
$fields['deprecated_reference'] = BaseFieldDefinition::create('jsonapi_test_deprecated_reference')
->setLabel(t('Reference'))
->setDescription(t('Deprecated reference field.'));
return $fields;
}
}
<?php
namespace Drupal\jsonapi_test_reference_types\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataReferenceTargetDefinition;
/**
* Entity reference field type which doesn't implement the standard interface.
*
* This is to test the handling of deprecated fields which do not implement
* \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemInterface.
*
* @see https://www.drupal.org/node/3279140
* @see \Drupal\Tests\jsonapi\Kernel\ResourceType\RelatedResourceTypesTest::testGetRelatableResourceTypesFromFieldDefinitionEntityReferenceFieldDeprecated()
*
* @todo Remove this in Drupal 11 https://www.drupal.org/project/drupal/issues/3353314.
*
* @FieldType(
* id = "jsonapi_test_deprecated_reference",
* )
*/
class DeprecatedReferenceItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['target_id'] = DataReferenceTargetDefinition::create('integer')
->setLabel(new TranslatableMarkup('Target ID'))
->setSetting('unsigned', TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = [
'columns' => [
'target_id' => [
'description' => 'The ID of the target entity.',
'type' => 'int',
'unsigned' => TRUE,
],
],
'indexes' => [
'target_id' => ['target_id'],
],
];
return $schema;
}
/**
* {@inheritdoc}
*/
public static function mainPropertyName() {
return 'target_id';
}
}
...@@ -526,4 +526,26 @@ public function testCollectionFilterAccess() { ...@@ -526,4 +526,26 @@ public function testCollectionFilterAccess() {
$this->assertContains('user.node_grants:view', explode(' ', $response->getHeader('X-Drupal-Cache-Contexts')[0])); $this->assertContains('user.node_grants:view', explode(' ', $response->getHeader('X-Drupal-Cache-Contexts')[0]));
} }
/**
* Tests deprecated entity reference items.
*
* @group legacy
*/
public function testDeprecatedEntityReferenceFieldItem(): void {
\Drupal::service('module_installer')->install(['jsonapi_test_reference_types']);
$this->setUpAuthorization('GET');
// @todo Remove line below in favor of commented line in https://www.drupal.org/project/drupal/issues/2878463.
$url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), ['entity' => $this->entity->uuid()]);
// $url = $this->entity->toUrl('jsonapi');
$query = ['include' => 'deprecated_reference'];
$url->setOption('query', $query);
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
$this->expectDeprecation('Entity reference field items not implementing Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemInterface is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3279140');
$this->request('GET', $url, $request_options);
}
} }
...@@ -211,4 +211,15 @@ public function testGetRelatableResourceTypesFromFieldDefinition() { ...@@ -211,4 +211,15 @@ public function testGetRelatableResourceTypesFromFieldDefinition() {
} }
} }
/**
* Test the deprecation error on entity reference fields.
*
* @group legacy
*/
public function testGetRelatableResourceTypesFromFieldDefinitionEntityReferenceFieldDeprecated(): void {
\Drupal::service('module_installer')->install(['jsonapi_test_reference_types']);
$this->expectDeprecation('Entity reference field items not implementing Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemInterface is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3279140');
$this->resourceTypeRepository->all();
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment