Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / migrate_plus / src / Plugin / migrate / process / EntityLookup.php
1 <?php
2
3 namespace Drupal\migrate_plus\Plugin\migrate\process;
4
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;
15
16 /**
17  * This plugin looks for existing entities.
18  *
19  * @MigrateProcessPlugin(
20  *   id = "entity_lookup",
21  *   handle_multiples = TRUE
22  * )
23  *
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
26  * configuration.
27  *
28  * Example usage with minimal configuration:
29  * @code
30  * destination:
31  *   plugin: 'entity:node'
32  * process:
33  *   type:
34  *     plugin: default_value
35  *     default_value: page
36  *   field_tags:
37  *     plugin: entity_lookup
38  *     source: tags
39  * @endcode
40  *
41  * Example usage with full configuration:
42  * @code
43  *   field_tags:
44  *     plugin: entity_lookup
45  *     source: tags
46  *     value_key: name
47  *     bundle_key: vid
48  *     bundle: tags
49  *     entity_type: taxonomy_term
50  *     ignore_case: true
51  * @endcode
52  */
53 class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginInterface {
54
55   /**
56    * The entity manager.
57    *
58    * @var \Drupal\Core\Entity\EntityManagerInterface
59    */
60   protected $entityManager;
61
62   /**
63    * The migration.
64    *
65    * @var \Drupal\migrate\Plugin\MigrationInterface
66    */
67   protected $migration;
68
69   /**
70    * The selection plugin.
71    *
72    * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
73    */
74   protected $selectionPluginManager;
75
76   /**
77    * The destination type.
78    *
79    * @var string
80    */
81   protected $destinationEntityType;
82
83   /**
84    * The destination bundle.
85    *
86    * @var string|bool
87    */
88   protected $destinationBundleKey;
89
90   /**
91    * The lookup value's key.
92    *
93    * @var string
94    */
95   protected $lookupValueKey;
96
97   /**
98    * The lookup bundle's key.
99    *
100    * @var string
101    */
102   protected $lookupBundleKey;
103
104   /**
105    * The lookup bundle.
106    *
107    * @var string
108    */
109   protected $lookupBundle;
110
111   /**
112    * The lookup entity type.
113    *
114    * @var string
115    */
116   protected $lookupEntityType;
117
118   /**
119    * The destination property or field.
120    *
121    * @var string
122    */
123   protected $destinationProperty;
124
125   /**
126    * {@inheritdoc}
127    */
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');
136   }
137
138   /**
139    * {@inheritdoc}
140    */
141   public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration = NULL) {
142     return new static(
143       $configuration,
144       $pluginId,
145       $pluginDefinition,
146       $migration,
147       $container->get('entity.manager'),
148       $container->get('plugin.manager.entity_reference_selection')
149     );
150   }
151
152   /**
153    * {@inheritdoc}
154    */
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) {
158       return [];
159     }
160
161     // In case of subfields ('field_reference/target_id'), extract the field
162     // name only.
163     $parts = explode('/', $destinationProperty);
164     $destinationProperty = reset($parts);
165     $this->determineLookupProperties($destinationProperty);
166
167     $this->destinationProperty = isset($this->configuration['destination_field']) ? $this->configuration['destination_field'] : NULL;
168
169     return $this->query($value);
170   }
171
172   /**
173    * Determine the lookup properties from config or target field configuration.
174    *
175    * @param string $destinationProperty
176    *   The destination property currently worked on. This is only used together
177    *   with the $row above.
178    */
179   protected function determineLookupProperties($destinationProperty) {
180     if (!empty($this->configuration['value_key'])) {
181       $this->lookupValueKey = $this->configuration['value_key'];
182     }
183     if (!empty($this->configuration['bundle_key'])) {
184       $this->lookupBundleKey = $this->configuration['bundle_key'];
185     }
186     if (!empty($this->configuration['bundle'])) {
187       $this->lookupBundle = $this->configuration['bundle'];
188     }
189     if (!empty($this->configuration['entity_type'])) {
190       $this->lookupEntityType = $this->configuration['entity_type'];
191     }
192
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);
205               }
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']);
209               }
210             }
211
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');
217             break;
218
219           case 'file':
220           case 'image':
221             $this->lookupEntityType = 'file';
222             $this->lookupValueKey = $this->lookupValueKey ?: 'uri';
223             break;
224
225           default:
226             throw new MigrateException('Destination field type ' .
227               $fieldConfig->getType() . 'is not a recognized reference type.');
228         }
229       }
230     }
231
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.');
235     }
236     if (!empty($this->lookupBundleKey) && empty($this->lookupBundle)) {
237       throw new MigrateException('The entity_lookup plugin found no bundle but destination entity requires one.');
238     }
239     if (empty($this->lookupEntityType)) {
240       throw new MigrateException('The entity_lookup plugin requires a entity_type, none located.');
241     }
242   }
243
244   /**
245    * Checks for the existence of some value.
246    *
247    * @param mixed $value
248    *   The value to query.
249    *
250    * @return mixed|null
251    *   Entity id if the queried entity exists. Otherwise NULL.
252    */
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
257     // outcome.
258     $ignoreCase = !empty($this->configuration['ignore_case']) ?: FALSE;
259
260     $multiple = is_array($value);
261
262     $query = $this->entityManager->getStorage($this->lookupEntityType)
263       ->getQuery()
264       ->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL);
265
266     if ($this->lookupBundleKey) {
267       $query->condition($this->lookupBundleKey, $this->lookupBundle);
268     }
269     $results = $query->execute();
270
271     if (empty($results)) {
272       return NULL;
273     }
274
275     // By default do a case-sensitive comparison.
276     if (!$ignoreCase) {
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)) {
282           unset($results[$k]);
283         }
284       }
285     }
286
287     if ($multiple && !empty($this->destinationProperty)) {
288       array_walk($results, function (&$value) {
289         $value = [$this->destinationProperty => $value];
290       });
291     }
292
293     return $multiple ? array_values($results) : reset($results);
294   }
295
296 }