Tel. 06151 / 39 10 793

Feeds Processor Plugin zum importieren spezieller Datensätze

14.05.2012  • Sebastian Kauschke
Feeds Processor Plugin zum importieren spezieller Datensätze

Die mächtigen Fähigkeiten des Feeds-Moduls hatten wir in dem Blogeintrag Externe Datenquellen, Drupals 6ter Sinn schon einmal ausführlich aufgezeigt.
Manchmal ergibt es sich jedoch, das auch die vielen vorhandenen Plugins nicht für den speziellen Anwendungsfall ausreichen. Im heutigen Beispiel hatte ich das Problem, das ein Import einer sehr großen CSV-Datei in eine Drupal-externe Datenbank erfolgen sollte.
Diese externe Datenbank wird innerhalb von Drupal durch einen $databases Eintrag in der settings.php angebunden. Hierauf möchte ich aber in diesem Beitrag nicht näher eingehen.
Für mein Vorhaben war Feeds daher sehr geeignet, da ich nur den letzten Schritt des Datenimportes anpassen musste. Zur Erinnerung: Beim Import durch das Feeds-Modul werden drei Schritte durchlaufen:

  1. 1) Das Fetching, hier konnte ich den normalen File upload Fetcher benutzen
  2. 2) Das Parsing, in meinem Fall eine simple CSV-Datei
  3. 3) Das Processing, in meinem Fall eine spezielle Angelegenheit, da in eine externe Datenbank eingefügt wurde, die auch noch spezielle Insert-Methoden benötigte (da spatiale MYSQL Felder benutzt wurden)

Wie bindet man nun so eine eigene Processing-Einheit an Feeds an?

Zuerst muss man einen ctools-Hook für Feeds-Plugins implementieren:

/**
* Implements hook_feeds_plugins()
*/
function mymodule_feeds_plugins() {
 
  $path = drupal_get_path('module', 'mymodule');
 
  $info = array();
  $info['MySpecialFeedsProcessor'] = array(     
    'name' => 'My special Feeds processor',
    'description' => 'Import data from a csv file.',
    'help' => '',
    'handler' => array(
      'parent' => 'FeedsProcessor',
      'class' => 'MySpecialFeedsProcessor',
      'file' => 'MySpecialFeedsProcessor.inc',
      'path' => $path,
    ),
  );
 
  return $info;
}

Hier teilt man über den Plugin-Hook dem Feeds Modul die Existenz eines Processor-Plugins mit. Andere Plugins (Fetcher und Parser) lassen sich ähnlich anlegen. Beispiele dazu gibt es in der feeds.plugins.inc die dem Feeds-Modul beiliegt.
Als Nächstes muss man das eigentliche Plugin implementieren. Das Plugin ist eine Klasse, die sich (in meinem Fall) von FeedsProcessor ableitet.

<?php

/**
* @file
* MySpecialFeedsProcessor class.
*/

/**
*  import processor plugin.
*/
class MySpecialFeedsProcessor extends FeedsProcessor {

  /**
   * Define entity type.
   */
  public function entityType() {
    return 'my_entity_type';
  }

  /**
   * Implements parent::entityInfo().
   */
  protected function entityInfo() {
    $info = parent::entityInfo();
    $info['label plural'] = t('My Fake Entity');
    return $info;
  }

  /**
   * Creates a new entity in memory and returns it. Just a new empty Object.
   */
  protected function newEntity(FeedsSource $source) {

    $data = new stdClass();
    return $data;
  }

  /**
   * Loads an existing entity. In my case this is always negative.
   */
  protected function entityLoad(FeedsSource $source, $id) {

    return null;
  }

  /**
   * Here you can validate the entity, but in this case i dont need that.
   *
   * @param type $entity
   * @return type
   */
  protected function entityValidate($entity) {

    return true;
  }

  /**
   * Save an entity. Here we invoke our special saving function ...
   */
  protected function entitySave($entity) {

    // Convert the entity object into an array, because my importfunction requires this.
    $data = (array) $entity;

    _externaldatabase_connect();
    _externaldatabase_import_dataline($data);
    _externaldatabase_disconnect();
  }

  /**
   * Delete multiple entities. We dont do that so this is empty here.
   */
  protected function entityDeleteMultiple($entity_ids) {
   
  }

  /**
   * Override parent::configDefaults().
   */
  public function configDefaults() {

    return parent::configDefaults();
  }

  /**
   * Inherit parent::configForm().
   */
  public function configForm(&$form_state) {

    $form = parent::configForm($form_state);
    return $form;
  }

  /**
   * Theres never a existing id, so this always returns 0
   *
   * @param FeedsSource $source
   * @param FeedsParserResult $result
   * @return type
   */
  protected function existingEntityId(FeedsSource $source, FeedsParserResult $result) {

    return 0;
  }

  /**
   * Return available mapping targets. These are essentially all the fields in the CSV-File that
   * i want to import.
   */
  public function getMappingTargets() {
    return array(
        'x_pos' => array(
            'name' => t('X Position'),
            'optional_unique' => TRUE,
        ),
        'y_pos' => array(
            'name' => t('Y Position'),
            'optional_unique' => TRUE,
        ),
        'title' => array(
            'name' => t('Title'),
            'optional_unique' => TRUE,
        ),
        'description' => array(
            'name' => t('Description'),
            'optional_unique' => TRUE,
        ),
    );
  }

}

Wie man sieht, sind viele der Funktionen quasi leer, da ich für meinen simplen Import keine größere Validierung und keine Prüfung auf vorhandene Elemente benötige. Dies ist natürlich anders, als wenn man Nodes oder User importieren würde.

Ich will noch auf die drei wichtigen Funktionen eingehen:
In der function getMappingTargets() werden alle Werte, die ich aus der CSV-Datei benötige, angelegt. In der Mapping-Ansicht des Processors kann ich diese Werte dann auswählen, und auf ein Source Feld mappen.
Die Funktion existingEntityId() gibt im meinem Fall immer 0 zurück. Ist dies nicht der Fall, wird die Funktion newEntity() nicht aufgerufen. Diese erzeugt das (leere) Objekt, welches letztendlich in entitySave() abgespeichert werden soll.
Ich returne hier nur ein leeres Objekt, die gemappten Werte aus der CSV-Datei sind dann in der entitySave()-Funktion in der $entity-Variable vorhanden (das macht der Mapper automatisch).
In der entitySave()-Funktion passiert dann der eigentliche Speichervorgang. Hier connecte in auf die externe Datenbank und füge die Werte ein.

Dies ist wirklich nur eine sehr einfache und "blinde" Version eines Importes. Für komplexere Aufgaben lassen sich hier Mechanismen implementieren, die das Update von vorhandenen Daten ermöglichen.

Kommentar hinzufügen

Der Inhalt dieses Feldes wird nicht öffentlich zugänglich angezeigt.

Suchen


Newsletter

Melden Sie sich zu unserem kostenlosen Newsletter an. Wir informieren Sie regelmässig über unsere Leistungsangebote, Drupal Best-Practices, Mobile Apps, ERPAL Updates und Veranstaltungen.

Unsere Partner
comm-press comm-press
Drupal Spezialisten aus Hamburg
neosmart - Digital Media, Webdesign & Webentwicklung<br />
aus Darmstadt neosmart
Digital Media, Webdesign & Webentwicklung aus Darmstadt
trinomica - Software Solutions trinomica
Software Solutions
Sponsoring und Mitgliedschaften

Drupal Initiative Deutschland - Firmen-Mitglied

Wir sind Diamant Sponsors für das DrupalCamp Frankfurt 2014

So finden Sie uns