Hi,
I was wondering if there is a way to hook into feeds after importing each item (or maybe after all of them).
What I need (as I mentioned in another ticket) is to import nodes and then create corresponding group nodes (Group module).
I was thinking I could do a normal node import and then hook into it with some custom code to create the group nodes programmatically.
I can see a postprocess function in the Processor but not sure if that has access to the feed import data (e.g all entities created, updated etc).
Thanks!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

kyuubi created an issue. See original summary.

MegaChriz’s picture

I have been struggling with this issue as well. I was doubting if I should either introduce a new event or pass the item to the entity instead. With the first solution, you'll need to subscribe to an event. With the second solution, you can use the regular entity hooks (for example hook_entity_update()) and on it request for the item being processed, which would only be available when processing. As I had a need to also be able to manipulate the entity before validating I had to at least introduce some kind of prevalidate event, as I did not found a hook that is invoked before entities are validated.

See the patch for work in progress that includes both solutions. The patch is named 'entityreference-version' because I created it on top of an other patch, so it may not apply completely.

abu-zakham’s picture

Status: Active » Reviewed & tested by the community
MegaChriz’s picture

Status: Reviewed & tested by the community » Needs work

The patch in #2 no longer applies.

A separate issue has been opened for the prevalidate event: #3054851: Dispatch an event before validation.

MegaChriz’s picture

Title: Hook into Feeds after each row import? » Dispatch events before and after saving an entity during processing
Category: Support request » Feature request
Status: Needs work » Needs review
FileSize
14.68 KB

#3054851: Dispatch an event before validation is committed. Back to this issue.

The attached patch adds two events: one for presaving and one for postsaving an entity. Besides test coverage for these two events, test coverage for most other events dispatched by Feeds is added as well. It also includes a (hidden) feature to skip importing item while presaving an entity. Just throw an EmptyFeedException. Skip importing an item while in the presave phase is a feature that exists in the D7 version of Feeds as well. So basically this is a port of some features from the D7 version.

anawillem’s picture

I have the patched feed files with the eeds-presave-and-skip-2991955-6.patch and testing the feed both with the hook_entity_presave and the hook_entity_postsave (just in case they made a difference) and could not get those fields to change/populate

Just in case it made a difference, I also tried naming the hook 'hook_feeds_entity_presave' and those fields did not seem to get populated with that change either...

No errors in the watchdog. It does not seem to be getting triggered at all as I have a drupal_set_message at the end, and see nothing.

The tests were for feeds of type RSS/Atom.

Please let me know if there is more information I can give.

The function I created is basically:

