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

    Я думаю, что у каждого, кто использует 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.
    Поделиться публикацией

    Похожие публикации

    Комментарии 49
      +1
      как насчет переноса пользователей?
        0
        думаю, что нужно аналогично заполнить структуру stdClass минимальным набором полей и вызвать user_save

        в этом плане очень сильно помогает модуль Devel, который показывает данные текущей ноды, т.е. те данные, которые надо заполнять.

        вообще, толком нигде не написано про импорт, приходится по большей части методом научного тыка все делать))
          +2
          К сожалению похоже но не так же, в этом плане модуль user всегда отставал от node.

          Там нужно передавать вторым параметром массив в котором ключи совпадают с именами колонок в таблице пользователей.
          $new_account = array(
            'name' => $name,
            'pass' => $pass,
            'status' => 1,
            'roles' => array('subscriber'),
            'data' => serialize(array('portal_user_data' => array(...)),
          );
          
          $account = user_save(FALSE, $new_account);
          

          Способ прямо-таки дореволюционный, но даже в 6-ке приходиться делать так. В 7-ке еще не смотрел, не знаю.

          Это создание самого пользователя. А профайл пользователя — тот же нод, так что можно импортировать так же как написано в топике.
            0
            Поля профиля можно передавать в этом же массиве, формат типа 'profile_fieldname', могу попозже исходники свои поднять
              0
              Если так, то классно. В моем случае профайла не было, вот я и не стал копать глубже. Хотя формат и способ работы у user_save, согласитеть, отщепенческий.
        0
        А я ухожу от Друпал! Написал на 6-ке модуль спортивной статистики (icehockey.su), задумал перенести на 7-ку — и проделав пол работы, бросил это дело. Почему? Да потому, что работа с Друпал сама по себе тяжёлая, медленная — и я просто устал от этого тормоза!
          +3
          Что бы ускорить эту работу нужно как можно больше использовать API Drupal'a. А в некоторых случаях, можно ещё и API сторонних модулей. Но для этого нужно потратить ни один месяц, что бы изучить всё это.

          Вот и автор в статье показал, как ускорить и упростить перевод контента сайтов на друпал с помощью его API.
            0
            А что толку что-то использовать. Сама работа с Друпал, а именно с админкой — это просто гемморой
              +3
              Напишите в «Я негодую», данный топик не самое лучше место для рассказа на тему «Я ниасилил Друпал»
                +1
                LavrenovNN геморой у того, кто не умеет и не знает как «это» делать правильно.
                Кривая обучения в Drupal довольно кривая, невозможно хорошо понять и изучить идеологию и устройство стистемы, тем более изучить хотя бы поверхностно >7000 модулей за 1-2 месяца. Отсюда и стоны новичкой… Нописание своего велика, зачастую им проще, чем понять как делать правильно :)

                Топик стартеру: вы не начать изучение вопроса с проекта migrate?
                  +2
                  Проект migrate я не смотрел. Имхо, более быстро написать свой скрипт импорта, который использует API, чем разбираться со сторонним модулем. Тем более, что наверняка у того модуля есть какие-то ограничения, т.к. всего не предусмотришь.

                  Мое решение более гибкое, хотя и низкоуровневое.
                    0
                    Поздравляю, вы придумали решение — читать документацию. Оно действительно очень гибкое и низком уровне
                    +1
                    Я два года имел дело с Друпал, и знаю его от и до… тем более, имеется опыт написания модуля под него. И что тут правильно делать? О чём вы говорите? Если я работаю в бэкенде, и жду выполнения операций по 10-20 секунд — это нормально? Вот и говорю, я о том, что с Друпалом работать очень сложно из-за его тормозов. Если бы не было с чем сравнить — то и не заикнулся бы. Моё мнение в том, что Друпал стал слишком толстый и неповоротливый, и 7 стала ею ещё больше.
                      0
                      А на что перешли то?
                        +2
                        Я 5-ый год ковыряю друпал и до сих пор знаю его не очень хорошо
                          0
                          Не туда ответил, извиняюсь. Ответ был ЛавреновННу.
                            –1
                            Я не для того подметил, что считаю себя истинным гуру — а в том плане, что не являюсь новичком. Да и что там собственно знать? Стоит пару модулей и тем сделать и всё становится очень понятно, а в чём это отображается — так это система хуков, работа с базой данных, построение форм, взаимодействие с другими модулями, ну и ещё несколько моментов. ВСЁ!!! И можно делать что пожелаешь… а отказ от Друпала обусловлен именно скоростью работы админки, а не фронтенда — где всё что можно, можно закэшировать.
                              0
                              Проведём эксперимент?
                            0
                            pimcore
                            +1
                            Очень странно…

                            Ну а профайлинг mysql пробовали делать? Может надо где-то индексы добавить?

                            Или если очень много кода обрабатывается (это ж сколько тогда должно быть кода?), то пробовали ли использовать кеширование?

                            Голая CMS выдерживает большие объемы данных (десятки тысяч нод), тормоза могут быть из-за дополнительных модулей.
                              +2
                              Хостинг сменить не пробовали?
                              –1
                              Что делать правильно? Работать с админкой? Причём тут кривая? Если на обновление контента состоящей из 10 страниц, у меня уходит пол часа — это нормально? Только не советуйте мне купить новое железо! Не надо… у меня всё в порядке! Другие системы просто летают (modx, joomla1.6, pimcore, typo3 и ...)
                                +2
                                Полчаса — это что-то невероятное. Мы каких то подробностей не знаем?
                          0
                          Да, к сожалению, у друпала есть один существенный недостаток — это высокий порог входа. Требуется много времени, чтобы изучить его.

                          Насчет тормоза не согласен, есть много кеширующих модулей. Мои сайты на друпал просто летают. Нужно только настроить кеширование.
                            0
                            Я думаю у Forbes нагрузка очень немаленькая, тем не менее он прекрасно на друпале пашет.
                              0
                              Forbes
                              Sportbox
                              Это из «Толстых», там соответствующий штат
                              Из поменьше и простых:
                              citaty.info
                              hr-portal.ru
                              fermer.ru (сезонный спад)
                              да даже тот же htmlbook.ru, около 70 000 просмотров, но имеет, вполне неплохо и расположен на виртуальном хостинге, кстати
                                0
                                У нас Казкоммерцбанк один из сайтов на друпале держит, насколько знаю сайт Федекса на друпале, еще че-то было.
                                  0
                                  Ну сайтик Аврил Лавин тоже на нем, да.
                                    0
                                    Я специально указал русско-язычные сайты.
                                    Во-первых я знаю их инфраструктуру, во-вторых чтобы не говорили потом другие «А на Западе у половины сайтов свой ДЦ»
                                      0
                                      А, ну логично.
                                  0
                                  Кстати, они в том году рассказывали на конференции, как его делали. Ничего сверхестественного — Boost поставили и все. Единственное, говорят, нужно было заморочиться с выводом пары динамических блоков и какая-то js-ная карусель изначально с Бустом глючила — надо было ее допилить децл. А в остальном — все стандартно и скучно — cck, views и готовые модули…
                              +1
                              Еще есть Node Import. Это для совсем ленивых.
                                0
                                Не. Шаг влево — шаг вправо, и модуль разводит руками и хлопает глазками, не находя себе оправдания. Только API. А с 7-ки — так совсем просто.
                                +3
                                ImageCache, возможно, не генерирует файлы из-за того, что у многих хостеров картинки обрабатываются сразу nginx-ом (404 не доходит до бекенда), сам с этим провозился в свое время на шареде (уже думал и картинки делать вида file.jpg.htm, чтобы до бекенда доходило), решил — костылями в шаблоне ноды (вызывать файл прямым запросом к ядру — site.com/?q=path/imagecache/file.jpg, заполнять например alt, а для картинки с заполненным альтом — выводить прямой путь), тогда картинка сохраняется и все работает
                                  0
                                  Я всяко пробовал: и на шареде, и на собственном сервере и nginx и локально на денвере — везде глючит. Проще принудительно генерить изображения, чем ждать когда ImageCache сообразит.
                                    0
                                    Только у вас. Всё отлично работает, если ngnix нормально настроен.
                                    Просто 404 он должен отдавать по цепочке друпалу, а не обрабатывать сам.
                                    На одном хостинге такое было, я описал суть, хостеры настроили именно это, как именно, не уточнял.

                                    Про денвер ничего сказать не могу, он на меня сразу произвёл впечатление собранной на коленке поделки школьников, может там .htaccess не обрабатывается. Wamp и Vertrigo отлично работают сразу.
                                    0
                                    sysoev.ru/nginx/docs/faq.html — читать в самом низу

                                    Вообще, довольно распространенная трабла — imagecache слабо дружит с nginx'ом без особого шаманства. Суть траблы заключается в том, nginx некорректно работает с путями, и при попытке аплоада просто не видит куда складывать файлы, а потому отправляет в /dev/null
                                    +5
                                    а еще начиная с шестого друпала есть batch operations, которые позволяют не вылезти за пределы time limit PHP. затравка: api.drupal.org/api/drupal/includes--form.inc/group/batch/6

                                    господа, в друпале все есть. Читайте книжки и доки.
                                      +1
                                      Так же добавлю про очереди введённые в Drupal 7 и бекпортированные на Drupal 6
                                      +1
                                      Для больших и сложных переездов рекомендую модуль Migrate.
                                      Мощная штука.
                                      Есть интеграция с drush, т.е. проблем с объемами и тайм лимитами не будет.
                                      Можно полностью откатить «чужеродный» контент в любой момент, и накатить по заново.
                                        0
                                        Уже второй человек мне рекомендует этот модуль, придется его посмотреть)))
                                          +2
                                          Ну это одно из самых универсальных(опять же одно из...) решений.
                                          Не вы первый сталкивались с миграцией, и возникающих проблем может быть гораздо больше.

                                          Посмотрите отдельно node_import, user_import. Для 7 интересный модуль datasources.
                                          Для большинста популярных систем есть готовые решения, такие как: wp2drupal, phpnuke2drupal, phpbb2drupal, joomla2drupal, spip2drupal, vbtodrupal…

                                          Просматривайте иногда список по тегу drupaler.ru/tag/module/function/migratsiya
                                          0
                                          Для действительно больших и сложных переездов всё-таки приходится писать руками, увы. Впрочем, зависит от ситуации.
                                          0
                                          еще советую взглянуть на модуль Feeds
                                            +1
                                            Совершенно непонятно какое отношение имеет заголовок к содержимому.

                                            Что я вижу в статье? Три литра воды и пример использования node_save. Ещё генерация картинок, которая глючит только у автора.

                                            Если так хочется изобрести свой велосипед для импорта контента из других БД, то, как минимум, надо описать: node_save, user_save, taxonomy_term_save, drupal_write_record и, самое главное!, детально разобрать Batch API! Иначе при импорте нескольких десятков тысяч материалов или пользователей, ваш скрипт загнётся на первых сотнях, если не поменять лимиты PHP на исполнение (а на шаред хостингах это не всегда разрешено).

                                            Если же велосипед изобретать неохота, тогда надо сделать обзор модулей импорта, в том числе migrate. Аргумент «я его не осилил» не должен присутствовать в статье с таким заголовком.
                                              0
                                              Статья практически ни о чём… Всё что осталось послевкусием от прочтения «что-то можно как-то переносить в Drupal».
                                                0
                                                Просто кто-то узнал, что ноды можно создавать не только через модули импорта ;)
                                                  0
                                                  заголовок просто неправильный, скорее всего «Импорт данных в CMS Drupal».
                                                  0
                                                  Делал и так, затем стал использовать модуль migrate (в D6 и D7). «Ваш» (обычный то есть) способ хорошо подходит для небольшого беспроблемного переноса, когда всё делается за 1 проход и один раз. Migrate же позволяет «обкатать» схему миграции, попробовать, откатиться назад и что-либо доделать (он хранит карту связей новое-старое).

                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                  Самое читаемое