Issue #3481695 by phenaproxima, a.dmitriiev, thejimbirch, b_sharpe, alexpott: Entity displays cloning requires special config action
(cherry picked from commit a1b47eafdb
)
merge-requests/9925/head
parent
25ad53d3df
commit
9b7d24a6fa
|
@ -138,12 +138,14 @@ final class EntityMethod implements ConfigActionPluginInterface, ContainerFactor
|
||||||
if ($this->numberOfRequiredParams !== 1 && $this->numberOfParams !== 1) {
|
if ($this->numberOfRequiredParams !== 1 && $this->numberOfParams !== 1) {
|
||||||
throw new EntityMethodException(sprintf('Entity method config action \'%s\' requires an array value. The number of parameters or required parameters for %s::%s() is not 1', $this->pluginId, $entity->getEntityType()->getClass(), $this->method));
|
throw new EntityMethodException(sprintf('Entity method config action \'%s\' requires an array value. The number of parameters or required parameters for %s::%s() is not 1', $this->pluginId, $entity->getEntityType()->getClass(), $this->method));
|
||||||
}
|
}
|
||||||
$entity->{$this->method}($value);
|
$result = $entity->{$this->method}($value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$entity->{$this->method}(...$value);
|
$result = $entity->{$this->method}(...$value);
|
||||||
}
|
}
|
||||||
return $entity;
|
// If an instance of the entity (either itself, or a clone) was returned
|
||||||
|
// by the method, return that.
|
||||||
|
return is_a($result, get_class($entity)) ? $result : $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,6 +324,7 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
#[ActionMethod(adminLabel: new TranslatableMarkup('Copy to another mode'), pluralize: FALSE)]
|
||||||
public function createCopy($mode) {
|
public function createCopy($mode) {
|
||||||
$display = $this->createDuplicate();
|
$display = $this->createDuplicate();
|
||||||
$display->mode = $display->originalMode = $mode;
|
$display->mode = $display->originalMode = $mode;
|
||||||
|
@ -588,4 +589,20 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
|
||||||
return \Drupal::logger('system');
|
return \Drupal::logger('system');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function set($property_name, $value): static {
|
||||||
|
// If changing the entity ID, also update the target entity type, bundle,
|
||||||
|
// and view mode.
|
||||||
|
if ($this->isNew() && $property_name === $this->getEntityType()->getKey('id')) {
|
||||||
|
if (substr_count($value, '.') !== 2) {
|
||||||
|
throw new \InvalidArgumentException("'$value' is not a valid entity display ID.");
|
||||||
|
}
|
||||||
|
[$this->targetEntityType, $this->bundle, $this->mode] = explode('.', $value);
|
||||||
|
}
|
||||||
|
parent::set($property_name, $value);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ class LayoutBuilderEntityViewDisplayValidationTest extends ConfigEntityValidatio
|
||||||
*/
|
*/
|
||||||
public function testImmutableProperties(array $valid_values = []): void {
|
public function testImmutableProperties(array $valid_values = []): void {
|
||||||
parent::testImmutableProperties([
|
parent::testImmutableProperties([
|
||||||
|
'id' => 'entity_test_with_bundle.two.full',
|
||||||
'targetEntityType' => 'entity_test_with_bundle',
|
'targetEntityType' => 'entity_test_with_bundle',
|
||||||
'bundle' => 'two',
|
'bundle' => 'two',
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace Drupal\KernelTests\Core\Entity;
|
||||||
|
|
||||||
use Drupal\comment\Entity\CommentType;
|
use Drupal\comment\Entity\CommentType;
|
||||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||||
|
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
|
||||||
use Drupal\field\Entity\FieldConfig;
|
use Drupal\field\Entity\FieldConfig;
|
||||||
use Drupal\field\Entity\FieldStorageConfig;
|
use Drupal\field\Entity\FieldStorageConfig;
|
||||||
use Drupal\KernelTests\KernelTestBase;
|
use Drupal\KernelTests\KernelTestBase;
|
||||||
|
@ -170,4 +171,22 @@ class EntityDisplayBaseTest extends KernelTestBase {
|
||||||
$this->assertSame($expected_dependencies, $entity_display->getDependencies());
|
$this->assertSame($expected_dependencies, $entity_display->getDependencies());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that changing the entity ID updates related properties.
|
||||||
|
*/
|
||||||
|
public function testChangeId(): void {
|
||||||
|
/** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $display */
|
||||||
|
$display = $this->container->get(EntityDisplayRepositoryInterface::class)
|
||||||
|
->getViewDisplay('entity_test', 'entity_test');
|
||||||
|
$this->assertSame('entity_test.entity_test.default', $display->id());
|
||||||
|
$display->set('id', 'node.page.rss');
|
||||||
|
$this->assertSame('node', $display->getTargetEntityTypeId());
|
||||||
|
$this->assertSame('page', $display->getTargetBundle());
|
||||||
|
$this->assertSame('rss', $display->getMode());
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage("'a.b' is not a valid entity display ID.");
|
||||||
|
$display->set('id', 'a.b');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,7 @@ class EntityFormDisplayValidationTest extends ConfigEntityValidationTestBase {
|
||||||
*/
|
*/
|
||||||
public function testImmutableProperties(array $valid_values = []): void {
|
public function testImmutableProperties(array $valid_values = []): void {
|
||||||
parent::testImmutableProperties([
|
parent::testImmutableProperties([
|
||||||
|
'id' => 'entity_test_with_bundle.two.default',
|
||||||
'targetEntityType' => 'entity_test_with_bundle',
|
'targetEntityType' => 'entity_test_with_bundle',
|
||||||
'bundle' => 'two',
|
'bundle' => 'two',
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -92,6 +92,7 @@ class EntityViewDisplayValidationTest extends ConfigEntityValidationTestBase {
|
||||||
*/
|
*/
|
||||||
public function testImmutableProperties(array $valid_values = []): void {
|
public function testImmutableProperties(array $valid_values = []): void {
|
||||||
parent::testImmutableProperties([
|
parent::testImmutableProperties([
|
||||||
|
'id' => 'entity_test_with_bundle.two.full',
|
||||||
'targetEntityType' => 'entity_test_with_bundle',
|
'targetEntityType' => 'entity_test_with_bundle',
|
||||||
'bundle' => 'two',
|
'bundle' => 'two',
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -5,7 +5,10 @@ declare(strict_types=1);
|
||||||
namespace Drupal\KernelTests\Core\Recipe;
|
namespace Drupal\KernelTests\Core\Recipe;
|
||||||
|
|
||||||
use Drupal\Core\Config\Action\ConfigActionException;
|
use Drupal\Core\Config\Action\ConfigActionException;
|
||||||
|
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
|
||||||
|
use Drupal\Core\Extension\ModuleInstallerInterface;
|
||||||
use Drupal\KernelTests\KernelTestBase;
|
use Drupal\KernelTests\KernelTestBase;
|
||||||
|
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||||
use Drupal\Tests\user\Traits\UserCreationTrait;
|
use Drupal\Tests\user\Traits\UserCreationTrait;
|
||||||
use Drupal\user\Entity\Role;
|
use Drupal\user\Entity\Role;
|
||||||
|
|
||||||
|
@ -15,6 +18,7 @@ use Drupal\user\Entity\Role;
|
||||||
*/
|
*/
|
||||||
class EntityCloneConfigActionTest extends KernelTestBase {
|
class EntityCloneConfigActionTest extends KernelTestBase {
|
||||||
|
|
||||||
|
use ContentTypeCreationTrait;
|
||||||
use UserCreationTrait;
|
use UserCreationTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,4 +73,37 @@ class EntityCloneConfigActionTest extends KernelTestBase {
|
||||||
$this->assertFalse($clone->hasPermission('access user profiles'));
|
$this->assertFalse($clone->hasPermission('access user profiles'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests cloning entity displays, which have specialized logic for that.
|
||||||
|
*/
|
||||||
|
public function testCloneEntityDisplay(): void {
|
||||||
|
$this->container->get(ModuleInstallerInterface::class)->install(['node']);
|
||||||
|
$this->createContentType(['type' => 'alpha']);
|
||||||
|
$this->createContentType(['type' => 'beta']);
|
||||||
|
|
||||||
|
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
|
||||||
|
$display_repository = $this->container->get(EntityDisplayRepositoryInterface::class);
|
||||||
|
// Create the default view displays for each node type.
|
||||||
|
$display_repository->getViewDisplay('node', 'alpha')->save();
|
||||||
|
$display_repository->getViewDisplay('node', 'beta')->save();
|
||||||
|
|
||||||
|
// Ensure the `rss` displays don't exist yet.
|
||||||
|
$this->assertTrue($display_repository->getViewDisplay('node', 'alpha', 'rss')->isNew());
|
||||||
|
$this->assertTrue($display_repository->getViewDisplay('node', 'beta', 'rss')->isNew());
|
||||||
|
// Use the action to clone the default view displays to the `rss` view mode.
|
||||||
|
/** @var \Drupal\Core\Config\Action\ConfigActionManager $manager */
|
||||||
|
$manager = $this->container->get('plugin.manager.config_action');
|
||||||
|
$manager->applyAction('cloneAs', 'core.entity_view_display.node.alpha.default', 'node.alpha.rss');
|
||||||
|
$manager->applyAction('entity_method:core.entity_view_display:createCopy', 'core.entity_view_display.node.beta.default', 'rss');
|
||||||
|
$this->assertFalse($display_repository->getViewDisplay('node', 'alpha', 'rss')->isNew());
|
||||||
|
$this->assertFalse($display_repository->getViewDisplay('node', 'beta', 'rss')->isNew());
|
||||||
|
|
||||||
|
// Ensure that this also works with wildcards.
|
||||||
|
$this->assertTrue($display_repository->getViewDisplay('node', 'alpha', 'search_result')->isNew());
|
||||||
|
$this->assertTrue($display_repository->getViewDisplay('node', 'beta', 'search_result')->isNew());
|
||||||
|
$manager->applyAction('entity_method:core.entity_view_display:createCopy', 'core.entity_view_display.node.*.default', 'search_result');
|
||||||
|
$this->assertFalse($display_repository->getViewDisplay('node', 'alpha', 'search_result')->isNew());
|
||||||
|
$this->assertFalse($display_repository->getViewDisplay('node', 'beta', 'search_result')->isNew());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Drupal\Tests\Core\Config\Entity;
|
namespace Drupal\Tests\Core\Config\Entity;
|
||||||
|
|
||||||
use Drupal\Core\Entity\EntityDisplayBase;
|
use Drupal\Core\Entity\EntityDisplayBase;
|
||||||
|
use Drupal\Core\Entity\EntityType;
|
||||||
use Drupal\Tests\UnitTestCase;
|
use Drupal\Tests\UnitTestCase;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ class EntityDisplayBaseTest extends UnitTestCase {
|
||||||
*/
|
*/
|
||||||
protected function setUp(): void {
|
protected function setUp(): void {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->entityDisplay = $this->getMockBuilder(EntityDisplayBaseMockableClass::class)
|
$this->entityDisplay = $this->getMockBuilder(EntityDisplayBaseMockableClass::class)
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->onlyMethods([])
|
->onlyMethods([])
|
||||||
|
@ -91,4 +93,13 @@ class EntityDisplayBaseMockableClass extends EntityDisplayBase {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEntityType() {
|
||||||
|
return new EntityType([
|
||||||
|
'id' => 'entity_view_display',
|
||||||
|
'entity_keys' => [
|
||||||
|
'id' => 'id',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue