Altering Feed data

Last updated on
13 September 2024

There are two main methods to alter Feed data upon import.

Feeds Tamper

An additional module - Feeds Tamper - provides an easy-to-use user interface for common data alterations prior to saving.

Custom submodule

Feeds recommends two pathways for altering feed data:

  • Use an event subscriber that extends AfterParseBase if:
    • You want to alter items.
    • You want to remove items from the parser result.
  • Use a regular event subscriber, implementing EventSubscriberInterface if:
    • You have an expensive calculation that you want to only calculate once and apply to all items.
    • You want to add items to the parser result.
    • You have an other complex use case in which it's not convenient to modify stuff per item.

If you find yourself needing to use both methods, be aware that you may need to set the priority for each subscriber to ensure they run in the correct order.

An event subscriber extending AfterParseBase

The base class \Drupal\feeds\EventSubscriber\AfterParseBase allows you to easily modify or remove items from the parser result.

Steps

  1. In a custom module, add a class that extends \Drupal\feeds\EventSubscriber\AfterParseBase.
  2. Implement the method ::applies() to limit the transformations for some feeds types. (You can skip this step if transformations should be applied to all feed types).
  3. Implement the method ::alterItem() for your transformations.
  4. If you want an item to be removed from the parser result, throw a \Drupal\feeds\Exception\SkipItemException in ::alterItem(). Do not throw this exception in other methods or not at all when your event subscriber does not extend AfterParseBase. Using it in "regular" event subscribers would cause errors during an import.
  5. Register the event subscriber in mymodule/mymodules.services.yml and tag it with 'event_subscriber'.

Example

The example event subscriber:

namespace Drupal\mymodule\EventSubscriber;

use Drupal\feeds\Event\ParseEvent;
use Drupal\feeds\EventSubscriber\AfterParseBase;
use Drupal\feeds\Exception\SkipItemException;
use Drupal\feeds\Feeds\Item\DynamicItem;
use Drupal\feeds\Feeds\Item\ItemInterface;

/**
 * Reacts on articles being processed.
 */
class ArticleFeed extends AfterParseBase {

  /**
   * {@inheritdoc}
   */
  public function applies(ParseEvent $event) {
    return $event->getFeed()->getType()->id() === 'my_feed_type';
  }

  /**
   * {@inheritdoc}
   */
  protected function alterItem(ItemInterface $item, ParseEvent $event) {
    // Example 1: split single value into multiple values.

    // Get an item's value.
    $authors = $item->get('authors');
    // Manipulate value.
    // 'authors' is for example: 'Morticia,Fester,Gomez'.
    $authors = explode(',', $authors);
    // And set an item's value.
    // 'authors' is now: [
    //   'Morticia',
    //   'Fester','
    //   'Gomez',
    // ].
    $item->set('authors', $authors);

    // Example 2: conditionally skip an item.
    if (strpos($item->get('title'), 'example') !== FALSE) {
      throw new SkipItemException('Do not import articles that have "example" in the title.');
    }

    // Example 3: add an item.
    if ($item->get('title') === 'Article 3a') {
      $item = new DynamicItem();
      $item->set('title', 'Article 3b');
      $event->getParserResult()->push($item);
    }
  }
}

In mymodules.services.yml:

services:
  mymodule.my_subscriber:
    class: Drupal\mymodule\EventSubscriber\ArticleFeed
    tags:
      - { name: event_subscriber }

Note

This documentation was sourced from the solution of #3000384.

Using a regular event subscriber, implementing EventSubscriberInterface

For more complex and customized alterations of feed data, you may want to create a class that just implements EventSubscriberInterface. Do this if extending AfterParseBase is not convenient for your use case.

Steps

  1. In a custom module, add a class that implements \Symfony\Component\EventDispatcher\EventSubscriberInterface.
  2. In ::getSubscribedEvents(), add a listener to the FeedsEvents:PARSE event. Set the listeners priority lower than 0. You can use the constant FeedsEvents:AFTER for this.
  3. Implement the listener's callback in the same class. It will receive a \Drupal\feeds\Event\ParseEvent object.
  4. Register the event subscriber in mymodule/mymodules.services.yml and tag it with 'event_subscriber'.

Example

namespace Drupal\mymodule\EventSubscriber;

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

/**
 * Modifies the parsed result of my feed.
 */
class MySubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[FeedsEvents::PARSE][] = ['afterParse', FeedsEvents::AFTER];
    return $events;
  }

  /**
   * Act on parser result.
   */
  public function afterParse(ParseEvent $event) {
    /** @var \Drupal\feeds\FeedInterface */
    $feed = $event->getFeed();
    /** @var \Drupal\feeds\Result\ParserResultInterface */
    $parser_result = $event->getParserResult();

    // Check if this is the feed type we want to manipulate.
    if ($feed->getType()->id() !== 'my_feed_type') {
      // Not the feed type that we are interested in. Abort.
      return;
    }

    /** @var \Drupal\feeds\Feeds\Item\ItemInterface */
    foreach ($parser_result as $item) {
      // Example: get an item's value.
      $authors = $item->get('authors');
      // Manipulate value.
      // 'authors' is for example: 'Morticia,Fester,Gomez'.
      $authors = explode(',', $authors);
      // And set an item's value.
      // 'authors' is now: [
      //   'Morticia',
      //   'Fester','
      //   'Gomez',
      // ].
      $item->set('authors', $authors);
    }
  }
}

In mymodules.services.yml:

services:
  mymodule.my_subscriber:
    class: Drupal\mymodule\EventSubscriber\MySubscriber
    tags:
      - { name: event_subscriber }

Help improve this page

Page status: No known problems

You can: