3 namespace Drupal\migrate_plus\Plugin\migrate\process;
5 use Drupal\Core\Config\Entity\ConfigEntityInterface;
6 use Drupal\Core\Entity\EntityManagerInterface;
7 use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
8 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
9 use Drupal\migrate\Plugin\MigrationInterface;
10 use Drupal\migrate\MigrateException;
11 use Drupal\migrate\MigrateExecutableInterface;
12 use Drupal\migrate\ProcessPluginBase;
13 use Drupal\migrate\Row;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
17 * This plugin looks for existing entities.
19 * @MigrateProcessPlugin(
20 * id = "entity_lookup",
21 * handle_multiples = TRUE
24 * In its most simple form, this plugin needs no configuration. However, if the
25 * lookup properties cannot be determined through introspection, define them via
28 * Example usage with minimal configuration:
31 * plugin: 'entity:node'
34 * plugin: default_value
37 * plugin: entity_lookup
41 * Example usage with full configuration:
44 * plugin: entity_lookup
49 * entity_type: taxonomy_term
53 class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginInterface {
58 * @var \Drupal\Core\Entity\EntityManagerInterface
60 protected $entityManager;
65 * @var \Drupal\migrate\Plugin\MigrationInterface
70 * The selection plugin.
72 * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
74 protected $selectionPluginManager;
77 * The destination type.
81 protected $destinationEntityType;
84 * The destination bundle.
88 protected $destinationBundleKey;
91 * The lookup value's key.
95 protected $lookupValueKey;
98 * The lookup bundle's key.
102 protected $lookupBundleKey;
109 protected $lookupBundle;
112 * The lookup entity type.
116 protected $lookupEntityType;
119 * The destination property or field.
123 protected $destinationProperty;
128 public function __construct(array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration, EntityManagerInterface $entityManager, SelectionPluginManagerInterface $selectionPluginManager) {
129 parent::__construct($configuration, $pluginId, $pluginDefinition);
130 $this->migration = $migration;
131 $this->entityManager = $entityManager;
132 $this->selectionPluginManager = $selectionPluginManager;
133 $pluginIdParts = explode(':', $this->migration->getDestinationPlugin()->getPluginId());
134 $this->destinationEntityType = empty($pluginIdParts[1]) ?: $pluginIdParts[1];
135 $this->destinationBundleKey = !$this->destinationEntityType ?: $this->entityManager->getDefinition($this->destinationEntityType)->getKey('bundle');
141 public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration = NULL) {
147 $container->get('entity.manager'),
148 $container->get('plugin.manager.entity_reference_selection')
155 public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) {
156 // If the source data is an empty array, return the same.
157 if (gettype($value) === 'array' && count($value) === 0) {
161 // In case of subfields ('field_reference/target_id'), extract the field
163 $parts = explode('/', $destinationProperty);
164 $destinationProperty = reset($parts);
165 $this->determineLookupProperties($destinationProperty);
167 $this->destinationProperty = isset($this->configuration['destination_field']) ? $this->configuration['destination_field'] : NULL;
169 return $this->query($value);
173 * Determine the lookup properties from config or target field configuration.
175 * @param string $destinationProperty
176 * The destination property currently worked on. This is only used together
177 * with the $row above.
179 protected function determineLookupProperties($destinationProperty) {
180 if (!empty($this->configuration['value_key'])) {
181 $this->lookupValueKey = $this->configuration['value_key'];
183 if (!empty($this->configuration['bundle_key'])) {
184 $this->lookupBundleKey = $this->configuration['bundle_key'];
186 if (!empty($this->configuration['bundle'])) {
187 $this->lookupBundle = $this->configuration['bundle'];
189 if (!empty($this->configuration['entity_type'])) {
190 $this->lookupEntityType = $this->configuration['entity_type'];
193 if (empty($this->lookupValueKey) || empty($this->lookupBundleKey) || empty($this->lookupBundle) || empty($this->lookupEntityType)) {
194 // See if we can introspect the lookup properties from destination field.
195 if (!empty($this->migration->getProcess()[$this->destinationBundleKey][0]['default_value'])) {
196 $destinationEntityBundle = $this->migration->getProcess()[$this->destinationBundleKey][0]['default_value'];
197 $fieldConfig = $this->entityManager->getFieldDefinitions($this->destinationEntityType, $destinationEntityBundle)[$destinationProperty]->getConfig($destinationEntityBundle);
198 switch ($fieldConfig->getType()) {
199 case 'entity_reference':
200 if (empty($this->lookupBundle)) {
201 $handlerSettings = $fieldConfig->getSetting('handler_settings');
202 $bundles = array_filter((array) $handlerSettings['target_bundles']);
203 if (count($bundles) == 1) {
204 $this->lookupBundle = reset($bundles);
206 // This was added in 8.1.x is not supported in 8.0.x.
207 elseif (!empty($handlerSettings['auto_create']) && !empty($handlerSettings['auto_create_bundle'])) {
208 $this->lookupBundle = reset($handlerSettings['auto_create_bundle']);
212 // Make an assumption that if the selection handler can target more
213 // than one type of entity that we will use the first entity type.
214 $this->lookupEntityType = $this->lookupEntityType ?: reset($this->selectionPluginManager->createInstance($fieldConfig->getSetting('handler'))->getPluginDefinition()['entity_types']);
215 $this->lookupValueKey = $this->lookupValueKey ?: $this->entityManager->getDefinition($this->lookupEntityType)->getKey('label');
216 $this->lookupBundleKey = $this->lookupBundleKey ?: $this->entityManager->getDefinition($this->lookupEntityType)->getKey('bundle');
221 $this->lookupEntityType = 'file';
222 $this->lookupValueKey = $this->lookupValueKey ?: 'uri';
226 throw new MigrateException('Destination field type ' .
227 $fieldConfig->getType() . 'is not a recognized reference type.');
232 // If there aren't enough lookup properties available by now, then bail.
233 if (empty($this->lookupValueKey)) {
234 throw new MigrateException('The entity_lookup plugin requires a value_key, none located.');
236 if (!empty($this->lookupBundleKey) && empty($this->lookupBundle)) {
237 throw new MigrateException('The entity_lookup plugin found no bundle but destination entity requires one.');
239 if (empty($this->lookupEntityType)) {
240 throw new MigrateException('The entity_lookup plugin requires a entity_type, none located.');
245 * Checks for the existence of some value.
247 * @param mixed $value
248 * The value to query.
251 * Entity id if the queried entity exists. Otherwise NULL.
253 protected function query($value) {
254 // Entity queries typically are case-insensitive. Therefore, we need to
255 // handle case sensitive filtering as a post-query step. By default, it
256 // filters case insensitive. Change to true if that is not the desired
258 $ignoreCase = !empty($this->configuration['ignore_case']) ?: FALSE;
260 $multiple = is_array($value);
262 $query = $this->entityManager->getStorage($this->lookupEntityType)
264 ->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL);
266 if ($this->lookupBundleKey) {
267 $query->condition($this->lookupBundleKey, $this->lookupBundle);
269 $results = $query->execute();
271 if (empty($results)) {
275 // By default do a case-sensitive comparison.
277 // Returns the entity's identifier.
278 foreach ($results as $k => $identifier) {
279 $entity = $this->entityManager->getStorage($this->lookupEntityType)->load($identifier);
280 $result_value = $entity instanceof ConfigEntityInterface ? $entity->get($this->lookupValueKey) : $entity->get($this->lookupValueKey)->value;
281 if (($multiple && !in_array($result_value, $value, TRUE)) || (!$multiple && $result_value !== $value)) {
287 if ($multiple && !empty($this->destinationProperty)) {
288 array_walk($results, function (&$value) {
289 $value = [$this->destinationProperty => $value];
293 return $multiple ? array_values($results) : reset($results);