diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc index fdac83b..5badd1e 100755 --- a/plugins/FeedsProcessor.inc +++ b/plugins/FeedsProcessor.inc @@ -584,6 +584,21 @@ abstract class FeedsProcessor extends FeedsPlugin { } /** + * Returns a statically cached version of the target mappings. + * + * @return array + * The targets for this importer. + */ + protected function getCachedTargets() { + $targets = &drupal_static('FeedsProcessor::getCachedTargets', array()); + if (!isset($targets[$this->id])) { + $targets[$this->id] = $this->getMappingTargets(); + } + + return $targets[$this->id]; + } + + /** * Execute mapping on an item. * * This method encapsulates the central mapping functionality. When an item is @@ -601,86 +616,133 @@ abstract class FeedsProcessor extends FeedsPlugin { * @ingroup mappingapi * * @see hook_feeds_parser_sources_alter() - * @see hook_feeds_data_processor_targets_alter() - * @see hook_feeds_node_processor_targets_alter() - * @see hook_feeds_term_processor_targets_alter() - * @see hook_feeds_user_processor_targets_alter() + * @see hook_feeds_processor_targets() + * @see hook_feeds_processor_targets_alter() */ protected function map(FeedsSource $source, FeedsParserResult $result, $target_item = NULL) { - - // Static cache $targets as getMappingTargets() may be an expensive method. - static $sources; - if (!isset($sources[$this->id])) { - $sources[$this->id] = feeds_importer($this->id)->parser->getMappingSources(); - } - static $targets; - if (!isset($targets[$this->id])) { - $targets[$this->id] = $this->getMappingTargets(); - } - $parser = feeds_importer($this->id)->parser; if (empty($target_item)) { $target_item = array(); } + $targets = $this->getCachedTargets(); // Many mappers add to existing fields rather than replacing them. Hence we // need to clear target elements of each item before mapping in case we are // mapping on a prepopulated item such as an existing node. foreach ($this->config['mappings'] as $mapping) { - if (isset($targets[$this->id][$mapping['target']]['real_target'])) { - $target_item->{$targets[$this->id][$mapping['target']]['real_target']} = NULL; + if (isset($targets[$mapping['target']]['real_target'])) { + $target_item->{$targets[$mapping['target']]['real_target']} = NULL; } else { $target_item->{$mapping['target']} = NULL; } } - /* - This is where the actual mapping happens: For every mapping we envoke - the parser's getSourceElement() method to retrieve the value of the source - element and pass it to the processor's setTargetElement() to stick it - on the right place of the target item. - - If the mapping specifies a callback method, use the callback instead of - setTargetElement(). - */ + // This is where the actual mapping happens: For every mapping we envoke + // the parser's getSourceElement() method to retrieve the value of the source + // element and pass it to the processor's setTargetElement() to stick it + // on the right place of the target item. self::loadMappers(); foreach ($this->config['mappings'] as $mapping) { - // Retrieve source element's value from parser. - if (isset($sources[$this->id][$mapping['source']]) && - is_array($sources[$this->id][$mapping['source']]) && - isset($sources[$this->id][$mapping['source']]['callback']) && - function_exists($sources[$this->id][$mapping['source']]['callback'])) { - $callback = $sources[$this->id][$mapping['source']]['callback']; - $value = $callback($source, $result, $mapping['source']); - } - else { - $value = $parser->getSourceElement($source, $result, $mapping['source']); - } + $value = $this->getSourceValue($source, $result, $mapping['source']); + $this->mapTarget($source, $target_item, $value, $mapping); + } - // Map the source element's value to the target. - if (isset($targets[$this->id][$mapping['target']]) && - is_array($targets[$this->id][$mapping['target']]) && - isset($targets[$this->id][$mapping['target']]['callback']) && - function_exists($targets[$this->id][$mapping['target']]['callback'])) { - $callback = $targets[$this->id][$mapping['target']]['callback']; + return $target_item; + } - // All target callbacks expect an array. - if (!is_array($value)) { - $value = array($value); - } + /** + * Returns the values from the parser, or callback. + * + * @param FeedsSource $source + * The feed source. + * @param FeedsParserResult $result + * The parser result. + * @param string $source_key + * The current key being processed. + * + * @return mixed + * A value, or a list of values. + */ + protected function getSourceValue(FeedsSource $source, FeedsParserResult $result, $source_key) { + $parser = feeds_importer($this->id)->parser; + static $sources = array(); + if (!isset($sources[$this->id])) { + $sources[$this->id] = $parser->getMappingSources(); + } - foreach ($targets[$this->id][$mapping['target']]['preprocess_callbacks'] as $preprocess) { - $preprocess($source, $target_item, $mapping['target'], $value, $mapping); - } + if (isset($sources[$this->id][$source_key]) && + is_array($sources[$this->id][$source_key]) && + isset($sources[$this->id][$source_key]['callback']) && + function_exists($sources[$this->id][$source_key]['callback'])) { - $callback($source, $target_item, $mapping['target'], $value, $mapping); - } - else { - $this->setTargetElement($source, $target_item, $mapping['target'], $value, $mapping); + $callback = $sources[$this->id][$source_key]['callback']; + return $callback($source, $result, $source_key); + } + else { + return $parser->getSourceElement($source, $result, $source_key); + } + } + + /** + * Maps values onto the target item. + * + * @param FeedsSource $source + * The feed source. + * @param mixed &$target_item + * The target item to apply values into. + * @param mixed $value + * A value, or a list of values. + * @param array $mapping + * The mapping configuration. + */ + protected function mapTarget(FeedsSource $source, &$target_item, $value, array $mapping) { + $targets = $this->getCachedTargets(); + $target = $mapping['target']; + + $callbacks = $targets[$target]['preprocess_callbacks']; + $this->invokePreprocessCallbacks($callbacks, $source, $target_item, $target, $value, $mapping); + + // Map the source element's value to the target. + // If the mapping specifies a callback method, use the callback instead of + // setTargetElement(). + if (isset($targets[$target]) && + is_array($targets[$target]) && + isset($targets[$target]['callback']) && + function_exists($targets[$target]['callback'])) { + + $callback = $targets[$target]['callback']; + // All target callbacks expect an array. + if (!is_array($value)) { + $value = array($value); } + $callback($source, $target_item, $target, $value, $mapping); + } + else { + $this->setTargetElement($source, $target_item, $target, $value, $mapping); } + } - return $target_item; + /** + * Invokes the preprocess callbacks for a target. + * + * @param array $callbacks + * A list of callables. + * @param FeedsSource $source + * The feed source. + * @param object $target_item + * The target item being created/updated. + * @param mixed $values + * A single value, or a list of values. + * @param array $mapping + * The mapping configuration. + */ + protected function invokePreprocessCallbacks(array $callbacks, FeedsSource $source, $target_item, $target, $values, array $mapping) { + if (!is_array($values)) { + $values = array($values); + } + foreach ($callbacks as $callback) { + $callback($source, $target_item, $target, $values, $mapping); + } } /** @@ -889,11 +951,7 @@ abstract class FeedsProcessor extends FeedsPlugin { * The serial id of an entity if found, 0 otherwise. */ protected function existingEntityId(FeedsSource $source, FeedsParserResult $result) { - $targets = &drupal_static('FeedsProcessor::existingEntityId', array()); - if (!isset($targets[$this->id])) { - $targets[$this->id] = $this->getMappingTargets(); - } - + $targets = $this->getCachedTargets(); $entity_id = 0; // Iterate through all unique targets and test whether they already exist in @@ -910,12 +968,12 @@ abstract class FeedsProcessor extends FeedsPlugin { ->fetchField(); } - if (!$entity_id && !empty($targets[$this->id][$target]['unique_callbacks'])) { + if (!$entity_id && !empty($targets[$target]['unique_callbacks'])) { if (!is_array($value)) { $value = array($value); } - foreach ($targets[$this->id][$target]['unique_callbacks'] as $callback) { + foreach ($targets[$target]['unique_callbacks'] as $callback) { if (is_callable($callback) && $entity_id = call_user_func_array($callback, array($source, $this->entityType(), $this->bundle(), $target, $value))) { // Stop at the first unique ID returned by a callback. break;