Updated to Drupal 8.5. Core Media not yet in use.
[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\Entity\EntityManagerInterface;
6 use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
7 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
8 use Drupal\migrate\Plugin\MigrationInterface;
9 use Drupal\migrate\MigrateException;
10 use Drupal\migrate\MigrateExecutableInterface;
11 use Drupal\migrate\ProcessPluginBase;
12 use Drupal\migrate\Row;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14
15 /**
16  * This plugin looks for existing entities.
17  *
18  * @MigrateProcessPlugin(
19  *   id = "entity_lookup",
20  *   handle_multiples = TRUE
21  * )
22  *
23  * In its most simple form, this plugin needs no configuration. However, if the
24  * lookup properties cannot be determined through introspection, define them via
25  * configuration.
26  *
27  * Example usage with minimal configuration:
28  * @code
29  * destination:
30  *   plugin: 'entity:node'
31  * process:
32  *   type:
33  *     plugin: default_value
34  *     default_value: page
35  *   field_tags:
36  *     plugin: entity_lookup
37  *     source: tags
38  * @endcode
39  *
40  * Example usage with full configuration:
41  * @code
42  *   field_tags:
43  *     plugin: entity_lookup
44  *     source: tags
45  *     value_key: name
46  *     bundle_key: vid
47  *     bundle: tags
48  *     entity_type: taxonomy_term
49  *     ignore_case: true
50  * @endcode
51  */
52 class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginInterface {
53
54   /**
55    * The entity manager.
56    *
57    * @var \Drupal\Core\Entity\EntityManagerInterface
58    */
59   protected $entityManager;
60
61   /**
62    * The migration.
63    *
64    * @var \Drupal\migrate\Plugin\MigrationInterface
65    */
66   protected $migration;
67
68   /**
69    * The selection plugin.
70    *
71    * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
72    */
73   protected $selectionPluginManager;
74
75   /**
76    * The destination type.
77    *
78    * @var string
79    */
80   protected $destinationEntityType;
81
82   /**
83    * The destination bundle.
84    *
85    * @var string|bool
86    */
87   protected $destinationBundleKey;
88
89   /**
90    * The lookup value's key.
91    *
92    * @var string
93    */
94   protected $lookupValueKey;
95
96   /**
97    * The lookup bundle's key.
98    *
99    * @var string
100    */
101   protected $lookupBundleKey;
102
103   /**
104    * The lookup bundle.
105    *
106    * @var string
107    */
108   protected $lookupBundle;
109
110   /**
111    * The lookup entity type.
112    *
113    * @var string
114    */
115   protected $lookupEntityType;
116
117   /**
118    * The destination property or field.
119    *
120    * @var string
121    */
122   protected $destinationProperty;
123
124   /**
125    * {@inheritdoc}
126    */
127   public function __construct(array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration, EntityManagerInterface $entityManager, SelectionPluginManagerInterface $selectionPluginManager) {
128     parent::__construct($configuration, $pluginId, $pluginDefinition);
129     $this->migration = $migration;
130     $this->entityManager = $entityManager;
131     $this->selectionPluginManager = $selectionPluginManager;
132     $pluginIdParts = explode(':', $this->migration->getDestinationPlugin()->getPluginId());
133     $this->destinationEntityType = empty($pluginIdParts[1]) ?: $pluginIdParts[1];
134     $this->destinationBundleKey = !$this->destinationEntityType ?: $this->entityManager->getDefinition($this->destinationEntityType)->getKey('bundle');
135   }
136
137   /**
138    * {@inheritdoc}
139    */
140   public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration = NULL) {
141     return new static(
142       $configuration,
143       $pluginId,
144       $pluginDefinition,
145       $migration,
146       $container->get('entity.manager'),
147       $container->get('plugin.manager.entity_reference_selection')
148     );
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) {
155     // In case of subfields ('field_reference/target_id'), extract the field
156     // name only.
157     $parts = explode('/', $destinationProperty);
158     $destinationProperty = reset($parts);
159     $this->determineLookupProperties($destinationProperty);
160
161     $this->destinationProperty = isset($this->configuration['destination_field']) ? $this->configuration['destination_field'] : NULL;
162
163     return $this->query($value);
164   }
165
166   /**
167    * Determine the lookup properties from config or target field configuration.
168    *
169    * @param string $destinationProperty
170    *   The destination property currently worked on. This is only used together
171    *   with the $row above.
172    */
173   protected function determineLookupProperties($destinationProperty) {
174     if (!empty($this->configuration['value_key'])) {
175       $this->lookupValueKey = $this->configuration['value_key'];
176     }
177     if (!empty($this->configuration['bundle_key'])) {
178       $this->lookupBundleKey = $this->configuration['bundle_key'];
179     }
180     if (!empty($this->configuration['bundle'])) {
181       $this->lookupBundle = $this->configuration['bundle'];
182     }
183     if (!empty($this->configuration['entity_type'])) {
184       $this->lookupEntityType = $this->configuration['entity_type'];
185     }
186
187     if (empty($this->lookupValueKey) || empty($this->lookupBundleKey) || empty($this->lookupBundle) || empty($this->lookupEntityType)) {
188       // See if we can introspect the lookup properties from destination field.
189       if (!empty($this->migration->getProcess()[$this->destinationBundleKey][0]['default_value'])) {
190         $destinationEntityBundle = $this->migration->getProcess()[$this->destinationBundleKey][0]['default_value'];
191         $fieldConfig = $this->entityManager->getFieldDefinitions($this->destinationEntityType, $destinationEntityBundle)[$destinationProperty]->getConfig($destinationEntityBundle);
192         switch ($fieldConfig->getType()) {
193           case 'entity_reference':
194             if (empty($this->lookupBundle)) {
195               $handlerSettings = $fieldConfig->getSetting('handler_settings');
196               $bundles = array_filter((array) $handlerSettings['target_bundles']);
197               if (count($bundles) == 1) {
198                 $this->lookupBundle = reset($bundles);
199               }
200               // This was added in 8.1.x is not supported in 8.0.x.
201               elseif (!empty($handlerSettings['auto_create']) && !empty($handlerSettings['auto_create_bundle'])) {
202                 $this->lookupBundle = reset($handlerSettings['auto_create_bundle']);
203               }
204             }
205
206             // Make an assumption that if the selection handler can target more
207             // than one type of entity that we will use the first entity type.
208             $this->lookupEntityType = $this->lookupEntityType ?: reset($this->selectionPluginManager->createInstance($fieldConfig->getSetting('handler'))->getPluginDefinition()['entity_types']);
209             $this->lookupValueKey = $this->lookupValueKey ?: $this->entityManager->getDefinition($this->lookupEntityType)->getKey('label');
210             $this->lookupBundleKey = $this->lookupBundleKey ?: $this->entityManager->getDefinition($this->lookupEntityType)->getKey('bundle');
211             break;
212
213           case 'file':
214           case 'image':
215             $this->lookupEntityType = 'file';
216             $this->lookupValueKey = $this->lookupValueKey ?: 'uri';
217             break;
218
219           default:
220             throw new MigrateException('Destination field type ' .
221               $fieldConfig->getType() . 'is not a recognized reference type.');
222         }
223       }
224     }
225
226     // If there aren't enough lookup properties available by now, then bail.
227     if (empty($this->lookupValueKey)) {
228       throw new MigrateException('The entity_lookup plugin requires a value_key, none located.');
229     }
230     if (!empty($this->lookupBundleKey) && empty($this->lookupBundle)) {
231       throw new MigrateException('The entity_lookup plugin found no bundle but destination entity requires one.');
232     }
233     if (empty($this->lookupEntityType)) {
234       throw new MigrateException('The entity_lookup plugin requires a entity_type, none located.');
235     }
236   }
237
238   /**
239    * Checks for the existence of some value.
240    *
241    * @param mixed $value
242    *   The value to query.
243    *
244    * @return mixed|null
245    *   Entity id if the queried entity exists. Otherwise NULL.
246    */
247   protected function query($value) {
248     // Entity queries typically are case-insensitive. Therefore, we need to
249     // handle case sensitive filtering as a post-query step. By default, it
250     // filters case insensitive. Change to true if that is not the desired
251     // outcome.
252     $ignoreCase = !empty($this->configuration['ignore_case']) ?: FALSE;
253
254     $multiple = is_array($value);
255
256     $query = $this->entityManager->getStorage($this->lookupEntityType)
257       ->getQuery()
258       ->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL);
259
260     if ($this->lookupBundleKey) {
261       $query->condition($this->lookupBundleKey, $this->lookupBundle);
262     }
263     $results = $query->execute();
264
265     if (empty($results)) {
266       return NULL;
267     }
268
269     // By default do a case-sensitive comparison.
270     if (!$ignoreCase) {
271       // Returns the entity's identifier.
272       foreach ($results as $k => $identifier) {
273         $result_value = $this->entityManager->getStorage($this->lookupEntityType)->load($identifier)->{$this->lookupValueKey}->value;
274         if (($multiple && !in_array($result_value, $value, TRUE)) || (!$multiple && $result_value !== $value)) {
275           unset($results[$k]);
276         }
277       }
278     }
279
280     if ($multiple && !empty($this->destinationProperty)) {
281       array_walk($results, function (&$value) {
282         $value = [$this->destinationProperty => $value];
283       });
284     }
285
286     return $multiple ? array_values($results) : reset($results);
287   }
288
289 }