Exportar e importar un node lleno de Paragraphs de un Drupal a otro

  • 26 Jun 2019
  • Drupal 7

Sin dudas, Paragraphs es una de las mejores cosas que le pasó a Drupal. A veces me pregunto cuantos módulos de Drupal han dejado de tener valor desde el día que Paragraphs salió a la calle. Por ejemplo, los módulos para syntax highlighter o el viejo Insert presente desde el 21 de octubre de 2009. Paragraphs los destruye. Con Paragraphs, en vez de crear todos los campos que te hacen falta en un tipo de contenido, los creas para todo el mundo y cada vez que creamos un node usamos solo los que necesitamos. Hace poco más de 3 años escribí un artículo para tratar de que nadie más use Insert.

Pero como en todo lo bueno siempre hay un punto negro. Los de Paragraphs podrían ser dos. Uno es la posibilidad de reutilizar estructuras de paragraphs creadas anteriormente en otros nodes y la otra es la posibilidad de exportar e importar nodes creados con campos de paragraphs a otras instancias de Drupal. De esto se habla en este artículo.

Replicate y Replicate Paragraphs

En la busqueda de un módulo para exportar e importar nodes inevitablemente Google nos sugerirá Node export. El problema con este módulo es que cuando el node tiene un campo de tipo Paragraphs, exporta la referencia al campo y no el contenido del campo en si. Entonces, si el resultado lo importamos en una instalación de Drupal diferente obtendremos cualquiercosa menos una copia exacta del node exportado. Replicate, por el contrario no cumple con la función de exportar, simplemente crea una copia exacta en la misma instancia de Drupal. Este hecho haría que Replicate sea descartado desde el vamos... sin embargo, la existencia de Replicate Paragraphs nos obliga a investigar más detalladamente las virtudes de Replicate.

Replicate, a diferencia de la mayor parte de la competencia, no cuenta con una UI. Una vez instalado vamos a encontrar un nuevo comando en Drush: replicate-drush-entity-by-id. Este comando ejecuta la siguiente función en replicate.drush.inc

function drush_replicate_drush_entity_by_ids($entity_type = NULL, $ids = NULL) {
  $original_ids = explode(',', $ids);

  foreach ($original_ids as $original_id) {
    $entity_id = replicate_entity_by_id($entity_type, $original_id);
    if (!empty($entity_id)) {
      drush_log(dt('@entity_type @entity_id is clone of @entity_type @original_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id, '@original_id' => (int)$original_id)), 'success');
    }
    else {
      drush_log(dt('Impossible to replicate @entity_type @original_id', array('@entity_type' => $entity_type, '@original_id' => check_plain($original_id))), 'error');
    }
  }
}

A su vez, la función replicate_entity_by_id se ocupará de exportar en node indicado como parámetro en el comando drush y luego guardarlo.

function replicate_entity_by_id($entity_type, $id) {
  if (($original = entity_load_single($entity_type, $id)) !== FALSE) {
    return replicate_entity($entity_type, $original);
  }

  return FALSE;
}

Dentro la variable $original vamos a tener lo que estamos buscando. El objeto exportado (lleno de Paragraphs no referenciados, es decir, con todo su contenido) ya serializado. El problema es que Replicate crea inmediatamente una copia. Para lograr nuestro propósito bastará crear dos nuevas funciones, una para exportar el contenido de $original a un archivo y otra para importar el contenido de tal archivo a una nueva entidad.

Primero agregamos los dos nuevos items a replicate_drush_command()

$items['replicate-export-drush-entity-by-ids'] = array(
    'description' => 'Export an entity and save to file (../export-[type]-[id])',
    'arguments' => array(
      'entity-type' => dt('Type of entity (eg. Node, Comment, ...) that you wish to replicate.'),
      'entity-ids' => dt('A comma delimited list of entity IDs which should be replicated (eg. NodeID, TermID, ...).'),
    ),
    'required-arguments' => 1,
    'aliases' => array('rep-exp', 'replicate-export'),
    'examples' => array(
      'drush replicate-drush-entity-by-ids node 1' =>
        'Replicate node 1 using full command.',
      'drush replicate node 1,2,3' =>
        'Replicate nodes 1,2 and 3 using aliased command.',
    ),
  );
  $items['replicate-import-drush-entity-by-ids'] = array(
    'description' => 'Create entity from exported file',
    'arguments' => array(
      'entity-type' => dt('Type of entity (eg. Node, Comment, ...) that you wish to replicate.'),
      'entity-ids' => dt('A comma delimited list of entity IDs which should be replicated (eg. NodeID, TermID, ...).'),
    ),
    'required-arguments' => 1,
    'aliases' => array('rep-imp', 'replicate-import'),
    'examples' => array(
      'drush replicate-drush-entity-by-ids node 1' =>
        'Replicate node 1 using full command.',
      'drush replicate node 1,2,3' =>
        'Replicate nodes 1,2 and 3 using aliased command.',
    ),
  );

Y las dos nuevas funciones:

function drush_replicate_export_drush_entity_by_ids($entity_type = NULL, $ids = NULL) {
  $original_ids = explode(',', $ids);

  foreach ($original_ids as $original_id) {
    if (($original = entity_load_single($entity_type, $original_id)) !== FALSE) {
      $clone = replicate_clone_entity($entity_type, $original);
    }
    if (!empty($clone)) {
      file_put_contents("/{$entity_type}-{$original_id}.txt", serialize($clone));
      drush_log(dt('@entity_type @entity_id is clone of @entity_type @original_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id, '@original_id' => (int)$original_id)), 'success');
    }
    else {
      drush_log(dt('Impossible to replicate @entity_type @original_id', array('@entity_type' => $entity_type, '@original_id' => check_plain($original_id))), 'error');
    }
  }
}


function drush_replicate_import_drush_entity_by_ids($entity_type = NULL, $ids = NULL) {
  $original_ids = explode(',', $ids);

  foreach ($original_ids as $original_id) {
    $clone = unserialize(file_get_contents("/{$entity_type}-{$original_id}.txt"));
    if ($clone) {
      entity_save($entity_type, $clone);
      list($entity_id) = entity_extract_ids($entity_type, $clone);

      if (isset($entity_id)) {
        module_invoke_all('replicate_entity_after_save', $clone, $entity_type, $entity);
      }
    }
    if (!empty($clone)) {
      drush_log(dt('@entity_type @entity_id is clone of @entity_type @original_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id, '@original_id' => (int)$original_id)), 'success');
    }
    else {
      drush_log(dt('Impossible to replicate @entity_type @original_id', array('@entity_type' => $entity_type, '@original_id' => check_plain($original_id))), 'error');
    }
  }
}

Entonces, para exporta un node bastará ejecutar:

drush @alias replicate-export node 123

Y para importarlo en la otra instancia de Drupal:

drush @alias replicate-import node 123

 

El código no es el más elegante de todos, pero digamos que en este espacio suelo compartir los primeros escritos de soluciones que todavía son un poco quick-and-dirty. Por ejemplo, los archivos podrían guardarse en un directorio privado.