Unverified Commit ee0e4e06 authored by maks9889's avatar maks9889 Committed by heddn
Browse files

Issue #2974221 by joachim, maks9889, heddn: add a process plugin for extracting an entity value

parent 40b1c916
<?php
namespace Drupal\migrate_plus\Plugin\migrate\process;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\MigrateException;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Extracts a specified field's value from an entity.
*
* This considers the source value to be an entity ID, and returns a field
* value from that field. The type of the entity and the field name must be
* specified. Optionally, the field property should be specified if it is not
* the default of 'value'.
*
* For content entities, if a langcode is given, that translation is loaded.
* The langcode is a reference so can be dynamic.
*
* Example:
* @code
* process:
* field_foo:
* plugin: entity_value
* source: field_noderef/0/target_id
* entity_type: node
* langcode: @_langcode
* field_name: field_foo
* @endcode
*
* @MigrateProcessPlugin(
* id = "entity_value",
* )
*/
class EntityValue extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The Field Name.
*
* @var string
*/
protected $fieldName;
/**
* The langcode reference. False if not configured.
*
* @var string
*/
protected $langCodeRef;
/**
* The storage for the configured entity type.
*
* @var \Drupal\Core\Entity\EntityStorageInterface|\Drupal\Core\Entity\RevisionableStorageInterface
*/
protected $entityStorage;
/**
* Flag indicating whether there are multiple values.
*
* @var bool
*/
protected $multiple;
/**
* Creates a EntityValue instance.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \InvalidArgumentException
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
EntityTypeManagerInterface $entity_type_manager
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
if (empty($this->configuration['entity_type'])) {
throw new \InvalidArgumentException("'entity_type' configuration must be specified for the entity_value process plugin.");
}
$entity_type = $this->configuration['entity_type'];
$this->entityStorage = $this->entityTypeManager->getStorage($entity_type);
$this->langCodeRef = isset($this->configuration['langcode']) ? $this->configuration['langcode'] : NULL;
if (empty($this->configuration['field_name'])) {
throw new \InvalidArgumentException("'field_name' configuration must be specified for migrate_plus_entity_value process plugin.");
}
$this->fieldName = $this->configuration['field_name'];
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) {
$this->multiple = is_array($value);
if (!isset($value)) {
return [];
}
$ids = $this->multiple ? $value : [$value];
$entities = $this->loadEntities($ids);
$langcode = $this->langCodeRef;
$arrays = array_map(function (EntityInterface $entity) use ($langcode) {
if ($entity instanceof ContentEntityInterface) {
if ($langcode) {
$entity = $entity->getTranslation($langcode);
}
else {
$entity = $entity->getUntranslated();
}
}
else {
if ($langcode) {
throw new \InvalidArgumentException('Langcode can only be used with content entities currently.');
}
}
try {
return $entity->get($this->fieldName)->getValue();
}
catch (\Exception $e) {
// Re-throw any exception thrown by the entity system.
throw new MigrateException("Got exception reading field value {$this->fieldName} entity with ID {$entity->id()} in migrate_plus_entity_value process plugin:" . $e->getMessage());
}
}, $entities);
$return = $this->multiple ? array_values($arrays) : ($arrays ? reset($arrays) : []);
return $return;
}
/**
* {@inheritdoc}
*/
public function multiple() {
return $this->multiple;
}
/**
* Load entities.
*
* @param array $ids
* The entity IDs.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* The entities.
*/
protected function loadEntities(array $ids) {
$entities = $this->entityStorage->loadMultiple($ids);
return $entities;
}
}
<?php
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate\process;
use Drupal\KernelTests\KernelTestBase;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
use Drupal\node\Entity\Node;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the entity_value plugin.
*
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate\process\EntityValue
* @group migrate_drupal
*/
class EntityValueTest extends KernelTestBase {
/**
* The generated title.
*
* @var string
*/
protected $title;
/**
* The generated Spanish title.
*
* @var string
*/
protected $titleSpanish;
/**
* The generated node ID.
*
* @var int
*/
protected $uid;
/**
* The plugin to test.
*
* @var \Drupal\migrate_plus\Plugin\migrate\process\EntityValue
*/
protected $plugin;
/**
* {@inheritdoc}
*/
public static $modules = [
'migrate',
'migrate_plus',
'system',
'node',
'user',
'language',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
ConfigurableLanguage::createFromLangcode('es')->save();
$this->installSchema('system', ['sequences']);
$this->installSchema('node', 'node_access');
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->title = $this->randomString();
$this->titleSpanish = $this->randomString();
$node = Node::create([
'type' => 'page',
'title' => $this->title,
'langcode' => 'en',
]);
$node->save();
$node_es = $node->addTranslation('es');
$node_es->setTitle($this->titleSpanish);
$node_es->save();
$this->uid = $node->id();
}
/**
* Test the EntityLoad plugin succeeding.
*
* @covers ::transform
*/
public function testEntityValueSuccess() {
$this->plugin = \Drupal::service('plugin.manager.migrate.process')
->createInstance('entity_value', [
'entity_type' => 'node',
'field_name' => 'title',
]);
$executable = $this->prophesize(MigrateExecutableInterface::class)
->reveal();
$row = new Row();
// Ensure that the entity is returned if it really exists.
$value = $this->plugin->transform($this->uid, $executable, $row, 'dummmy');
$this->assertSame($this->title, $value[0]['value']);
$this->assertFalse($this->plugin->multiple());
// Ensure that an array of entities is returned.
$value = $this->plugin->transform([$this->uid], $executable, $row,
'dummmy');
$this->assertSame($this->title, $value[0][0]['value']);
$this->assertTrue($this->plugin->multiple());
// Ensure that the plugin returns [] if the entity doesn't exist.
$value = $this->plugin->transform(9999999, $executable, $row, 'dummmy');
$this->assertSame([], $value);
$this->assertFalse($this->plugin->multiple());
// Ensure that the plugin returns [] if NULL is passed.
$value = $this->plugin->transform(NULL, $executable, $row, 'dummmy');
$this->assertSame([], $value);
$this->assertFalse($this->plugin->multiple());
}
/**
* Test the EntityLoad plugin succeeding.
*
* @covers ::transform
*/
public function testEntityValueLangSuccess() {
$this->plugin = \Drupal::service('plugin.manager.migrate.process')
->createInstance('entity_value', [
'entity_type' => 'node',
'langcode' => 'es',
'field_name' => 'title',
]);
$executable = $this->prophesize(MigrateExecutableInterface::class)
->reveal();
$row = new Row();
// Ensure that the entity is returned if it really exists.
$value = $this->plugin->transform($this->uid, $executable, $row, 'dummmy');
$this->assertSame($this->titleSpanish, $value[0]['value']);
$this->assertFalse($this->plugin->multiple());
// Ensure that an array of entities is returned.
$value = $this->plugin->transform([$this->uid], $executable, $row,
'dummmy');
$this->assertSame($this->titleSpanish, $value[0][0]['value']);
$this->assertTrue($this->plugin->multiple());
}
/**
* Test the EntityLoad plugin throwing.
*
* @param mixed $config
* the Plugin Config.
*
* @covers ::__construct
* @dataProvider entityValueFailureConfigData
*/
public function testEntityValueConfig($config) {
$this->setExpectedException(\InvalidArgumentException::class);
$plugin = \Drupal::service('plugin.manager.migrate.process')
->createInstance('entity_value', $config);
}
/**
* Provides data for entityLoadFailureConfigData.
*
* @return array
* The data.
*/
public function entityValueFailureConfigData() {
return [
[
[
'entity_type' => '',
],
],
[
[
'entity_type' => NULL,
],
],
[
[
'entity_type' => 'node',
'source' => '',
],
],
[
[
'entity_type' => 'node',
'source' => NULL,
],
],
[
[
'entity_type' => 'node',
'source' => 'test',
'field_name' => '',
],
],
[
[
'entity_type' => 'node',
'source' => 'test',
'field_name' => NULL,
],
],
];
}
}
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