diff --git a/modules/dgi_migrate_foxml_standard_mods/migrations/dgis_nodes.yml b/modules/dgi_migrate_foxml_standard_mods/migrations/dgis_nodes.yml index eb204d1..e3d6e5c 100644 --- a/modules/dgi_migrate_foxml_standard_mods/migrations/dgis_nodes.yml +++ b/modules/dgi_migrate_foxml_standard_mods/migrations/dgis_nodes.yml @@ -334,7 +334,7 @@ process: property: nodeValue field_description: - << : *base_mods_node - query: 'mods:abstract' + query: 'mods:abstract[@displayLabel="summary"]' - plugin: callback callable: iterator_to_array - plugin: multiple_values @@ -635,7 +635,6 @@ process: - plugin: dgi_migrate.subproperty property: nodeValue - plugin: single_value - - plugin: null_coalesce field_item_identifier: - << : *base_mods_node query: 'mods:location/mods:holdingSimple/mods:copyInformation/mods:itemIdentifier' @@ -655,7 +654,6 @@ process: - plugin: dgi_migrate.subproperty property: nodeValue - plugin: single_value - - plugin: null_coalesce field_local_identifier: - << : *base_mods_node query: 'mods:identifier[@type="local"]' @@ -677,7 +675,7 @@ process: - plugin: null_coalesce field_note_paragraph: - << : *base_mods_node - query: 'mods:note' + query: 'mods:note[not(@type="funding" or @type="admin")]' - plugin: callback callable: iterator_to_array - plugin: multiple_values @@ -719,7 +717,6 @@ process: - plugin: dgi_migrate.subproperty property: nodeValue - plugin: single_value - - plugin: null_coalesce field_linked_agent: - << : *base_mods_node query: 'mods:name[@type="personal"]' @@ -1638,6 +1635,10 @@ process: type: related_item process_values: true values: + _mods_xpath: + # XXX: Copy here so it's available for further subprocessing. + - plugin: get + source: parent_row/dest/_mods_xpath field_relationship_type: - << : *nested_mods_node query: '@type' @@ -1693,27 +1694,95 @@ process: - plugin: callback callable: array_filter - plugin: null_coalesce + field_related_item_identifier: + - <<: *nested_mods_node + query: 'mods:identifier' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_related_item_genre: + - <<: *nested_mods_node + query: 'mods:genre' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: genre + <<: *generic_term_after + - <<: *generic_term_extract _resource_type: - << : *base_mods_node query: 'mods:typeOfResource' - plugin: callback callable: iterator_to_array + - plugin: skip_on_empty + method: process - plugin: multiple_values - - plugin: dgi_migrate.subproperty - property: nodeValue - - plugin: single_value - - plugin: callback - callable: array_filter - field_resource_type: + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: resource_types + <<: *generic_term_after + - <<: *generic_term_extract + _unspecified_resource_type: - plugin: default_value - source: '@_resource_type' default_value: Unspecified - - plugin: entity_generate - bundle: resource_types - bundle_key: vid - entity_type: taxonomy_term - value_key: name - ignore_case: *case_insensitive + - plugin: gate + use_as_key: '@_resource_type' + valid_keys: + - null + key_direction: unlock + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: resource_types + _auth_value_uri: + - plugin: default_value + default_value: '' + _auth_source: + - plugin: default_value + default_value: '' + _value: + - plugin: get + source: parent_value + actual: + - plugin: get + source: + - '@_auth_source' + - '@_auth_value_uri' + - '@_value' + # XXX: Needs to be provided wherever this is used, corresponding + # to the vocab in which to do the things. + - '@_vid' + - plugin: flatten + - plugin: migration_lookup + migration: dgis_stub_terms_generic + stub_id: dgis_stub_terms_generic + - <<: *generic_term_extract + field_resource_type: + - plugin: get + source: + - '@_resource_type' + - '@_unspecified_resource_type' + - plugin: null_coalesce field_restriction_on_access: - << : *base_mods_node query: 'mods:accessCondition[@type="restriction on access"]' @@ -1722,21 +1791,6 @@ process: - plugin: multiple_values - plugin: dgi_migrate.subproperty property: nodeValue - # XXX: Was intended as outbound mapping, not inbound. Let's just comment for now. - #field_rights_statement: - # - << : *base_mods_node - # query: 'mods:accessCondition[@type="use and reproduction"]' - # - plugin: callback - # callable: iterator_to_array - # - plugin: multiple_values - # - plugin: dgi_migrate.subproperty - # property: nodeValue - # - plugin: entity_generate - # bundle: rights_statements - # bundle_key: vid - # entity_type: taxonomy_term - # value_key: name - # ignore_case: *case_insensitive field_shelf_locator: - << : *base_mods_node query: 'mods:location/mods:holdingSimple/mods:copyInformation/mods:shelfLocator' @@ -1745,7 +1799,7 @@ process: - plugin: multiple_values - plugin: dgi_migrate.subproperty property: nodeValue - _subject_topic: + field_subject: - << : *base_mods_node query: 'mods:subject/mods:topic[normalize-space()]' - plugin: callback @@ -1761,7 +1815,7 @@ process: default_value: subject <<: *generic_term_after - <<: *generic_term_extract - _subject_name_personal: + field_subject_name_person: - << : *base_mods_node query: 'mods:subject/mods:name[@type="personal" or not(@type)][normalize-space()]' - plugin: callback @@ -1832,7 +1886,7 @@ process: method: row - plugin: dgi_migrate.process.single_extract index: [ target_id ] - _subject_name_corporate: + field_subject_name_organization: - << : *base_mods_node query: 'mods:subject/mods:name[@type="corporate"][normalize-space()]' - plugin: callback @@ -1893,13 +1947,6 @@ process: method: row - plugin: dgi_migrate.process.single_extract index: [ target_id ] - field_subject: - - plugin: get - source: - - '@_subject_topic' - - '@_subject_name_personal' - - '@_subject_name_corporate' - - plugin: flatten field_sub_location: - << : *base_mods_node query: 'mods:location/mods:holdingSimple/mods:copyInformation/mods:subLocation' @@ -1973,6 +2020,578 @@ process: default_value: language <<: *generic_term_after - <<: *generic_term_extract + field_degree_name: + - <<: *base_mods_node + query: 'mods:extension/etd:degree/etd:name' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: degree_names + <<: *generic_term_after + - <<: *generic_term_extract + _degree_level: + - <<: *base_mods_node + query: 'mods:extension/etd:degree/etd:level' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: degree_levels + <<: *generic_term_after + - <<: *generic_term_extract + _unspecified_degree_level: + - plugin: default_value + default_value: Unspecified + - plugin: gate + use_as_key: '@_degree_level' + valid_keys: + - null + key_direction: unlock + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: degree_levels + _auth_value_uri: + - plugin: default_value + default_value: '' + _auth_source: + - plugin: default_value + default_value: '' + _value: + - plugin: get + source: parent_value + actual: + - plugin: get + source: + - '@_auth_source' + - '@_auth_value_uri' + - '@_value' + # XXX: Needs to be provided wherever this is used, corresponding + # to the vocab in which to do the things. + - '@_vid' + - plugin: flatten + - plugin: migration_lookup + migration: dgis_stub_terms_generic + stub_id: dgis_stub_terms_generic + - <<: *generic_term_extract + field_degree_level: + - plugin: get + source: + - '@_degree_level' + - '@_unspecified_degree_level' + - plugin: null_coalesce + field_department: + - <<: *base_mods_node + query: 'mods:extension/etd:degree/etd:discipline' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: discipline + <<: *generic_term_after + - <<: *generic_term_extract + field_institution: + - <<: *base_mods_node + query: 'mods:extension/etd:degree/etd:grantor' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: institution + <<: *generic_term_after + - <<: *generic_term_extract + field_version_identifier: + - <<: *base_mods_node + query: 'mods:note[@type="version identification"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_abstract: + - <<: *base_mods_node + query: 'mods:abstract[@displayLabel="academic"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_open_url: + - <<: *base_mods_node + query: 'mods:identifier[@type="openurl"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_ismn: + - <<: *base_mods_node + query: 'mods:identifier[@type="ismn"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_repec: + - <<: *base_mods_node + query: 'mods:identifier[@type="repec"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_gpo_number: + - <<: *base_mods_node + query: 'mods:identifier[@type="gpo"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_pubmed_central_number: + - <<: *base_mods_node + query: 'mods:identifier[@type="pmcid"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_pubmed_number: + - <<: *base_mods_node + query: 'mods:identifier[@type="pmid"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_ddc_classification: + - <<: *base_mods_node + query: 'mods:classification[@authority="ddc"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_sudoc_number: + - <<: *base_mods_node + query: 'mods:classification[@authority="sudoc"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_swank_classification: + - <<: *base_mods_node + query: 'mods:classification[@authority="swank"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: null_coalesce + field_conference: + - <<: *base_mods_node + query: 'mods:name[@type="conference"]/mods:namePart' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: conference_events + <<: *generic_term_after + - <<: *generic_term_extract + field_series_paragraph: + - <<: *base_mods_node + query: 'mods:relatedItem[@type="series"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_paragraph_generate + validate: *validate + type: series + process_values: true + values: + _mods_xpath: + # XXX: Copy here so it's available for further subprocessing. + - plugin: get + source: parent_row/dest/_mods_xpath + field_series_titles: + - <<: *nested_mods_node + query: 'mods:titleInfo/mods:title' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: series_titles + <<: *generic_term_after + - <<: *generic_term_extract + field_series_number: + - <<: *nested_mods_node + query: 'mods:titleInfo/mods:partNumber' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_publication_title: + - <<: *base_mods_node + query: 'mods:relatedItem[@type="host"]/mods:titleInfo/mods:title' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: titles + <<: *generic_term_after + - <<: *generic_term_extract + field_publication_number: + - <<: *base_mods_node + query: 'mods:relatedItem[@type="host"]/mods:titleInfo/mods:partNumber' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_publication_volume_title: + - <<: *base_mods_node + query: 'mods:relatedItem[@type="host"]/mods:titleInfo/mods:partName' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_publication_chapter_number: + - <<: *base_mods_node + query: 'mods:part[@type="chapter"]/mods:detail/mods:number' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_publication_section: + - <<: *base_mods_node + query: 'mods:part[@type="section"]/mods:detail/mods:title' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_publication_identifier: + - <<: *base_mods_node + query: 'mods:relatedItem[@type="host"]/mods:identifier' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_publication_url: + - <<: *base_mods_node + query: 'mods:relatedItem[@type="host"]/mods:location/mods:url' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: callback + callable: trim + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_publication_genre: + - <<: *base_mods_node + query: 'mods:relatedItem[@type="host"]/mods:genre' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: genre + <<: *generic_term_after + - <<: *generic_term_extract + field_extent_total_pages: + - <<: *base_mods_node + query: 'mods:part/mods:extent/mods:total' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_extent_first_page: + - <<: *base_mods_node + query: 'mods:part/mods:extent/mods:start' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_extent_last_page: + - <<: *base_mods_node + query: 'mods:part/mods:extent/mods:end' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: callback + callable: array_filter + - plugin: null_coalesce + field_admin_note: + - <<: *base_mods_node + query: 'mods:note[@type="admin"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: callback + callable: array_filter + field_funder: + - << : *base_mods_node + query: 'mods:name[@type="corporate"][mods:role/mods:roleTerm="funder"][normalize-space(mods:namePart)]' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _authority: + - << : *nested_mods_node + query: 'normalize-space(@authority)' + method: evaluate + _value_uri: + - << : *nested_mods_node + query: 'normalize-space(@valueURI)' + method: evaluate + _date_name: + - << : *nested_mods_node + query: 'normalize-space(mods:namePart[@type="date"][normalize-space()][1])' + method: evaluate + _display_form: + - << : *nested_mods_node + query: 'normalize-space(mods:displayForm[normalize-space()][1])' + method: evaluate + _untyped_names: + - << : *nested_mods_node + query: 'normalize-space(mods:namePart[not(@type)][normalize-space()])' + method: evaluate + _affiliation_lookup: + - << : *nested_mods_node + query: 'normalize-space(mods:affiliation[normalize-space()])' + method: evaluate + - plugin: skip_on_empty + method: process + - plugin: migration_lookup + migration: dgis_stub_terms_affiliate + stub_id: dgis_stub_terms_affiliate + _affiliation: + - plugin: default_value + source: '@_affiliation_lookup' + default_value: '' + target_id: + - plugin: get + source: + - '@_authority' + - '@_value_uri' + - '@_untyped_names' + - '@_date_name' + - '@_display_form' + - '@_affiliation' + - plugin: flatten + - plugin: migration_lookup + migration: dgis_stub_terms_corporate_body + stub_id: dgis_stub_terms_corporate_body + - plugin: skip_on_empty + method: row + - plugin: dgi_migrate.process.single_extract + index: [ target_id ] + field_sponsorship_information: + - <<: *base_mods_node + query: 'mods:note[@type="funding"]' + - plugin: callback + callable: iterator_to_array + - plugin: multiple_values + - plugin: dgi_migrate.subproperty + property: nodeValue + - plugin: single_value + - plugin: callback + callable: array_filter + _use_license: + - <<: *base_mods_node + query: 'mods:accessCondition[@type="use and reproduction"][@displayLabel="Creative Commons" or @displayLabe="Creative Commons"]' + - plugin: callback + callable: iterator_to_array + - plugin: skip_on_empty + method: process + - plugin: multiple_values + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: creative_commons_licenses_4_0 + <<: *generic_term_after + - <<: *generic_term_extract + _unspecified_use_license: + - plugin: default_value + default_value: Unspecified + - plugin: gate + use_as_key: '@_use_license' + valid_keys: + - null + key_direction: unlock + - plugin: dgi_migrate.sub_process + process_values: true + values: + _vid: + - plugin: default_value + default_value: creative_commons_licenses_4_0 + _auth_value_uri: + - plugin: default_value + default_value: '' + _auth_source: + - plugin: default_value + default_value: '' + _value: + - plugin: get + source: parent_value + actual: + - plugin: get + source: + - '@_auth_source' + - '@_auth_value_uri' + - '@_value' + # XXX: Needs to be provided wherever this is used, corresponding + # to the vocab in which to do the things. + - '@_vid' + - plugin: flatten + - plugin: migration_lookup + migration: dgis_stub_terms_generic + stub_id: dgis_stub_terms_generic + - <<: *generic_term_extract + field_use_license: + - plugin: get + source: + - '@_use_license' + - '@_unspecified_use_license' + - plugin: flatten nid: - plugin: migration_lookup source: '@field_pid' diff --git a/src/MigrateBatchExecutable.php b/src/MigrateBatchExecutable.php index 9344dc6..eb23c5b 100644 --- a/src/MigrateBatchExecutable.php +++ b/src/MigrateBatchExecutable.php @@ -237,6 +237,30 @@ protected function enqueue() { return MigrationInterface::RESULT_COMPLETED; } + /** + * Helper to format exceptions. + * + * @param \Exception $e + * The exception to format. + * @param string $prefix + * Prefix to tack onto exceptions, such that causes can be added + * recursively. + * @param int $depth + * Recursive depth, to augment message. + * + * @return string + * The formatted exception. + */ + protected function formatException(\Throwable $e, string $prefix = 'Initial', int $depth = 0) : string { + $parts = []; + $parts[] = "$prefix message: {$e->getMessage()}"; + $parts[] = "$prefix trace:\n{$e->getTraceAsString()}"; + if ($e->getPrevious()) { + $parts[] = $this->formatException($e->getPrevious(), "--- Cause #{$depth}", $depth + 1); + } + return implode("\n", $parts); + } + /** * The meat of processing a row. * @@ -257,11 +281,6 @@ protected function processRowFromQueue(Row $row) { $this->processRow($row); $save = TRUE; } - catch (MigrateException $e) { - $this->getIdMap()->saveIdMapping($row, [], $e->getStatus()); - $this->saveMessage($e->getMessage(), $e->getLevel()); - $save = FALSE; - } catch (MigrateSkipRowException $e) { if ($e->getSaveToMap()) { $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_IGNORED); @@ -271,6 +290,16 @@ protected function processRowFromQueue(Row $row) { } $save = FALSE; } + catch (MigrateException $e) { + $this->getIdMap()->saveIdMapping($row, [], $e->getStatus()); + $this->saveMessage($this->formatException($e), $e->getLevel()); + $save = FALSE; + } + catch (\Exception $e) { + $this->getIdMap()->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_FAILED); + $this->saveMessage($this->formatException($e), MigrationInterface::MESSAGE_ERROR); + $save = FALSE; + } if ($save) { try { diff --git a/src/Plugin/migrate/process/Xml/ContextQuery.php b/src/Plugin/migrate/process/Xml/ContextQuery.php index 4d825cf..fa78efd 100644 --- a/src/Plugin/migrate/process/Xml/ContextQuery.php +++ b/src/Plugin/migrate/process/Xml/ContextQuery.php @@ -27,11 +27,11 @@ class ContextQuery extends ProcessPluginBase { use MissingBehaviorTrait; /** - * The instance on which to run the query. + * Reference to the instance in the row on which to run the query. * - * @var \DOMXPath + * @var string */ - protected mixed $xpath; + protected string $xpath; /** * The query to execute. @@ -84,7 +84,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable ])); } - return call_user_func([$xpath, $this->method], $this->query, $value); + return $xpath->{$this->method}($this->query, $value); } }