Skip to content
Snippets Groups Projects
Commit 617626af authored by catch's avatar catch
Browse files

Issue #3498915 by benjifisher, nicxvan, mondrake: Move content_entity source...

Issue #3498915 by benjifisher, nicxvan, mondrake: Move content_entity source plugin to migrate module
parent ba9b75f6
No related branches found
No related tags found
2 merge requests!11197Issue #3506427 by eduardo morales alberti: Remove responsive_image.ajax from hook,!10223132456: Fix issue where views instances are emptied before an ajax request is complete
Pipeline #413501 passed with warnings
Pipeline: drupal

#413502

    ......@@ -27739,6 +27739,12 @@
    'count' => 1,
    'path' => __DIR__ . '/modules/migrate/tests/src/Kernel/Plugin/id_map/SqlTest.php',
    ];
    $ignoreErrors[] = [
    'message' => '#^Method Drupal\\\\Tests\\\\migrate\\\\Kernel\\\\Plugin\\\\source\\\\ContentEntityTest\\:\\:createEntityReferenceField\\(\\) has no return type specified\\.$#',
    'identifier' => 'missingType.return',
    'count' => 1,
    'path' => __DIR__ . '/modules/migrate/tests/src/Kernel/Plugin/source/ContentEntityTest.php',
    ];
    $ignoreErrors[] = [
    'message' => '#^Method Drupal\\\\Tests\\\\migrate\\\\Kernel\\\\QueryBatchTest\\:\\:queryDataProvider\\(\\) has no return type specified\\.$#',
    'identifier' => 'missingType.return',
    ......@@ -28249,18 +28255,6 @@
    'count' => 1,
    'path' => __DIR__ . '/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/ContentEntityConstructorTest.php',
    ];
    $ignoreErrors[] = [
    'message' => '#^Method Drupal\\\\Tests\\\\migrate_drupal\\\\Kernel\\\\Plugin\\\\migrate\\\\source\\\\ContentEntityTest\\:\\:createEntityReferenceField\\(\\) has no return type specified\\.$#',
    'identifier' => 'missingType.return',
    'count' => 1,
    'path' => __DIR__ . '/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/ContentEntityTest.php',
    ];
    $ignoreErrors[] = [
    'message' => '#^Method Drupal\\\\Tests\\\\migrate_drupal\\\\Kernel\\\\Plugin\\\\migrate\\\\source\\\\ContentEntityTest\\:\\:migrationConfigurationProvider\\(\\) has no return type specified\\.$#',
    'identifier' => 'missingType.return',
    'count' => 1,
    'path' => __DIR__ . '/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/ContentEntityTest.php',
    ];
    $ignoreErrors[] = [
    'message' => '#^Method Drupal\\\\Tests\\\\migrate_drupal\\\\Kernel\\\\StateFileExistsTest\\:\\:createDatabaseStateSettings\\(\\) has no return type specified\\.$#',
    'identifier' => 'missingType.return',
    <?php
    namespace Drupal\migrate\Plugin\migrate\source;
    use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
    use Drupal\Core\Entity\ContentEntityInterface;
    use Drupal\Core\Entity\ContentEntityTypeInterface;
    use Drupal\Core\Entity\EntityFieldManagerInterface;
    use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
    use Drupal\Core\Entity\EntityTypeManagerInterface;
    use Drupal\Core\Entity\Query\QueryInterface;
    use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
    use Drupal\migrate\EntityFieldDefinitionTrait;
    use Drupal\migrate\Plugin\MigrateSourceInterface;
    use Drupal\migrate\Plugin\MigrationInterface;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    /**
    * Source plugin to get content entities from the current version of Drupal.
    *
    * This plugin uses the Entity API to export entity data. If the source entity
    * type has custom field storage fields or computed fields, this class will need
    * to be extended and the new class will need to load/calculate the values for
    * those fields.
    *
    * Available configuration keys:
    * - entity_type: The entity type ID of the entities being exported. This is
    * calculated dynamically by the deriver so it is only needed if the deriver
    * is not utilized, i.e., a custom source plugin.
    * - bundle: (optional) If the entity type is bundleable, only return entities
    * of this bundle.
    * - include_translations: (optional) Indicates if the entity translations
    * should be included, defaults to TRUE.
    * - add_revision_id: (optional) Indicates if the revision key is added to the
    * source IDs, defaults to TRUE.
    *
    * Examples:
    *
    * This will return the default revision for all nodes, from every bundle and
    * every translation. The revision key is added to the source IDs.
    * @code
    * source:
    * plugin: content_entity:node
    * @endcode
    *
    * This will return the default revision for all nodes, from every bundle and
    * every translation. The revision key is not added to the source IDs.
    * @code
    * source:
    * plugin: content_entity:node
    * add_revision_id: false
    * @endcode
    *
    * This will only return nodes of type 'article' in their default language.
    * @code
    * source:
    * plugin: content_entity:node
    * bundle: article
    * include_translations: false
    * @endcode
    *
    * For additional configuration keys, refer to the parent class:
    * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
    *
    * @MigrateSource(
    * id = "content_entity",
    * source_module = "migrate",
    * deriver = "\Drupal\migrate\Plugin\migrate\source\ContentEntityDeriver",
    * )
    */
    class ContentEntity extends SourcePluginBase implements ContainerFactoryPluginInterface {
    use EntityFieldDefinitionTrait;
    /**
    * The entity type definition.
    *
    * @var \Drupal\Core\Entity\EntityTypeInterface
    */
    protected $entityType;
    /**
    * The plugin's default configuration.
    *
    * @var array
    */
    protected array $defaultConfiguration = [
    'bundle' => NULL,
    'include_translations' => TRUE,
    'add_revision_id' => TRUE,
    ];
    /**
    * {@inheritdoc}
    */
    public function __construct(
    array $configuration,
    string $plugin_id,
    array $plugin_definition,
    MigrationInterface $migration,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected EntityFieldManagerInterface $entityFieldManager,
    protected EntityTypeBundleInfoInterface $entityTypeBundleInfo,
    ) {
    if (empty($plugin_definition['entity_type'])) {
    throw new InvalidPluginDefinitionException($plugin_id, 'Missing required "entity_type" definition.');
    }
    $this->entityType = $this->entityTypeManager->getDefinition($plugin_definition['entity_type']);
    if (!$this->entityType instanceof ContentEntityTypeInterface) {
    throw new InvalidPluginDefinitionException($plugin_id, sprintf('The entity type (%s) is not supported. The "content_entity" source plugin only supports content entities.', $plugin_definition['entity_type']));
    }
    if (!empty($configuration['bundle'])) {
    if (!$this->entityType->hasKey('bundle')) {
    throw new \InvalidArgumentException(sprintf('A bundle was provided but the entity type (%s) is not bundleable.', $plugin_definition['entity_type']));
    }
    $bundle_info = array_keys($this->entityTypeBundleInfo->getBundleInfo($this->entityType->id()));
    if (!in_array($configuration['bundle'], $bundle_info, TRUE)) {
    throw new \InvalidArgumentException(sprintf('The provided bundle (%s) is not valid for the (%s) entity type.', $configuration['bundle'], $plugin_definition['entity_type']));
    }
    }
    parent::__construct($configuration + $this->defaultConfiguration, $plugin_id, $plugin_definition, $migration);
    }
    /**
    * {@inheritdoc}
    */
    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, ?MigrationInterface $migration = NULL): static {
    return new static(
    $configuration,
    $plugin_id,
    $plugin_definition,
    $migration,
    $container->get('entity_type.manager'),
    $container->get('entity_field.manager'),
    $container->get('entity_type.bundle.info')
    );
    }
    /**
    * {@inheritdoc}
    */
    public function __toString() {
    return (string) $this->entityType->getPluralLabel();
    }
    /**
    * Initializes the iterator with the source data.
    *
    * @return \Generator
    * A data generator for this source.
    */
    protected function initializeIterator(): \Generator {
    $ids = $this->query()->execute();
    return $this->yieldEntities($ids);
    }
    /**
    * Loads and yields entities, one at a time.
    *
    * @param array $ids
    * The entity IDs.
    *
    * @return \Generator
    * An iterable of the loaded entities.
    */
    protected function yieldEntities(array $ids): \Generator {
    $storage = $this->entityTypeManager
    ->getStorage($this->entityType->id());
    foreach ($ids as $id) {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $storage->load($id);
    yield $this->toArray($entity);
    if ($this->configuration['include_translations']) {
    foreach ($entity->getTranslationLanguages(FALSE) as $language) {
    yield $this->toArray($entity->getTranslation($language->getId()));
    }
    }
    }
    }
    /**
    * Converts an entity to an array.
    *
    * Makes all IDs into flat values. All other values are returned as per
    * $entity->toArray(), which is a nested array.
    *
    * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    * The entity to convert.
    *
    * @return array
    * The entity, represented as an array.
    */
    protected function toArray(ContentEntityInterface $entity): array {
    $return = $entity->toArray();
    // This is necessary because the IDs must be flat. They cannot be nested for
    // the ID map.
    foreach (array_keys($this->getIds()) as $id) {
    /** @var \Drupal\Core\TypedData\Plugin\DataType\ItemList $value */
    $value = $entity->get($id);
    // Force the IDs on top of the previous values.
    $return[$id] = $value->first()->getString();
    }
    return $return;
    }
    /**
    * Query to retrieve the entities.
    *
    * @return \Drupal\Core\Entity\Query\QueryInterface
    * The query.
    */
    public function query(): QueryInterface {
    $query = $this->entityTypeManager
    ->getStorage($this->entityType->id())
    ->getQuery()
    ->accessCheck(FALSE);
    if (!empty($this->configuration['bundle'])) {
    $query->condition($this->entityType->getKey('bundle'), $this->configuration['bundle']);
    }
    // Exclude anonymous user account.
    if ($this->entityType->id() === 'user' && !empty($this->entityType->getKey('id'))) {
    $query->condition($this->entityType->getKey('id'), 0, '>');
    }
    return $query;
    }
    /**
    * {@inheritdoc}
    */
    public function count($refresh = FALSE): int {
    // If no translations are included, then a simple query is possible.
    if (!$this->configuration['include_translations']) {
    return parent::count($refresh);
    }
    // @todo Determine a better way to retrieve a valid count for translations.
    // https://www.drupal.org/project/drupal/issues/2937166
    return MigrateSourceInterface::NOT_COUNTABLE;
    }
    /**
    * {@inheritdoc}
    */
    protected function doCount(): int {
    return $this->query()->count()->execute();
    }
    /**
    * {@inheritdoc}
    */
    public function fields(): array {
    // Retrieving fields from a non-fieldable content entity will throw a
    // LogicException. Return an empty list of fields instead.
    if (!$this->entityType->entityClassImplements('Drupal\Core\Entity\FieldableEntityInterface')) {
    return [];
    }
    $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($this->entityType->id());
    if (!empty($this->configuration['bundle'])) {
    $field_definitions += $this->entityFieldManager->getFieldDefinitions($this->entityType->id(), $this->configuration['bundle']);
    }
    $fields = array_map(function ($definition) {
    return (string) $definition->getLabel();
    }, $field_definitions);
    return $fields;
    }
    /**
    * {@inheritdoc}
    */
    public function getIds(): array {
    $id_key = $this->entityType->getKey('id');
    $ids[$id_key] = $this->getDefinitionFromEntity($id_key);
    if ($this->configuration['add_revision_id'] && $this->entityType->isRevisionable()) {
    $revision_key = $this->entityType->getKey('revision');
    $ids[$revision_key] = $this->getDefinitionFromEntity($revision_key);
    }
    if ($this->entityType->isTranslatable()) {
    $langcode_key = $this->entityType->getKey('langcode');
    $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
    }
    return $ids;
    }
    }
    <?php
    namespace Drupal\migrate\Plugin\migrate\source;
    use Drupal\Component\Plugin\Derivative\DeriverBase;
    use Drupal\Core\Entity\ContentEntityTypeInterface;
    use Drupal\Core\Entity\EntityTypeManagerInterface;
    use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    /**
    * Deriver for content entity source plugins.
    */
    class ContentEntityDeriver extends DeriverBase implements ContainerDeriverInterface {
    /**
    * Constructs a new ContentEntityDeriver.
    *
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
    * The entity type manager.
    */
    public function __construct(
    protected EntityTypeManagerInterface $entityTypeManager,
    ) {}
    /**
    * {@inheritdoc}
    */
    public static function create(ContainerInterface $container, $base_plugin_id): static {
    return new static(
    $container->get('entity_type.manager'),
    );
    }
    /**
    * {@inheritdoc}
    */
    public function getDerivativeDefinitions($base_plugin_definition): ?array {
    $this->derivatives = [];
    foreach ($this->entityTypeManager->getDefinitions() as $id => $definition) {
    if ($definition instanceof ContentEntityTypeInterface) {
    $this->derivatives[$id] = $base_plugin_definition;
    // Provide entity_type so the source can be used apart from a deriver.
    $this->derivatives[$id]['entity_type'] = $id;
    }
    }
    return parent::getDerivativeDefinitions($base_plugin_definition);
    }
    }
    <?php
    declare(strict_types=1);
    namespace Drupal\Tests\migrate\Kernel\Plugin\source;
    use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
    use Drupal\KernelTests\KernelTestBase;
    use Drupal\migrate\Plugin\MigrationInterface;
    use Drupal\migrate\Plugin\migrate\source\ContentEntity;
    /**
    * Tests the constructor of the entity content source plugin.
    *
    * @group migrate
    */
    class ContentEntityConstructorTest extends KernelTestBase {
    /**
    * {@inheritdoc}
    */
    protected static $modules = [
    'migrate',
    'node',
    'system',
    'user',
    ];
    /**
    * Tests the constructor.
    *
    * @dataProvider providerTestConstructor
    */
    public function testConstructor(array $configuration, array $plugin_definition, string $exception_class, string $expected): void {
    $migration = $this->prophesize(MigrationInterface::class)->reveal();
    $this->expectException($exception_class);
    $this->expectExceptionMessage($expected);
    ContentEntity::create($this->container, $configuration, 'content_entity', $plugin_definition, $migration);
    }
    /**
    * Provides data for constructor tests.
    */
    public static function providerTestConstructor(): array {
    return [
    'entity type missing' => [
    [],
    ['entity_type' => ''],
    InvalidPluginDefinitionException::class,
    'Missing required "entity_type" definition.',
    ],
    'non content entity' => [
    [],
    ['entity_type' => 'node_type'],
    InvalidPluginDefinitionException::class,
    'The entity type (node_type) is not supported. The "content_entity" source plugin only supports content entities.',
    ],
    'not bundleable' => [
    ['bundle' => 'foo'],
    ['entity_type' => 'user'],
    \InvalidArgumentException::class,
    'A bundle was provided but the entity type (user) is not bundleable.',
    ],
    'invalid bundle' => [
    ['bundle' => 'foo'],
    ['entity_type' => 'node'],
    \InvalidArgumentException::class,
    'The provided bundle (foo) is not valid for the (node) entity type.',
    ],
    ];
    }
    }
    ......@@ -2,7 +2,7 @@
    declare(strict_types=1);
    namespace Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source;
    namespace Drupal\Tests\migrate\Kernel\Plugin\source;
    use Drupal\Component\Plugin\PluginBase;
    use Drupal\Core\Field\FieldStorageDefinitionInterface;
    ......@@ -23,7 +23,7 @@
    /**
    * Tests the entity content source plugin.
    *
    * @group migrate_drupal
    * @group migrate
    * @group #slow
    */
    class ContentEntityTest extends KernelTestBase {
    ......@@ -37,7 +37,6 @@ class ContentEntityTest extends KernelTestBase {
    protected static $modules = [
    'user',
    'migrate',
    'migrate_drupal',
    'system',
    'node',
    'taxonomy',
    ......@@ -428,13 +427,13 @@ public function testTermSource(array $configuration): void {
    /**
    * Data provider for several test methods.
    *
    * @see \Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\ContentEntityTest::testUserSource
    * @see \Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\ContentEntityTest::testFileSource
    * @see \Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\ContentEntityTest::testNodeSource
    * @see \Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\ContentEntityTest::testMediaSource
    * @see \Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\ContentEntityTest::testTermSource
    * @see \Drupal\Tests\migrate\Kernel\Plugin\source\ContentEntityTest::testUserSource
    * @see \Drupal\Tests\migrate\Kernel\Plugin\source\ContentEntityTest::testFileSource
    * @see \Drupal\Tests\migrate\Kernel\Plugin\source\ContentEntityTest::testNodeSource
    * @see \Drupal\Tests\migrate\Kernel\Plugin\source\ContentEntityTest::testMediaSource
    * @see \Drupal\Tests\migrate\Kernel\Plugin\source\ContentEntityTest::testTermSource
    */
    public static function migrationConfigurationProvider() {
    public static function migrationConfigurationProvider(): array {
    $data = [];
    foreach ([FALSE, TRUE] as $include_translations) {
    foreach ([FALSE, TRUE] as $add_revision_id) {
    ......@@ -460,7 +459,7 @@ public static function migrationConfigurationProvider() {
    * @return array
    * The definition.
    */
    protected function migrationDefinition($plugin_id, array $configuration = []): array {
    protected function migrationDefinition(string $plugin_id, array $configuration = []): array {
    return [
    'source' => [
    'plugin' => $plugin_id,
    ......
    ......@@ -18,6 +18,11 @@
    /**
    * Source plugin to get content entities from the current version of Drupal.
    *
    * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
    * \Drupal\migrate\Plugin\migrate\source\ContentEntity instead.
    *
    * @see https://www.drupal.org/node/3498916
    *
    * This plugin uses the Entity API to export entity data. If the source entity
    * type has custom field storage fields or computed fields, this class will need
    * to be extended and the new class will need to load/calculate the values for
    ......@@ -61,12 +66,6 @@
    *
    * For additional configuration keys, refer to the parent class:
    * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
    *
    * @MigrateSource(
    * id = "content_entity",
    * source_module = "migrate_drupal",
    * deriver = "\Drupal\migrate_drupal\Plugin\migrate\source\ContentEntityDeriver",
    * )
    */
    class ContentEntity extends SourcePluginBase implements ContainerFactoryPluginInterface {
    use EntityFieldDefinitionTrait;
    ......@@ -114,6 +113,8 @@ class ContentEntity extends SourcePluginBase implements ContainerFactoryPluginIn
    * {@inheritdoc}
    */
    public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
    @trigger_error(__CLASS__ . ' is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use \Drupal\migrate\Plugin\migrate\source\ContentEntity instead. See https://www.drupal.org/node/3498916', E_USER_DEPRECATED);
    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
    if (empty($plugin_definition['entity_type'])) {
    throw new InvalidPluginDefinitionException($plugin_id, 'Missing required "entity_type" definition.');
    }
    ......
    ......@@ -10,6 +10,11 @@
    /**
    * Deriver for content entity source plugins.
    *
    * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
    * \Drupal\migrate\Plugin\migrate\source\ContentEntityDeriver instead.
    *
    * @see https://www.drupal.org/node/3498916
    */
    class ContentEntityDeriver extends DeriverBase implements ContainerDeriverInterface {
    ......@@ -29,6 +34,7 @@ class ContentEntityDeriver extends DeriverBase implements ContainerDeriverInterf
    * The entity type manager.
    */
    public function __construct($base_plugin_id, EntityTypeManagerInterface $entityTypeManager) {
    @trigger_error(__CLASS__ . ' is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use \Drupal\migrate\Plugin\migrate\source\ContentEntity instead. See https://www.drupal.org/node/3498916', E_USER_DEPRECATED);
    $this->entityTypeManager = $entityTypeManager;
    }
    ......
    ......@@ -12,6 +12,7 @@
    /**
    * Tests the constructor of the entity content source plugin.
    *
    * @group legacy
    * @group migrate_drupal
    */
    class ContentEntityConstructorTest extends KernelTestBase {
    ......
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment