Pull to refresh

Импорт сайтов из разных CMS на Drupal

Reading time 6 min
Views 6.4K
Я думаю, что у каждого, кто использует Drupal в своей работе, периодически возникают задачи переноса сайтов, работающих на других CMS, или просто задачи импорта данных на платформу Drupal.

Такие задачи периодически возникают и у меня, но раньше я весь импорт делал написанием скрипта на php, который просто записывает нужную информацию прямо в БД Drupal. Я, конечно же, знал, что есть механизмы, которые позволяют добавлять данные, используя API Drupal, но как-то разбираться с ними было лень, да и скрипт для записи сразу в БД пишется довольно быстро.

Когда сайт на Drupal достаточно простой и на нем не используются какие-то сложные модули (и их мало), то такой принцип импорта (прямая запись в БД) себя оправдывает. Но что делать, когда нужно перенести данные на очень сложный сайт со множеством модулей и их сложной настройкой?

В этом случае очень сильно поможет знание API Drupal'a, т.к. всю работу по правильному апдейту всех взаимосвязанных таблиц с учетом всех хитрых настроек за нас сделает Drupal.

Как оказалось, использование API Drupal'a не то чтобы просто, а очень просто. Про это и будет сегодняшняя статья.

Итак, у нас есть сайт на Drupal, в котором есть несколько типов содержимого, для каждого типа добавлено дополнительное поле с картинкой (используется CCK), сделана куча вьюх (используется Views), а для нарезки картинок используется ImageCache (очень глючная вещь, но лучше пока что нет ничего). Сайт работает на Drupal 6. Думаю, что с другими версиями будет аналогично, только скорее всего придется чуть подправить код, т.к. API у них несколько отличается.

Скрипт импорта данных будет располагаться в корне сайта и вызываться через http-запрос, что-то типа hxxp://site.ru/import.php. Как вы будете передавать данные для импорта (использование других БД, чтение файлов на диске или через POST-данные) — это уже ваше дело, сути это не меняет.

Первым делом разместим вот этот кусок кода в самом начале нашего скрипта:

require_once 'includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);


Этот кусок кода загружает ядро Drupal и делает все необходимые настройки для подключения к БД сайта.

Далее мы должны подключиться к своим источникам данных и загрузить необходимые данные.

Предположим нам надо добавить новость на сайт, для этого пишем следующий код:

$node = new stdClass();
$node->title = "Заголовок новости";
$node->body = "<p>HTML-код новости</p>";
$node->teaser = $node->body;
$node->type = "news";
$node->created = time(); // дата создания
$node->changed = $node->created; // дата обновления
$node->status = 1; // нода опубликована
$node->format = 1; // используется фильтр Filtered HTML
$node->comment = 2; // комментарии разрешены
$node->uid = 0; // ноду добавил "Гость", можно поставить uid=1, тогда ноду добавит админ сайта
$node->language = 'ru'; // нода на русском языке
node_save($node);
$new_id = $node->nid;


Для создания новой ноды мы должны создать экземпляр класса stdClass и заполнить его необходимыми данными. В данном примере указывается заголовок новости, ее тело, тизер. Тип содержимого (type) я указал «news», так в моем сайте обозначены новости. Тип может быть любым другим, т.к. практически все в Drupal делается через концепцию ноды.

Собственно, использование API Drupal'a заключается в единственной строчке — вызове метода node_save, в который передается заполненный экземпляр класса для данных ноды. Этот метод делает запись в таблицах node, node_revisions и, возможно, в других связанных таблицах, вам уже не нужно думать об этом.

Если вы хотите получить идентификатор записанной ноды, то сразу после вызова node_save прочитайте значение в переменной $node->nid (функция сама добавит новое свойство и запишет туда значение).

Теперь нам нужно добавить дополнительное CCK-поле к нашей новости. В моем случае это будет поле field_img, которое используется для вывода картинки к новости, а ImageCache используется, когда нужно вывести либо thumbnail, либо чуть уменьшенную копию картинки, т.к. картинки могут быть разных размеров, то все они через ImageCache подгоняются под заданный размер.

Для добавления нового поля к нашей ноде нужно добавить картинку как «файл» в Drupal, а затем, используя полученный идентификатор «файла» записать все необходимые данные по CCK-полю.

$file = new stdClass();
$file->uid = 0;
$file->filename = "newsimage.jpeg";
$file->filepath = "files/newsimage.jpeg";
$file->filemime = file_get_mimetype($file->filename);
$file->filesize = filesize($filepath);
$file->status = 1;
$file->timestamp = time();
$file->origname = "";
drupal_write_record('files', $file);
$file_id = $file->fid;


