Unverified Commit 61bff4ef authored by larowlan's avatar larowlan
Browse files

Issue #2870874 by clayfreeman, tim.plunkett, Wim Leers, larowlan, mradcliffe:...

Issue #2870874 by clayfreeman, tim.plunkett, Wim Leers, larowlan, mradcliffe: EntityBase::getTypedData() does not return the correct data type for configuration entities due to lack of consideration for data type derivatives
parent b8004807
......@@ -587,12 +587,38 @@ public function toArray() {
*/
public function getTypedData() {
if (!isset($this->typedData)) {
$class = \Drupal::typedDataManager()->getDefinition('entity')['class'];
$class = $this->getTypedDataClass();
$this->typedData = $class::createFromEntity($this);
}
return $this->typedData;
}
/**
* Returns the typed data class name for this entity.
*
* @return string
* The string representing the typed data class name.
*
* @see \Drupal\Core\Entity\Plugin\DataType\EntityAdapter
*/
private function getTypedDataClass(): string {
$typed_data_manager = \Drupal::typedDataManager();
// Check more specific data types that could apply to this entity.
$candidate_data_types = [
"entity:{$this->getEntityTypeId()}:{$this->bundle()}",
"entity:{$this->getEntityTypeId()}",
];
foreach ($candidate_data_types as $candidate_data_type) {
if ($typed_data_manager->hasDefinition($candidate_data_type)) {
return $typed_data_manager->getDefinition($candidate_data_type)['class'];
}
}
// Fall back to the generic entity definition.
return $typed_data_manager->getDefinition('entity')['class'];
}
/**
* {@inheritdoc}
*/
......
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Entity\Plugin\DataType\ConfigEntityAdapter;
use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
use Drupal\entity_test\Entity\EntityTestBundle;
use Drupal\entity_test\Entity\EntityTestWithBundle;
/**
* Tests the functionality provided by \Drupal\Core\Entity\EntityBase.
*
* @coversDefaultClass \Drupal\Core\Entity\EntityBase
* @group Entity
*/
class EntityBaseTest extends EntityKernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('entity_test_with_bundle');
}
/**
* Tests that the correct entity adapter is returned.
*
* @covers ::getTypedData
* @covers ::getClass
*/
public function testGetTypedData() {
$bundle = EntityTestBundle::create([
'id' => $this->randomMachineName(),
]);
$bundle->save();
$entity = EntityTestWithBundle::create([
'type' => $bundle->id(),
'name' => $this->randomString(),
]);
$entity->save();
$this->assertInstanceOf(ConfigEntityAdapter::class, $bundle->getTypedData());
$this->assertInstanceOf(EntityAdapter::class, $entity->getTypedData());
}
}
......@@ -9,6 +9,7 @@
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\TypedData\TypedDataManagerInterface;
......@@ -157,7 +158,6 @@ protected function setUp(): void {
$this->typedDataManager = $this->createMock(TypedDataManagerInterface::class);
$this->typedDataManager->expects($this->any())
->method('getDefinition')
->with('entity')
->will($this->returnValue(['class' => '\Drupal\Core\Entity\Plugin\DataType\EntityAdapter']));
$english = new Language(['id' => 'en']);
......@@ -333,6 +333,85 @@ public function testPreSaveRevision() {
$this->assertNull($this->entity->preSaveRevision($storage, $record));
}
/**
* Data provider for the ::getTypedData() test.
*
* The following entity data definitions, the first two being derivatives of
* the last definition, will be tested in order:
*
* 1. entity:$entity_type:$bundle
* 2. entity:$entity_type
* 3. entity
*
* @see \Drupal\Core\Entity\EntityBase::getTypedData()
* @see \Drupal\Core\Entity\EntityBase::getTypedDataClass()
* @see \Drupal\Core\Entity\Plugin\DataType\Deriver\EntityDeriver
*
* @return array
* Array of arrays with the following elements:
* - A bool whether to provide a bundle-specific definition.
* - A bool whether to provide an entity type-specific definition.
*/
public function providerTestTypedData(): array {
return [
'Entity data definition derivative with entity type and bundle' => [
TRUE,
TRUE,
],
'Entity data definition derivative with entity type' => [
FALSE,
TRUE,
],
'Entity data definition' => [
FALSE,
FALSE,
],
];
}
/**
* Tests each condition in EntityBase::getTypedData().
*
* @covers ::getTypedData
* @dataProvider providerTestTypedData
*/
public function testTypedData(bool $bundle_typed_data_definition, bool $entity_type_typed_data_definition): void {
$expected = EntityAdapter::class;
$typedDataManager = $this->createMock(TypedDataManagerInterface::class);
$typedDataManager->expects($this->once())
->method('getDefinition')
->willReturnMap([
[
"entity:{$this->entityTypeId}:{$this->bundle}", FALSE,
$bundle_typed_data_definition ? ['class' => $expected] : NULL,
],
[
"entity:{$this->entityTypeId}", FALSE,
$entity_type_typed_data_definition ? ['class' => $expected] : NULL,
],
[
'entity', TRUE,
['class' => $expected],
],
]);
// Temporarily replace the appropriate services in the container.
$container = \Drupal::getContainer();
$container->set('typed_data_manager', $typedDataManager);
\Drupal::setContainer($container);
// Create a mock entity used to retrieve typed data.
$entity = $this->getMockForAbstractClass(ContentEntityBase::class, [
[],
$this->entityTypeId,
$this->bundle,
], '', TRUE, TRUE, TRUE, ['isNew']);
// Assert that the returned data type is an instance of EntityAdapter.
$this->assertInstanceOf($expected, $entity->getTypedData());
}
/**
* @covers ::validate
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment