Mercurial > hg > isophonics-drupal-site
comparison core/modules/migrate_drupal/src/FieldDiscovery.php @ 18:af1871eacc83
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:33:08 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
17:129ea1e6d783 | 18:af1871eacc83 |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\migrate_drupal; | |
4 | |
5 use Drupal\Component\Plugin\Exception\PluginNotFoundException; | |
6 use Drupal\Core\Logger\LoggerChannelInterface; | |
7 use Drupal\migrate\Exception\RequirementsException; | |
8 use Drupal\migrate\Plugin\MigrationInterface; | |
9 use Drupal\migrate\Plugin\MigrationPluginManagerInterface; | |
10 use Drupal\migrate\Plugin\RequirementsInterface; | |
11 use Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface; | |
12 use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; | |
13 | |
14 /** | |
15 * Provides field discovery for Drupal 6 & 7 migrations. | |
16 */ | |
17 class FieldDiscovery implements FieldDiscoveryInterface { | |
18 | |
19 /** | |
20 * The CCK plugin manager. | |
21 * | |
22 * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface | |
23 */ | |
24 protected $cckPluginManager; | |
25 | |
26 /** | |
27 * An array of already discovered field plugin information. | |
28 * | |
29 * @var array | |
30 */ | |
31 protected $fieldPluginCache; | |
32 | |
33 /** | |
34 * The field plugin manager. | |
35 * | |
36 * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface | |
37 */ | |
38 protected $fieldPluginManager; | |
39 | |
40 /** | |
41 * The migration plugin manager. | |
42 * | |
43 * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface | |
44 */ | |
45 protected $migrationPluginManager; | |
46 | |
47 /** | |
48 * The logger channel service. | |
49 * | |
50 * @var \Drupal\Core\Logger\LoggerChannelInterface | |
51 */ | |
52 protected $logger; | |
53 | |
54 /** | |
55 * A cache of discovered fields. | |
56 * | |
57 * It is an array of arrays. If the entity type is bundleable, a third level | |
58 * of arrays is added to account for fields discovered at the bundle level. | |
59 * | |
60 * [{core}][{entity_type}][{bundle}] | |
61 * | |
62 * @var array | |
63 */ | |
64 protected $discoveredFieldsCache = []; | |
65 | |
66 /** | |
67 * An array of bundle keys, keyed by drupal core version. | |
68 * | |
69 * In Drupal 6, only nodes were fieldable, and the bundles were called | |
70 * 'type_name'. In Drupal 7, everything became entities, and the more | |
71 * generic 'bundle' was used. | |
72 * | |
73 * @var array | |
74 */ | |
75 protected $bundleKeys = [ | |
76 FieldDiscoveryInterface::DRUPAL_6 => 'type_name', | |
77 FieldDiscoveryInterface::DRUPAL_7 => 'bundle', | |
78 ]; | |
79 | |
80 /** | |
81 * An array of source plugin ids, keyed by Drupal core version. | |
82 * | |
83 * @var array | |
84 */ | |
85 protected $sourcePluginIds = [ | |
86 FieldDiscoveryInterface::DRUPAL_6 => 'd6_field_instance', | |
87 FieldDiscoveryInterface::DRUPAL_7 => 'd7_field_instance', | |
88 ]; | |
89 | |
90 /** | |
91 * An array of supported Drupal core versions. | |
92 * | |
93 * @var array | |
94 */ | |
95 protected $supportedCoreVersions = [ | |
96 FieldDiscoveryInterface::DRUPAL_6, | |
97 FieldDiscoveryInterface::DRUPAL_7, | |
98 ]; | |
99 | |
100 /** | |
101 * Constructs a FieldDiscovery object. | |
102 * | |
103 * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_plugin_manager | |
104 * The field plugin manager. | |
105 * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager | |
106 * The migration plugin manager. | |
107 * @param \Drupal\Core\Logger\LoggerChannelInterface $logger | |
108 * The logger channel service. | |
109 */ | |
110 public function __construct(MigrateFieldPluginManagerInterface $field_plugin_manager, MigrationPluginManagerInterface $migration_plugin_manager, LoggerChannelInterface $logger) { | |
111 $this->fieldPluginManager = $field_plugin_manager; | |
112 $this->migrationPluginManager = $migration_plugin_manager; | |
113 $this->logger = $logger; | |
114 } | |
115 | |
116 /** | |
117 * {@inheritdoc} | |
118 */ | |
119 public function addAllFieldProcesses(MigrationInterface $migration) { | |
120 $core = $this->getCoreVersion($migration); | |
121 $fields = $this->getAllFields($core); | |
122 foreach ($fields as $entity_type_id => $bundle) { | |
123 $this->addEntityFieldProcesses($migration, $entity_type_id); | |
124 } | |
125 } | |
126 | |
127 /** | |
128 * {@inheritdoc} | |
129 */ | |
130 public function addEntityFieldProcesses(MigrationInterface $migration, $entity_type_id) { | |
131 $core = $this->getCoreVersion($migration); | |
132 $fields = $this->getAllFields($core); | |
133 if (!empty($fields[$entity_type_id]) && is_array($fields[$entity_type_id])) { | |
134 foreach ($fields[$entity_type_id] as $bundle => $fields) { | |
135 $this->addBundleFieldProcesses($migration, $entity_type_id, $bundle); | |
136 } | |
137 } | |
138 } | |
139 | |
140 /** | |
141 * {@inheritdoc} | |
142 */ | |
143 public function addBundleFieldProcesses(MigrationInterface $migration, $entity_type_id, $bundle) { | |
144 $core = $this->getCoreVersion($migration); | |
145 $fields = $this->getAllFields($core); | |
146 $plugin_definition = $migration->getPluginDefinition(); | |
147 if (empty($fields[$entity_type_id][$bundle])) { | |
148 return; | |
149 } | |
150 $bundle_fields = $fields[$entity_type_id][$bundle]; | |
151 foreach ($bundle_fields as $field_name => $field_info) { | |
152 $plugin = $this->getFieldPlugin($field_info['type'], $migration); | |
153 if ($plugin) { | |
154 $method = isset($plugin_definition['field_plugin_method']) ? $plugin_definition['field_plugin_method'] : 'defineValueProcessPipeline'; | |
155 | |
156 // @todo Remove the following 3 lines of code prior to Drupal 9.0.0. | |
157 // https://www.drupal.org/node/3032317 | |
158 if ($plugin instanceof MigrateCckFieldInterface) { | |
159 $method = isset($plugin_definition['cck_plugin_method']) ? $plugin_definition['cck_plugin_method'] : 'processCckFieldValues'; | |
160 } | |
161 | |
162 call_user_func_array([ | |
163 $plugin, | |
164 $method, | |
165 ], [ | |
166 $migration, | |
167 $field_name, | |
168 $field_info, | |
169 ]); | |
170 } | |
171 else { | |
172 // Default to a get process plugin if this is a value migration. | |
173 if ((empty($plugin_definition['field_plugin_method']) || $plugin_definition['field_plugin_method'] === 'defineValueProcessPipeline') && (empty($plugin_definition['cck_plugin_method']) || $plugin_definition['cck_plugin_method'] === 'processCckFieldValues')) { | |
174 $migration->setProcessOfProperty($field_name, $field_name); | |
175 } | |
176 } | |
177 } | |
178 } | |
179 | |
180 /** | |
181 * Returns the appropriate field plugin for a given field type. | |
182 * | |
183 * @param string $field_type | |
184 * The field type. | |
185 * @param \Drupal\migrate\Plugin\MigrationInterface $migration | |
186 * The migration to retrieve the plugin for. | |
187 * | |
188 * @return \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface|\Drupal\migrate_drupal\Plugin\MigrateFieldInterface|bool | |
189 * The appropriate field or cck plugin to process this field type. | |
190 * | |
191 * @throws \Drupal\Component\Plugin\Exception\PluginException | |
192 * @throws \InvalidArgumentException | |
193 */ | |
194 protected function getFieldPlugin($field_type, MigrationInterface $migration) { | |
195 $core = $this->getCoreVersion($migration); | |
196 if (!isset($this->fieldPluginCache[$core][$field_type])) { | |
197 try { | |
198 $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, ['core' => $core], $migration); | |
199 $plugin = $this->fieldPluginManager->createInstance($plugin_id, ['core' => $core], $migration); | |
200 } | |
201 catch (PluginNotFoundException $ex) { | |
202 // @todo Replace try/catch block with $plugin = FALSE for Drupal 9. | |
203 // https://www.drupal.org/project/drupal/issues/3033733 | |
204 try { | |
205 /** @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManager $cck_plugin_manager */ | |
206 $cck_plugin_manager = $this->getCckPluginManager(); | |
207 $plugin_id = $cck_plugin_manager->getPluginIdFromFieldType($field_type, ['core' => $core], $migration); | |
208 $plugin = $cck_plugin_manager->createInstance($plugin_id, ['core' => $core], $migration); | |
209 } | |
210 catch (PluginNotFoundException $ex) { | |
211 $plugin = FALSE; | |
212 } | |
213 } | |
214 $this->fieldPluginCache[$core][$field_type] = $plugin; | |
215 } | |
216 return $this->fieldPluginCache[$core][$field_type]; | |
217 } | |
218 | |
219 /** | |
220 * Gets all field information related to this migration. | |
221 * | |
222 * @param string $core | |
223 * The Drupal core version to get fields for. | |
224 * | |
225 * @return array | |
226 * A multidimensional array of source data from the relevant field instance | |
227 * migration, keyed first by entity type, then by bundle and finally by | |
228 * field name. | |
229 */ | |
230 protected function getAllFields($core) { | |
231 if (empty($this->discoveredFieldsCache[$core])) { | |
232 $this->discoveredFieldsCache[$core] = []; | |
233 $source_plugin = $this->getSourcePlugin($core); | |
234 foreach ($source_plugin as $row) { | |
235 /** @var \Drupal\migrate\Row $row */ | |
236 if ($core === FieldDiscoveryInterface::DRUPAL_7) { | |
237 $entity_type_id = $row->get('entity_type'); | |
238 } | |
239 else { | |
240 $entity_type_id = 'node'; | |
241 } | |
242 $bundle = $row->getSourceProperty($this->bundleKeys[$core]); | |
243 $this->discoveredFieldsCache[$core][$entity_type_id][$bundle][$row->getSourceProperty('field_name')] = $row->getSource(); | |
244 } | |
245 } | |
246 return $this->discoveredFieldsCache[$core]; | |
247 } | |
248 | |
249 /** | |
250 * Gets all field information for a particular entity type. | |
251 * | |
252 * @param string $core | |
253 * The Drupal core version. | |
254 * @param string $entity_type_id | |
255 * The legacy entity type ID. | |
256 * | |
257 * @return array | |
258 * A multidimensional array of source data from the relevant field instance | |
259 * migration for the entity type, keyed first by bundle and then by field | |
260 * name. | |
261 */ | |
262 protected function getEntityFields($core, $entity_type_id) { | |
263 $fields = $this->getAllFields($core); | |
264 if (!empty($fields[$entity_type_id])) { | |
265 return $fields[$entity_type_id]; | |
266 } | |
267 return []; | |
268 } | |
269 | |
270 /** | |
271 * Gets all field information for a particular entity type and bundle. | |
272 * | |
273 * @param string $core | |
274 * The Drupal core version. | |
275 * @param string $entity_type_id | |
276 * The legacy entity type ID. | |
277 * @param string $bundle | |
278 * The legacy bundle (or content_type). | |
279 * | |
280 * @return array | |
281 * An array of source data from the relevant field instance migration for | |
282 * the bundle, keyed by field name. | |
283 */ | |
284 protected function getBundleFields($core, $entity_type_id, $bundle) { | |
285 $fields = $this->getEntityFields($core, $entity_type_id); | |
286 if (!empty($fields[$bundle])) { | |
287 return $fields[$bundle]; | |
288 } | |
289 return []; | |
290 } | |
291 | |
292 /** | |
293 * Gets the deprecated CCK Plugin Manager service as a BC shim. | |
294 * | |
295 * We don't inject this service directly because it is deprecated, and we | |
296 * don't want to instantiate the plugin manager unless we have to, to avoid | |
297 * triggering deprecation errors. | |
298 * | |
299 * @return \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface | |
300 * The CCK Plugin Manager. | |
301 */ | |
302 protected function getCckPluginManager() { | |
303 if (!$this->cckPluginManager) { | |
304 $this->cckPluginManager = \Drupal::service('plugin.manager.migrate.cckfield'); | |
305 } | |
306 return $this->cckPluginManager; | |
307 } | |
308 | |
309 /** | |
310 * Gets the source plugin to use to gather field information. | |
311 * | |
312 * @param string $core | |
313 * The Drupal core version. | |
314 * | |
315 * @return array|\Drupal\migrate\Plugin\MigrateSourceInterface | |
316 * The source plugin, or an empty array if none can be found that meets | |
317 * requirements. | |
318 */ | |
319 protected function getSourcePlugin($core) { | |
320 $definition = $this->getFieldInstanceStubMigrationDefinition($core); | |
321 $source_plugin = $this->migrationPluginManager | |
322 ->createStubMigration($definition) | |
323 ->getSourcePlugin(); | |
324 if ($source_plugin instanceof RequirementsInterface) { | |
325 try { | |
326 $source_plugin->checkRequirements(); | |
327 } | |
328 catch (RequirementsException $e) { | |
329 // If checkRequirements() failed, the source database did not support | |
330 // fields (i.e., CCK is not installed in D6 or Field is not installed in | |
331 // D7). Therefore, $fields will be empty and below we'll return an empty | |
332 // array. The migration will proceed without adding fields. | |
333 $this->logger->notice('Field discovery failed for Drupal core version @core. Did this site have the CCK or Field module installed? Error: @message', [ | |
334 '@core' => $core, | |
335 '@message' => $e->getMessage(), | |
336 ]); | |
337 return []; | |
338 } | |
339 } | |
340 return $source_plugin; | |
341 } | |
342 | |
343 /** | |
344 * Provides the stub migration definition for a given Drupal core version. | |
345 * | |
346 * @param string $core | |
347 * The Drupal core version. | |
348 * | |
349 * @return array | |
350 * The stub migration definition. | |
351 */ | |
352 protected function getFieldInstanceStubMigrationDefinition($core) { | |
353 return [ | |
354 'destination' => ['plugin' => 'null'], | |
355 'idMap' => ['plugin' => 'null'], | |
356 'source' => [ | |
357 'ignore_map' => TRUE, | |
358 'plugin' => $this->sourcePluginIds[$core], | |
359 ], | |
360 ]; | |
361 } | |
362 | |
363 /** | |
364 * Finds the core version of a Drupal migration. | |
365 * | |
366 * @param \Drupal\migrate\Plugin\MigrationInterface $migration | |
367 * The migration. | |
368 * | |
369 * @return string|bool | |
370 * A string representation of the Drupal version, or FALSE. | |
371 * | |
372 * @throws \InvalidArgumentException | |
373 */ | |
374 protected function getCoreVersion(MigrationInterface $migration) { | |
375 $tags = $migration->getMigrationTags(); | |
376 if (in_array('Drupal 7', $tags, TRUE)) { | |
377 return FieldDiscoveryInterface::DRUPAL_7; | |
378 } | |
379 elseif (in_array('Drupal 6', $tags, TRUE)) { | |
380 return FieldDiscoveryInterface::DRUPAL_6; | |
381 } | |
382 throw new \InvalidArgumentException("Drupal Core version not found for this migration"); | |
383 } | |
384 | |
385 } |