The Feeds module is a great module. You can import all kinds of external data like rss feeds, xml and json api data into nodes relatively easily. There are also some nice events you can hook into to alter the incoming data. I set up a feed to consume the xmltv tv guide data so I could build an app to help me find tv I might want to watch. Here are the steps to get the feed working.
Feeds in Drupal come in 2 parts. You have the Feed Type which is under the structure menu and the Feed which is under the content menu.
Step 1: Build a Feed type for importing channel data from xmltv.
The Feed module doesn't natively support XML data, you need to install the Feeds Extensible Parsers module to get this option. Then when creating a feed type select XML.
I set the Always download on just while testing so it tried to import the data everytime, you can switch this off once live if you want.
Step 2: Setup the mapping.
This is the hardest bit but once you understand the process its quite simple. The key is the context field, it is the items selector. So its the pattern that matches the items. I tried setting it to "/" once and caused me all kinds of pain. Using this xml structure you can see the context gets me the item and from there its easy to select which fields and attributes you want.
<tv generator-info-name="xmltv.co.uk" source-info-name="xmltv.co.uk">
<channel id="00da025711e82cf319cb488d5988c099">
<display-name>Sony Movies</display-name>
</channel>
<channel id="00dfea977320f17bb419abaa1f079f39">
<display-name>Good Food</display-name>
<icon src="/images/channels/00dfea977320f17bb419abaa1f079f39.png"/>
</channel>
<channel id="013983523257bbdfa4691c23f55ff485">
<display-name>Word Network</display-name>
</channel>
<channel id="018202232e044b504f9dc5263617d496">
<display-name>The Box</display-name>
<icon src="/images/channels/018202232e044b504f9dc5263617d496.png"/>
</channel>
</tv>
Step 3: Setup feed and run it.
Hit save and import and it should try and run.
Step 4: (optional) Hook into preSave and afterParse events.
I wanted to change the format of the timestamps and lookup an entity reference and set it on the newly created nodes. I did this with the following code:
<?php
namespace Drupal\smart_tv_guide\EventSubscriber;
use Drupal\feeds\Event\FeedsEvents;
use Drupal\feeds\Event\EntityEvent;
use Drupal\feeds\Event\ParseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Subscribe to after parse event so we can alter fields while importing.
*/
class SmartTvGuideEventsSubscriber implements EventSubscriberInterface {
/**
* Constructor.
*/
public function __construct() {
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = [];
$events[FeedsEvents::PROCESS_ENTITY_PRESAVE][] = 'preSave';
$events[FeedsEvents::PARSE][] = ['afterParse', FeedsEvents::AFTER];
return $events;
}
/**
* Modify events after existing parser.
*
* @param \Drupal\feeds\Event\ParseEvent $event
* The parse event.
*/
public function afterParse(ParseEvent $event) {
/**
* @var \Drupal\feeds\Result\ParserResultInterface
* @var \Drupal\feeds\Feeds\Item\ItemInterface
*/
foreach ($event->getParserResult() as $item) {
$item->set('_start', date('c', strtotime($item->get('_start'))));
$item->set('_stop', date('c', strtotime($item->get('_stop'))));
}
}
/**
* Acts on presaving an entity.
*/
public function preSave(EntityEvent $event) {
if ($event->getFeed()->getType()->id() == 'show_importer') {
$node = $event->getEntity();
$item = $event->getItem();
// Create a custom show id and store it on the node show_id field.
$channel = $node->field_show_channel_id->value;
$start = $node->field_show_start->value;
$stop = $node->field_show_stop->value;
$node->set('field_show_id', $channel . $start . $stop);
// Lookup the channel using the channel id and then entity reference
// the channel from the show node.
$database = \Drupal::database();
$query = $database->query("select entity_id from node__field_channel_id where field_channel_id_value = '" . $channel . "'");
$channel_nid = $query->fetchField();
$node->field_show_channel->target_id = $channel_nid;
$node->save();
}
}
}
This code is fully working and the comments should be clear. Once its working its a thing of beauty but took be a fair while to figure this all out, hope this helps someone.
Add new comment