diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc index 0644feb..58379fe 100644 --- a/plugins/FeedsProcessor.inc +++ b/plugins/FeedsProcessor.inc @@ -32,6 +32,11 @@ class FeedsValidationException extends Exception {} class FeedsAccessException extends Exception {} /** + * Thrown if an entity could not be loaded. + */ +class FeedsEntityNotFoundException extends Exception {} + +/** * Abstract class, defines interface for processors. */ abstract class FeedsProcessor extends FeedsPlugin { @@ -116,6 +121,9 @@ abstract class FeedsProcessor extends FeedsPlugin { * * @todo We should be able to batch load these, if we found all of the * existing ids first. + * + * @throws FeedsEntityNotFoundException + * When an entity could not be loaded. */ protected function entityLoad(FeedsSource $source, $entity_id) { $info = $this->entityInfo(); @@ -131,7 +139,13 @@ abstract class FeedsProcessor extends FeedsPlugin { $entity = db_query("SELECT * FROM {" . $table . "} WHERE $key = :entity_id", $args)->fetchObject(); } - if ($entity && !empty($info['entity keys']['language'])) { + if (empty($entity)) { + throw new FeedsEntityNotFoundException(t('Entity with id @entity_id could not be loaded.', array( + '@entity_id' => $entity_id, + ))); + } + + if (!empty($info['entity keys']['language'])) { $entity->{$info['entity keys']['language']} = $this->entityLanguage(); } @@ -236,8 +250,8 @@ abstract class FeedsProcessor extends FeedsPlugin { $this->initEntitiesToBeRemoved($source, $state); } - $skip_new = $this->config['insert_new'] == FEEDS_SKIP_NEW; - $skip_existing = $this->config['update_existing'] == FEEDS_SKIP_EXISTING; + $insert_new = $this->config['insert_new'] != FEEDS_SKIP_NEW; + $update_existing = $this->config['update_existing'] != FEEDS_SKIP_EXISTING; while ($item = $parser_result->shiftItem()) { @@ -250,39 +264,34 @@ abstract class FeedsProcessor extends FeedsPlugin { module_invoke_all('feeds_before_update', $source, $item, $entity_id); - // If it exists, and we are not updating, or if it does not exist, and we - // are not inserting, pass onto the next item. - if (($entity_id && $skip_existing) || (!$entity_id && $skip_new)) { - continue; - } - try { $hash = $this->hash($item); - $changed = $hash !== $this->getHash($entity_id); // Do not proceed if the item exists, has not changed, and we're not // forcing the update. - if ($entity_id && !$changed && !$this->config['skip_hash_check']) { + if ($entity_id && !$this->config['skip_hash_check'] && $update_existing) { + if ($hash === $this->getHash($entity_id)) { + // The item has not changed. + continue; + } + } + + // Load or create the entity. + $entity = $this->provideEntity($source, $entity_id, $update_existing, $insert_new); + if (empty($entity)) { + // No entity was found nor created. Go to the next item. continue; } - // Load an existing entity. + // Add item info. + $this->newItemInfo($entity, $source->feed_nid, $hash); if ($entity_id) { - $entity = $this->entityLoad($source, $entity_id); - // The feeds_item table is always updated with the info for the most // recently processed entity. The only carryover is the entity_id. - $this->newItemInfo($entity, $source->feed_nid, $hash); $entity->feeds_item->entity_id = $entity_id; $entity->feeds_item->is_new = FALSE; } - // Build a new entity. - else { - $entity = $this->newEntity($source); - $this->newItemInfo($entity, $source->feed_nid, $hash); - } - // Set property and field values. $this->map($source, $parser_result, $entity); $this->entityValidate($entity); @@ -411,6 +420,56 @@ abstract class FeedsProcessor extends FeedsPlugin { } /** + * Loads an existing entity or creates new one. + * + * The entity_id parameter is emptied if an existing entity could not + * be found or if creating a new entity. + * + * Called by the process() method. + * + * @param FeedsSource $source + * The feeds source that spawns this entity. + * @param int|string $entity_id + * (optional) The id of the entity to load. + * @param bool $load_existing + * (optional) If the entity may be loaded at all. + * Defaults to true. + * @param bool $create_new + * (optional) If a new entity may be created. + * Defaults to true. + * + * @return object|null + * An entity if loading or creation was succesful. + * NULL otherwise. + * + * @see ::process() + */ + protected function provideEntity(FeedsSource $source, &$entity_id = NULL, $load_existing = TRUE, $create_new = TRUE) { + $entity = NULL; + + if ($entity_id && $load_existing) { + // Try to load an existing entity. + try { + $entity = $this->entityLoad($source, $entity_id); + } + catch (FeedsEntityNotFoundException $e) { + // In case the entity could not be found, empty the entity_id and act as if + // the item to import is new. + $entity_id = NULL; + // Log the exception. + $source->log('import', '@exception', array('@exception' => $e->getMessage(), WATCHDOG_WARNING); + } + } + + if (!$entity_id && $create_new) { + // Build a new entity. + $entity = $this->newEntity($source); + } + + return $entity; + } + + /** * Initializes the list of entities to remove. * * This populates $state->removeList with all existing entities previously