I've created migration handler for fieldable_panels_pane entity for our project needs. It works.
Should I prepare patch here? Is maintainer interested in Migration module integration?
If there is a point I can prepare migration handler and example migration (also online documentation).
Thanks!

Comments

andypost’s picture

Awesome idea! waiting for patch ;)

primozsusa’s picture

This would be great!

primozsusa’s picture


/**
 * Class MigrateDestinationFpp
 */
class MigrateDestinationFpp extends MigrateDestinationEntity {

  var $entity_type = 'fieldable_panels_pane';
  var $entity_info = NULL;
  var $entity_key = NULL;

  static public function getKeySchema() {
    return array(
      'fpid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'description' => 'ID of destination node',
      ),
    );
  }

  /**
   * Return an options array (language, text_format), used for creating fields.
   *
   * @param string $language
   * @param string $text_format
   */
  static public function options($language, $text_format) {
    return compact('language', 'text_format');
  }

  /**
   * Basic initialization
   *
   * @param string $bundle
   * @param array $options
   *  Options (language, text_format) used for creating fields.
   */
  public function __construct($bundle, array $options = array()) {
    parent::__construct('fieldable_panels_pane', $bundle, $options);
    $this->entity_info = entity_get_info('fieldable_panels_pane');
    $this->entity_key = $this->entity_info['entity keys']['id'];
  }

  /**
   * Returns a list of fields available to be mapped for entities attached to
   * a particular bundle.
   *
   * @param Migration $migration
   *  Optionally, the migration containing this destination.
   * @return array
   *  Keys: machine names of the fields (to be passed to addFieldMapping)
   *  Values: Human-friendly descriptions of the fields.
   */
  public function fields($migration = NULL) {
    $properties = entity_get_property_info($this->entityType);
    $fields = array(
      'fpid' => t('existing fpp id'),
      'vid' => 'The current version in use for this entity.',
      'title' => 'Title',
      'link' => 'Title has link (tiny int)',
      'path' => 'The path the title should link to.',
      'reusable' => 'Whether or not this entity will appear in the Add Content dialog. (tiny int)',
      'admin_title' => 'The title it will appear in the Add Content dialog as.',
      'admin_description' => 'The description it will appear in the Add Content dialog with.',
      'category' => 'The category it will appear in the Add Content dialog under.',
      'language' => 'The {languages}.language of this entity.',
      'is_new' => 'TRUE if unread by recipient, FALSE if read by recipient',
    );

    foreach ($properties['properties'] as $name => $property_info) {
      if (isset($property_info['setter callback'])) {
        $fields[$name] = $property_info['description'];
      }
    }

    // Then add in anything provided by handlers
    $fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle);
    $fields += migrate_handler_invoke_all('FieldablePanelsPane', 'fields', $this->entityType, $this->bundle);

    return $fields;
  }

  /**
   * @param $ids
   *  Array of profile IDs to be deleted.
   */
  public function bulkRollback(array $ids) {
    migrate_instrument_start('fieldable_panels_pane_delete_multiple');
    $this->prepareRollback($ids);
    fieldable_panels_panes_delete_multiple($ids);
    $this->completeRollback($ids);
    migrate_instrument_stop('fieldable_panels_pane_delete_multiple');
  }

  /**
   * Import a single entity.
   *
   * @param $entity
   *  Entity object to build. Prefilled with any fields mapped in the Migration.
   * @param $row
   *  Raw source data object - passed through to prepare/complete handlers.
   * @return array
   *  Array of key fields of the entity that was saved if
   *  successful. FALSE on failure.
   */
  public function import(stdClass $entity, stdClass $row) {
    $migration = Migration::currentMigration();
    $type = $this->entity_info['entity keys']['bundle'];
    $entity->$type = $this->bundle;
    list($id, $vid, $bundle) = entity_extract_ids($this->entityType, $entity);

    // Updating previously-migrated content?
    if (isset($row->migrate_map_destid1)) {
      // Make sure is_new is off
      $entity->is_new = FALSE;
      if (!empty($id)) {
        if ($id != $row->migrate_map_destid1) {
          throw new MigrateException(t("Incoming id !id and map destination id !destid1 don't match",
            array('!id' => $id, '!destid1' => $row->migrate_map_destid1)));
        }
      }
      else {
        $entity->{$this->entity_key} = $row->migrate_map_destid1;
      }
    }
    if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
      if (empty($id)) {
        throw new MigrateException(t('System-of-record is DESTINATION, but no destination id provided'));
      }
      $old_entity = entity_load_single($this->entityType, $id);
      if (!isset($entity->created)) {
        $entity->created = $old_entity->created;
      }
      if (!isset($entity->uid)) {
        $entity->uid = $old_entity->uid;
      }
    }

    // Invoke migration prepare handlers
    $this->prepare($entity, $row);

    // Trying to update an existing entity
    if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
      // Incoming data overrides existing data.
      foreach ($entity as $field => $value) {
        $old_entity->$field = $value;
      }
      // Use the loaded entity from now on.
      $entity = $old_entity;
    }
    else {
      // Create a full profile class.
      $entity = entity_create($this->entityType, (array) $entity);
    }

    if (empty($id) && !(isset($entity->is_new) && $entity->is_new)) {
      $updating = TRUE;
    }
    else {
      $updating = FALSE;
    }

    migrate_instrument_start('entity_save');
    entity_save($this->entityType, $entity);
    migrate_instrument_stop('entity_save');

    list($id, $vid, $bundle) = entity_extract_ids($this->entityType, $entity);

    if (isset($id)) {
      if ($updating) {
        $this->numUpdated++;
      }
      else {
        $this->numCreated++;
      }

      $return = array($id);
    }
    else {
      $return = FALSE;
    }

    $this->complete($entity, $row);
    return $return;
  }

}

