Экспорт и импорт данных из своей таблицы с помощью Chaos Tools

Экспорт и импорт данных из своей таблицы с помощью Chaos ToolsНаверняка многие из вас сталкивались с задачей переноса данных с одного сайта на другой. Например, создали локально представление вьюса - и надо быстро его перенести на рабочий сайт. И вы явно обратили внимание, что в каждом представлении есть вкладка "Экспорт". Благодаря такой возможности вы можете перенести это представление на рабочий сайт буквально за 10 секунд. Фактически происходит следующее: из таблицы выбирается запись с этим представлением, обрабатывается специальным образом и отдаётся пользователю в виде текста. А потом при импорте этот текст декодируется и вставляется как запись в таблицу. Одним из механизмов, позволяющих это реализовывать, является Ctools Exportable - о нём я и расскажу.

Шаг первый. Расширяем схему таблицы.

Для того, чтобы Chaos Tools смог работать с вашей таблицей, надо расширить её описание в hook_schema(), добавив параметр export:

/**
 * Implements hook_schema().
 */
function mymodule_schema() {
  $schema['mymodule_table'] = array(
    'export' => array(
      // Первичный ключ таблицы.
      'key' => 'oid',
      // Идентификатор. Можно указать любое значение.
      'identifier' => 'myobj',
      // Название своего хука. О нём чуть позже. Но на экспорт он не влияет.
      'default hook' => 'default_mymodule_myobj',
      // Если не выключить эту опцию, то ctools будет подсовывать в код экспорта
      // возможность выключить вставленную запись при её импорте.
      'can disable' => FALSE,
      // Информация об API вашего модуля.
      'api' => array(
        // Название модуля.
        'owner' => 'mymodule',
        // Название файла, подключаемого для API.
        'api' => 'default_mymodule_myobjs',
        // Минимальная и текущая версия API.
        'minimum_version' => 1,
        'current_version' => 1,
      ),
    ),
    // Дальше всё как обычно - описание полей, ключи и т.д.
    'fields' => array(
      'oid' => array(
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('oid'),
  );
  return $schema;
}

Обращаю внимание на тот факт, что если данные экспорта были добавлены в модуль уже после его релиза, то не нужно предоставлять дополнительных хуков hook_update_N() для его имплементации в уже созданную таблицу. Данные из массива 'export' будут взяты в оборот после обычного сброса кэша на сайте.

Шаг второй. Реализация экспорта.

Теперь нам надо создать страницу для экспорта записи. Добавляем страницу в хуке меню:

/**
 * Implements hook_menu().
 */
function mymodule_menu() {
  $items['admin/structure/mymodule/export/%'] = array(
    'title' => 'Mymodule object export',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('mymodule_export_form', 4),
   
    // Вообще здесь должен стоять нормальная проверка доступа,
    // но я поставил TRUE для простоты.
    'access callback' => TRUE,
  );
  return $items;
}

Вместо %, как можно догадаться, в пути будет находиться ID вашей записи в таблице.

Теперь реализация формы экспорта:

function mymodule_export_form($form, $form_state, $id) {
  // Подключаем файл с функциями экспорта.
  ctools_include('export');
  // Выгружаем объект из базы.
  $objects = ctools_export_load_object('mymodule_table', 'all', array($id));
  // Убеждаемся, что объект был выгружен.
  if (empty($objects[$id])) {
    return t('It is not possible to export this object right now. Try again later.');
  }
  $object = $objects[$id];
  // Создаём из выгруженного объекта код для экспорта.
  $code = ctools_export_crud_export('mymodule_table', $object);
  // Подсчитываем кол-во строк в коде (это уже для удобства).
  $lines = substr_count($code, "\n");
  // Создаём текстовое поле с выгруженным кодом.
  $form['export'] = array(
    '#title' => t('Export data'),
    '#type' => 'textarea',
    '#value' => $code,
    '#rows' => $lines,
    '#description' => t('Copy the export text and paste it into import area.'),
  );
  return $form;
}

С экспортом закончили, переходим к импорту.

Шаг третий. Реализация импорта.

Для импорта вам тоже понадобится создать страницу с формой, куда будет вставляться текст с кодом. Поэтому дополняем наш hook_menu() ещё одной страницей:

/**
  * Implements hook_menu().
  */
function mymodule_menu() {
  $items['admin/structure/mymodule/export/%'] = array(
    'title' => 'Mymodule object export',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('mymodule_export_form', 4),
    // Вообще здесь должен стоять нормальная проверка доступа,
    // но я поставил TRUE для простоты.
    'access callback' => TRUE,
  );
  $items['admin/structure/mymodule/import'] = array(
    'title' => 'Mymodule object import',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('mymodule_import_form'),
    // Вообще здесь должен стоять нормальная проверка доступа,
    // но я поставил TRUE для простоты.
    'access callback' => TRUE,
  );
  return $items;
}

Теперь создаём форму с импортом:

/**
 * Форма для импорта объектов.
 */
function mymodule_import_form($form, $form_state) {
  // Текстовая область для вставки импортируемого кода.
  $form['import'] = array(
  '#type' => 'textarea',
  '#title' => t('Paste code here to import object'),
  );
  $form['actions'] = array(
    '#type' => 'action',
  );
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Import'),
  );
  return $form;
}

/**
  * Обработчик импорта объектов.
  */
function mymodule_import_form_submit($form, &$form_state) {
  // Забираем полученный код для импорта.
  $code = $form_state['values']['import'];
  // Подключаем файл с функциями экспорта/импорта.
  ctools_include('export');
  // Создаем из полученного кода объект с нашей записью.
  $object = ctools_export_crud_import('mymodule_table', $code);
  // Убираем ключ из объекта, чтобы была создана новая запись в таблице.
  unset($object->oid);
  // Сохраняем объект в базе.
  $result = drupal_write_record($object);
  // Выводим сообщение в зависимости от результата записи.
  if ($result) {
    drupal_set_message('Запись была успешно импортирована');
  } else {
    drupal_set_message('При импорте возникла ошибка и запись не была импортирована', 'error');
  }
}
Ну вот и вся хитрость. Теперь ваши модули станут удобнее в миграции :)