Здесь filename — это имя файла, filepath — это имя файла с относительным (от корня сайта) путем, в данном случае считаем, что все наши картинки лежат в папке files. Дальше идет вызов функции для указания mime-типа картинки (можно просто руками указать «image/jpeg») и вычисление размера файла. После этого вызывается api-функция drupal_write_record, которая просто записывает структуру $files в системную таблицу files. Получается, что это некий аналог обертки вокруг функции записи данных в БД Drupal.

Теперь к ноде добавляем все данные по CCK-полю и сохраняем ее:

$node->field_img[0]['fid'] = $file->fid;
$node->field_img[0]['data']['alt'] = $node->title;
$node->field_img[0]['data']['title'] = $node->title;
node_save($node);


Ну и напоследок, приведу решение очень странного и неприятного глюка с ImageCache. Сам по себе модуль работает прекрасно и он очень удобен, только вот Drupal не хочет вызывать его в нужный момент, т.е. после импорта новости на сайта и обновления страницы мы thumbnails от ImageCache не увидим.

Все дело в том, что ImageCache вызывается только в тот момент, когда возникает 404-ошибка при обращении к файлу с картинкой. По идее, ImageCache должен перехватывать эту ошибку, и если идет обращение к несуществующей картинке, то генерировать ее, а затем выдавать саму картинку с 200-м кодом. Но так не происходит.

Я перерыл кучу форумов, пытался сам покопаться в коде, но так и не понял из-за чего картинка не генерируется. Поэтому я решил прямо в момент импорта новости генерировать картинки через ImageCache напрямую, используя API Drupal'a.

Вот этот кусок кода я вызываю сразу после сохранения CCK-поля с картинкой:

// генерируем thumbnail-картинку
$preset = imagecache_preset_by_name("thumb");
$dst = imagecache_create_path($preset['presetname'], $file->filepath);
imagecache_build_derivative($preset['actions'], $filepath, $dst);
// генерируем preview-картинку
$preset = imagecache_preset_by_name("preview");
$dst = imagecache_create_path($preset['presetname'], $file->filepath);
imagecache_build_derivative($preset['actions'], $filepath, $dst);


После этого у меня в нужных папках создаются нужные картинки и этот известный глюк пропадает.

Вот весь код полностью:

require_once 'includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

$node = new stdClass();
$node->title = "Заголовок новости";
$node->body = "<p>HTML-код новости</p>";
$node->teaser = $node->body;
$node->type = "news";
$node->created = time(); // дата создания
$node->changed = $node->created; // дата обновления
$node->status = 1; // нода опубликована
$node->format = 1; // используется фильтр Filtered HTML
$node->comment = 2; // комментарии разрешены
$node->uid = 0; // ноду добавил "Гость", можно поставить uid=1, тогда ноду добавит админ сайта
$node->language = 'ru'; // нода на русском языке
node_save($node);
$new_id = $node->nid;

$file = new stdClass();
$file->uid = 0;
$file->filename = "newsimage.jpeg";
$file->filepath = "files/newsimage.jpeg";
$file->filemime = file_get_mimetype($file->filename);
$file->filesize = filesize($filepath);
$file->status = 1;
$file->timestamp = time();
$file->origname = "";
drupal_write_record('files', $file);
$file_id = $file->fid;

$node->field_img[0]['fid'] = $file->fid;
$node->field_img[0]['data']['alt'] = $node->title;
$node->field_img[0]['data']['title'] = $node->title;
node_save($node);

// генерируем thumbnail-картинку
$preset = imagecache_preset_by_name("thumb");
$dst = imagecache_create_path($preset['presetname'], $file->filepath);
imagecache_build_derivative($preset['actions'], $filepath, $dst);
// генерируем preview-картинку
$preset = imagecache_preset_by_name("preview");
$dst = imagecache_create_path($preset['presetname'], $file->filepath);
imagecache_build_derivative($preset['actions'], $filepath, $dst);


Использованные материалы:
api.drupal.org/api/drupal/6 — официальная документация
www.drupal.ru — поиск по сайту по запросу «импорт данных»

PS: использование предложенного мной метода не ограничивается только API Drupal, возможен также вызов функций подключенных модулей напрямую, что и продемонстрировано на примере функций ImageCache.
Tags:
Hubs:
+47
Comments 49
Comments Comments 49

Articles