Example

 
class NcWebPanesDemo extends Migration {

  public function __construct() {
    parent::__construct();

    $this->description = t('Import Web Panes FPP');

    $this->map = new MigrateSQLMap($this->machineName,
      array(
        'fppid' => array(
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => TRUE,
          'description' => 'FPP old id'
        ),
      ),
      MigrateDestinationFpp::getKeySchema()
    );

    // Create MigrateSource object
    $source_dir = drupal_get_path('module', 'nc_agency_demo');

    $this->source = new MigrateSourceCSV($source_dir.'/import/fppcontent.csv',
      $this->csvcolumns(),
      array('delimiter' => ',', 'header_rows' => 1)
    );

    // Create destination
    $this->destination = new MigrateDestinationFpp('nc_pane_fpp');

    $this->addFieldMapping('title', 'title');
    $this->addFieldMapping('title_field', 'title');
    $this->addFieldMapping('link', 'title_has_link')->defaultValue(FALSE);
    $this->addFieldMapping('path', 'title_link_path')->defaultValue('');
    $this->addFieldMapping('reusable')->defaultValue(TRUE);
    $this->addFieldMapping('field_nc_pane_body', 'content');
    $this->addFieldMapping('admin_title', 'admintitle');
    $this->addFieldMapping('category', 'category');
    $this->addFieldMapping('is_new')->defaultValue(TRUE);

    $this->addUnmigratedSources(array(
      'label',
    ));

    $this->addUnmigratedDestinations(array(
      'vid',
      'field_nc_pane_body:language',
      'title_field:language',
      'admin_description',
      'language'
    ));
  }

  /**
   * Source columns array
   * @return mixed
   */
  function csvcolumns() {
    $columns[0] = array('fppid', 'FppId');
    $columns[1] = array('label', 'Label');
    $columns[2] = array('title', 'Title');
    $columns[3] = array('title_has_link', 'TitleHasLink');
    $columns[4] = array('title_link_path', 'TitleLinkPath');
    $columns[5] = array('content', 'Content');
    $columns[6] = array('admintitle', 'AdminTitle');
    $columns[7] = array('category', 'Category');

    return $columns;
  }

}
danylevskyi’s picture

primozsusa, it's incredibly great! Thanks!

eft’s picture

Status: Active » Needs review
StatusFileSize
new11.39 KB

Here is a patch. It is slightly different than the code posted in #3. It is more directly based on the current implementation of MigrateDestinationNode.

damienmckenna’s picture

A minor item: I've split the missing 'deletion callback' into a new issue: #2287015: Missing deletion callback in fieldable_panels_panes_entity_info

damienmckenna’s picture

StatusFileSize
new10.89 KB

Rerolled, and without the 'deletion callback' item.

damienmckenna’s picture

StatusFileSize
new10.9 KB

A minor change to fix some comment formatting.

damienmckenna’s picture

Status: Needs review » Fixed

Committed. Thanks primozsusa and eft!

  • DamienMcKenna committed 2ff1796 on 7.x-1.x
    Issue #2145209 by primozsusa, eft, DamienMcKenna: Support for the...

Status: Fixed » Closed (fixed)

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