modulename_entity_postsave(Drupal\Core\Entity\EntityInterface $entity) {
  $entity_type = $entity->getEntityType()->id();
  if($entity_type == 'alert') {
    $nid = $entity->getOriginalId();
    $node = node_load($nid);
    $original_description = $node->field_alert_description['en'][0]['field_alert_description_value'];

    if(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'Regional.')) {
      $node->field_alert_type['en'][0]['field_alert_type_target_id'] = array('tid' => 446);
      $original_description = str_replace('Regional.','', $original_description);
      $node->body['en'][0]['body_value'] = $original_description;
    }
    else {
      $node->field_alert_type['en'][0]['field_alert_type_target_id'] = array('tid' => 445);
    }

    if(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'DOF.')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 449);
      $node->body['en'][0]['body_value'] = str_replace('DOF.','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'DOF')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 449);
      $node->body['en'][0]['body_value'] = str_replace('DOF','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'Defend.')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 449);
      $node->body['en'][0]['body_value'] = str_replace('Defend.','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'MCAF.')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 485);
      $node->body['en'][0]['body_value'] = str_replace('MCAF.','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'MCAF')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 485);
      $node->body['en'][0]['body_value'] = str_replace('MCAF','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'MRD.')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 486);
      $node->body['en'][0]['body_value'] = str_replace('MRD.','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'MRD')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 486);
      $node->body['en'][0]['body_value'] = str_replace('MRD','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'STG.')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 487);
      $node->body['en'][0]['body_value'] = str_replace('STG.','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'STG')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 487);
      $node->body['en'][0]['body_value'] = str_replace('STG','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'C4.')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 448);
      $node->body['en'][0]['body_value'] = str_replace('C4.','', $original_description);
    }
    elseif(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'C4')) {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 448);
      $node->body['en'][0]['body_value'] = str_replace('C4','', $original_description);
    }
    else {
      $node->field_alert_organization['en'][0]['field_alert_organization_target_id'] = array('tid' => 447);
      $node->body['en'][0]['body_value'] = str_replace('C3.','', $original_description);
    }
    $node->save();
    drupal_set_message( "ALERT FEED: Node with nid " . $node->id() . " saved!\n", "alertfeed");
  }
MegaChriz’s picture

@anawillem
Thanks for trying the patch! What is added are not hooks, but events. So in order to use them, you'll need to have an event subscriber that subscribes to FeedsEvents::PROCESS_ENTITY_PRESAVE or FeedsEvents::PROCESS_ENTITY_POSTSAVE.

Example:

namespace Drupal\mymodule\EventSubscriber;

class MyFeedSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events = [];
    $events[FeedsEvents::PROCESS_ENTITY_PRESAVE][] = 'presave';
    $events[FeedsEvents::PROCESS_ENTITY_POSTSAVE][] = 'postsave';
    return $events;
  }

  /**
   * Acts on presaving an entity.
   */
  public function presave(EntityEvent $event) {
    $node = $event->getEntity();
    $item = $event->getItem();

    // Example: skip nodes if the item's 'blocked' value evaluates to true.
    if ($item->get('blocked')) {
      if (!$node->isNew()) {
        // Remove node.
        $node->delete();
      }

      // Prevent this node from being saved.
      throw new EmptyFeedException();
    }
  }

  /**
   * Acts on postsaving an entity.
   */
  public function postsave(EntityEvent $event) {
    $node = $event->getEntity();
    $item = $event->getItem();

    // @todo implement your logic here.
  }

}

The purpose of the patch is to be able to react on saving an entity in the context of a feed import, but also to be able to prevent an entity from being saved based on the parsed feed item data. This data is not available during regular entity hooks.

anawillem’s picture

Thank you for responding! Very clear. I have something up with a few edits as I respond to errors, but am now getting an error that I am not entirely understanding:
TypeError: Argument 1 passed to Drupal\edf_tweaks\EventSubscriber\EdfTweaksAlertsFeedEventSubscriber::presave() must be an instance of Drupal\edf_tweaks\EventSubscriber\Drupal\feeds\Event\EntityEvent, instance of Drupal\feeds\Event\EntityEvent given in Drupal\edf_tweaks\EventSubscriber\EdfTweaksAlertsFeedEventSubscriber->presave() (line 39

The first lines of the file I am working with are:

<?php

namespace Drupal\edf_tweaks\EventSubscriber;

use Drupal\feeds\Event\ClearEvent;
use Drupal\feeds\Event\ExpireEvent;
use Drupal\feeds\Event\EntityEvent;
use Drupal\feeds\Event\FeedsEvents;
use Drupal\feeds\Event\FetchEvent;
use Drupal\feeds\Event\InitEvent;
use Drupal\feeds\Event\ParseEvent;
use Drupal\feeds\Event\ProcessEvent;
use Drupal\feeds\Event\CleanEvent;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds\Plugin\Type\CleanableInterface;
use Drupal\feeds\Plugin\Type\ClearableInterface;
use Drupal\feeds\StateInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class EdfTweaksAlertsFeedEventSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events = [];
    $events[FeedsEvents::PROCESS_ENTITY_PRESAVE][] = 'presave';
    $events[FeedsEvents::PROCESS_ENTITY_POSTSAVE][] = 'postsave';
    return $events;
  }

  /**
   * Acts on presaving an entity.
   * @param Drupal\feeds\Event\EntityEvent $event
   *
   * @return Drupal\feeds\Event\EntityEvent $event
   */
  public function presave(Drupal\feeds\Event\EntityEvent $event) {
    $node = $event->getEntity();
    $item = $event->getItem();
 }


  /**
   * Acts on postsaving an entity.
   */
  public function postsave(Drupal\feeds\Event\EntityEvent $event) {
    $entity_type = $event->getEntityType()->id();
    if($entity_type == 'alert') {
      $node = $event->getEntity();
      $item = $event->getItem();

      $original_description = $node->field_alert_description['en'][0]['field_alert_description_value'];

      if(strpos($node->field_alert_description['en'][0]['field_alert_description_value'],'Regional.')) {
        $node->field_alert_type['en'][0]['field_alert_type_target_id'] = array('tid' => 446);
        $original_description = str_replace('Regional.','', $original_description);
        $node->body['en'][0]['body_value'] = $original_description;
      }
      else {
        $node->field_alert_type['en'][0]['field_alert_type_target_id'] = array('tid' => 445);
      }

      $node->save();
      drupal_set_message( "ALERT FEED: Node with nid " . $node->id() . " saved!\n", "alertfeed");
    }
  }

}

Instead of working with the $item, I am working with the $node and then just doing a node_save();

Similar as last time, I am not getting any of the messages triggered. This is after adding the service to the module.services.yml file.

Thank you again!

MegaChriz’s picture

@anawillem
When you specify the full namespaced class name in an use statement, you only need use the non-namespaced class name in the rest of the code.

Example:

namespace Drupal\mymodule\EventSubscriber;

use Drupal\feeds\Event\EntityEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class MySubscriber implements EventSubscriberInterface {

  /**
   * Acts on presaving an entity.
   *
   * @param \Drupal\feeds\Event\EntityEvent $event
   *   The presave event.
   */
  public function presave(EntityEvent $event) {
    $node = $event->getEntity();
    $item = $event->getItem();
   }

}

From the code example above, pay attention to the following line:

public function presave(EntityEvent $event) {

It only states "EntityEvent" here, not "Drupal\feeds\Event\EntityEvent", because the full namespaced class is already in the "use" statement earlier in the code:

use Drupal\feeds\Event\EntityEvent;

Sidenote: if for some reason you wouldn't use the "use" statement, the full namespaced class should start with a backslash, since you are in a namespace:

public function presave(\Drupal\feeds\Event\EntityEvent $event) {

Without the backslash, PHP assumes the class is called "Drupal\mymodule\EventSubscriber\Drupal\feeds\Event\EntityEvent". Starting with a backslash let's PHP know it should ignore the declared namespace.
For better readability of the code, I recommend to always use the "use" statement near the start of the file. I only explained this to explain why you got that TypeError error.

You also don't have to include use statements for classes you don't use in your class. So from your code example above, you would only need the following lines in the "use" section of your class:

use Drupal\feeds\Event\EntityEvent;
use Drupal\feeds\Event\FeedsEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
MegaChriz’s picture

  • MegaChriz committed d6bfe28 on 8.x-3.x
    Issue #2991955 by MegaChriz: Dispatch events before and after saving an...
MegaChriz’s picture

Status: Needs review » Fixed

Committed #11.

anawillem’s picture

Well, after some jostling around of code, I have something that works well with your committed #11. For posterity, the final code is like this:

<?php

namespace Drupal\edf_tweaks\EventSubscriber;

use Drupal\feeds\Event\EntityEvent;
use Drupal\feeds\Event\FeedsEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class EdfTweaksAlertsFeedEventSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events = [];
    $events[FeedsEvents::PROCESS_ENTITY_PRESAVE][] = 'presave';
    $events[FeedsEvents::PROCESS_ENTITY_POSTSAVE][] = 'postsave';
    return $events;
  }

  /**
   * Acts on presaving an entity.
   * @param Drupal\feeds\Event\EntityEvent $event
   *
   * @return Drupal\feeds\Event\EntityEvent $event
   */
  public function presave(EntityEvent $event) {
    $node = $event->getEntity();
    $item = $event->getItem();

    // Example: skip nodes if the item's 'blocked' value evaluates to true.
    if ($item->get('blocked')) {
      if (!$node->isNew()) {
        // Remove node.
        $node->delete();
      }

      // Prevent this node from being saved.
      throw new EmptyFeedException();
    }
  }

  /**
   * Acts on postsaving an entity.
   */
  public function postsave(EntityEvent $event) {
    $entity = $event->getEntity();
    $nid = $entity->Id();
    $node = node_load($nid);

    $original_description = $node->field_alert_description->value;

    if(strpos($node->field_alert_description->value,'Regional.')) {
      $node->field_alert_type = 446;   // taxonomy reference field
      $original_description = str_replace('Regional.','', $original_description);
      $node->body = $original_description;
    }
    else {
      $node->field_alert_type = 445;  // taxonomy reference field
    }


    $node->save();

    // Logs a notice
    $message = "ALERT FEED: Node with nid " . $node->id() . " saved!\n";
    \Drupal::logger('my_module')->notice($message);
  }
}

Thank you, @MegaChriz!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Bohus Ulrych’s picture

update post

Note: this helped (from test ... FeedsSubscriber.php)

  public static function getSubscribedEvents() {
    return [
      FeedsEvents::FEEDS_DELETE => ['onDelete'],
      FeedsEvents::INIT_IMPORT => ['onInitImport'],
      FeedsEvents::FETCH => [
        ['preFetch', FeedsEvents::BEFORE],
        ['postFetch', FeedsEvents::AFTER],
      ],
      FeedsEvents::PARSE => [
        ['preParse', FeedsEvents::BEFORE],
        ['postParse', FeedsEvents::AFTER],
      ],
      FeedsEvents::PROCESS => [
        ['preProcess', FeedsEvents::BEFORE],
        ['postProcess', FeedsEvents::AFTER],
      ],
      FeedsEvents::PROCESS_ENTITY_PREVALIDATE => ['prevalidate'],
      FeedsEvents::PROCESS_ENTITY_PRESAVE => ['preSave'],
      FeedsEvents::PROCESS_ENTITY_POSTSAVE => ['postSave'],
      FeedsEvents::CLEAN => ['onClean'],
      FeedsEvents::INIT_CLEAR => ['onInitClear'],
      FeedsEvents::CLEAR => ['onClear'],
      FeedsEvents::INIT_EXPIRE => ['onInitExpire'],
      FeedsEvents::EXPIRE => ['onExpire'],
      FeedsEvents::IMPORT_FINISHED => ['onFinish'],
    ];
  }