Как мне кажется сейчас "Переход с Drupal 7 на Drupal 11" более чем актуальная тема (тех поддержка Друпала 7 заканчивается полностью), но на удивление очень мало информации по этому поводу. У меня возникла такая задача и я уже несколько дней пытаю ДипСик по этому поводу, но как то не очень он справляется. Поэтому просьба поделитесь опытом у кого такой имеется.
У меня достаточно простой сайт без замороченной таксономии и меню, поэтому это всё можно сделать ручками. Но на сайте достаточно много статей. Сам я несколько лет назад пробовал переходить через модуль Мигрейт, но мне такой "переход" очень не понравилось. Все статьи перенеслись как то очень криво. Подумал ладно, подожду. Наверняка за это время светлые головы что то придумают..
Но нет. Ничего придумано за это время не было (я по крайней мере не нашёл).
В этот раз решил просто выгрузить статьи с 7ки в CSV и потом залить их через модуль Feeds в 11. Попробую одинаково настроить старые пути к страницам, а те что не смогу настроить просто пропишу через 301 редирект. Но тут тоже возникли проблемы.
1. Модуль Views Bulk Operations не работает у меня 7ке. Саму страницу Views сформировал, а выгрузить её через экспорт в CSV не смог. Ладно думаю перенесу ручками через ексель и потом сохраню в CSV .
2. Но на Друпале 11 модуль Feeds так же не заработал полностью Feeds_UI не устанавливается через Composer (пробовал много раз по разному). А без него не могу отладить загрузку..
3. Мне интересно почему под такую востребованную задачу до сих пор нет более менее внятного решения? Даже подумываю может на Вордпрес перейти пока не поздно? Друпал становится менее доступным простым пользователем как я?
Комментарии
Когда ничего не помогает надо читать документацию. Ссылку дам позднее. Я обновлялся через drush. Вьювсы пришлось делать заново.
https://www.drupal.org/docs/upgrading-drupal/upgrading-from-drupal-6-or-...инструкция.
@Zeratul и надеюсь @Sletelena, вариант с views data export и feeds полностью рабочий.
И для 7ки все устанавливается и для 11ки. Вчера проверял. Что-то не так делаете.
Попробовать через встроенного migrate перенести информацию на D11 сайт можно, если сайт простой. Для этого на новом сайте нужно воссоздать типы контента (термины, пользователей) с теми же полями, что и на старом и указать модулю migrate БДу строго сайта. Да он может подтянуть контент. Но все равно придется много доливать (через feeds).
Вам надо:
Научится разворачивать Drupal 11 с Composer и им же ставить модули. Это не сложно. Если получается не сразу - это нормально.
Надумаете переходить на Вордпресс - попробуйте установить Вордпресс и воссоздать функционал сайта. Если функционал простой - может и прокатит.
А можно спросить у вас. При установке Feeds у вас в папке
web/modules/contrib/feeds/modules есть папка Feeds_UI или
/modules/contrib/feeds_ui
У меня нету.. а ДипСик говорит что должна быть.. И поэтому я не вижу кнопки визуальной настройки загрузки из CSV файла.
Это я научился.. всё устанавливаю Компостером.
ставил вот так
composer require 'drupal/feeds:^3.0'
и вот так
composer require drupal/feeds
Эффект одинаковый
Я так понимаю что вначале нужно всё равно термины и таксономию перетаскивать с помощью Feeds? Или мигрейт с этим справится сам?
ЧатГПТ и Дипкик - хорошие инструменты, но ошибаются.
feeds установили - замечательно. feeds_ui это подмодуль от старой версии feeds, сейчас его ставить не надо. В этом и есть ошибка Дипсика. /admin/structure/feeds - если тут есть кнопка "Add feed type" - вы на правильном пути.
Термины может мигрейт перенесет, а вот связи между терминами и нодами - не знаю. Мигрейтетом сложнее.
Да кнопка такая есть.. Но тогда как проверить соответствие полям которое я назначил? Как посмотреть что читается из CSV файла? То что я настроил не зашло.. ДипСик сказал что именно модуль Feeds_UI это может делать. Если не он, то кто?
Спросите Дипсик существует ли Feeds_UI для D11? Намекните, что это подмодуль для D7. Можете на меня сослаться если он не верит.
Проверить соответствия. 2 сайта открываете и сравниваете:
D7: /admin/structure/types/CONTETNT_TYPE/news/fields
D11: /admin/structure/types/manage/CONTETNT_TYPE/fields
Экспорты через views data export разобрались. У Дипсик спросите.
И можете делать импорты кнопкой Add feed type
Я спрашивал.. И спросил ещё раз.. Вот что он мне ответил:
существует ли Feeds_UI для D11?
Вот как выглядит его правильная и полная структура после установки через Composer:
text
web/
├── modules/
│ └── contrib/
│ └── feeds/ # Основной модуль (пакет drupal/feeds)
│ ├── modules/
│ │ └── feeds_ui/ # Подмодуль Feeds UI
│ │ ├── src/ - Исходный код
│ │ ├── feeds_ui.info.yml - Файл описания модуля
│ │ └── ...
│ ├── src/
│ ├── feeds.info.yml
│ └── ...
└── ...
Ну запутался робот. Вы хотите чтоб он за вас всю работу делал?
Намекните ему: почему же этих файлов нет в коде модуля начиная с 8й версии?
https://git.drupalcode.org/project/feeds
Намекнул.. Но ДипСик не сдаётся и стоит на своём. )) Вот что он ответил :
Раньше (для D7 и старых версий D8):
feeds (ядро)
feeds_ui (отдельный подмодуль для интерфейса)
Теперь (для актуальных версий D8/D9/D10/D11):
feeds (ядро + весь UI внутри себя)
Утверждает что в админке всё равно должно быть два модуля..
Feeds
Feeds_UI
Дело оказалось в модуле Views Bulk Operations именно он отвечает за галочку (Выгрузить в CSV) я создавал тут тему не работает модуль Views Bulk Operations но вопрос так и не решился.
Но так как само представление формируется, в табличном виде нормально. Я решил сделать так:
1. Просто скопировать с экрана ХТМЛ таблицу, Crl +C
2. Вставить её в Эксель и потом сохранить в CSV.
3. Загрузить на новый сайт Drupal 11.
Но на простом тесте на этапе загрузки возникает ошибка:
Название (name): Значение не может быть пустым.
Проверьте соответствия в настройках потока данных.
Перелопатил весь инет нет не одного намёка на решение! Поэтому я достаточно долго тупил на неё. Но всё дело оказалось в банальной перекодировке. Преобразовал в UTF8 (без BOM) и всё заработало.
На рабочей выгрузке в эксель, там где в тексте был перевод строки (Абзац), Ексель добавил дополнительные строки. Но таких мест было не много, я поправил файл ручками и в итоге всё успешно залил на сайт Drupal 11.
Благо таких строчек было не много поэтому всё успешно залил на новый Drupal 11.
Всю таксономию таким образом перенёс, довольно успешно..
Но перенести таким образом статьи не получается. По двум причинам:
1. Именно из за вставке Eкселем лишних строк. Править их ручками просто не реально (в каждой статье их штук по 10).
Таким образом можно перенести структуру статей со связями, ссылками и самое главное со старым URL, а вместо текста статьи ставить одну строчку. И уже потом ручками открывая редактирование каждой статьи на Drupal 7 и на Drupal 11, через Ctr +C переносить контент. Для маленького количества статей как вариант.
Понятно что перенос контента таким образом для серьёзной CMS просто смешон. Но когда нет выбора..
2. Из за наличие картинок и ссылок внутри статей. Все они будут потеряны при переносе в таком формате.
Может кто ещё что то подскажет как можно решить данную проблему?
Как я думаю здесь нужно как то читать HTML код статьи, упаковывать его, затем экспортировать. А при импорте распаковывать.
Я бы так делал. Код можно расположить в собственном модуле, в файле ***.theme подцепится на один из хуков, в целом как удобно будет.
1. На сервере, где mysql 8 поднял бы базу от друпал 7. Чуть выше в комментариях я про это описывал.
2. В конфиге settings.php подключаем использование 2 баз. Примерно так:
<?php
// Это база от друпал 10
$databases['default']['new_db_name'] = array(
'database' => 'new_db_name ',
'username' => 'username',
'password' => 'password',
'prefix' => '',
'host' => 'localhost',
'port' => '3306',
'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
'driver' => 'mysql',
); // Это база от друпал 7
$databases['external']['old_db_name'] = array(
'database' => 'old_db_name',
'username' => 'username',
'password' => 'password',
'prefix' => '',
'host' => 'localhost',
'port' => '3306',
'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
'driver' => 'mysql',
);
?>
3. Пишем функцию, которая получает статьи из старой базы, затем перебираем их в цикле. В каждой итерации цикла, модифицируем данные если нужно. Затем сохраняем в новую базу.
Естественно в новой базе должен быть создан материал, который вы переносите и необходимые поля.
Таким образом вы сможете реализовать любую логику для переноса данных.
Данный код указывает друпалу, какую базу использовать для работы.
<?php
\Drupal\Core\Database\Database::setActiveConnection('new_db_name');
// или
\Drupal\Core\Database\Database::setActiveConnection('old_db_name');
?>
1. Да базу перенести на новый сервак и конвертировать в SQL8 я смогу.
Но к сожалению я не настолько хорош в ПХП (точнее сказать я в нём очень плох), что бы написать функцию "перенести всё". )) Я не знаю в каких таблицах где и что хранится и в каком виде. Можно это переносить напрямую из таблицы в таблицу или в Друпал 11 несколько иной формат хранения статей, и т.д. Здесь много моментов которые понятны программистам Друпал но не понятны простым смертным.
2. Если можно копировать напрямую из одной БД в другую, то вообще можно всё сделать одним SQL запросом. Если предварительно создать идентичную структуру, тем способом который я описал выше..
3. Немного лирики: Я понимаю, что так сделать для реального специалиста не сложно.. А вмести с эти пониманием сразу возникает вопрос: Почему разработчики Друпал до сих пор не сделали что то такое простое и внятно понятное среднестатистическому пользователю? Они не заинтересованны в том что бы удержать пользователей на своей системе? На форуме WordPress постоянно висит тема "Как с Drupal перейти на WordPress", а здесь такая тема уже очень давно не появляется. Хотя раньше попадались.
Используем не VBO, а views data export. Там можно создать csv файл.
Этот файл открываем в libbre office calc и проверяем что нет пустых строк.
"Из за наличие картинок и ссылок внутри статей. Все они будут потеряны при переносе в таком формате." - там можно сохранять HTML код при экспорте. И при импорте указывать формат ввода с поддрежкой этого формата.
"Немного лирики". Становитесь реальным специалистом.
wordpress
https://wordpress.org/support/plugin/fg-drupal-to-wp/reviews/ - пишут в отзывах, что не все переносится
Решили перейти - попробуйте создать на ВП контентную структуру подобную той что сейчас. Увидите, что на Друпале проще. И да там много платных модулей за которые вы не понятно как платить будете.
"...Я не знаю в каких таблицах где и что хранится и в каком виде. Можно это переносить напрямую из таблицы в таблицу или в Друпал 11 несколько иной формат хранения статей, и т.д...."
Если использовать функции друпала для работы с материалами, то описанные проблемы не важны. Если в гугле вбить "drupal 7 get nodes by type", то ИИ гугла сразу выдает готовый код для получения материалов. Вы же сделали скрипт или скачали и разобрались в нем, тот, который тестирует подключение к базе. Получение и сохранение материалов в друпале не сложнее будет)
По пункту 2 - не надо так делать, лучше использовать функции друпала.
По пункту 3.
"Почему разработчики Друпал до сих пор не сделали что то такое простое и внятно понятное среднестатистическому пользователю?"
Универсальные решения всегда сложны в разработке, требуют много времени. Так же созданный модуль требует поддержки и решения багрепортов от сообщества. Иначе смысла от модуля нет. Вопрос цены.
Всегда проще сделать импорт под конкретный проект и получить за это оплату.
Они не заинтересованны в том что бы удержать пользователей на своей системе?
Да, друпал не дружелюбен к пользователю.
Хотел бы отдельно сказать о WordPress.
По своей работе я с этой системой встречаюсь очень часто, как правило 1-2 сайта в месяц на ней приходят. За 8 лет коммерческой разработки, я могу только пару сайтов вспомнить, которые работали бы быстро на WordPress. Дружелюбие к пользователю отличное, в админке все понятно. Но огромный минус этой системы в том, что она почти все хранит в таблице wp_postmeta. В итоге эта таблица раздувается до огромных размеров, в результате сайт тормозит. По этому на WordPress я почти всегда вижу 2, а то и 3 модуля кеширования). Последний сайт, который у меня был, там wp_postmeta выросла до 9 гб.
Я не говорю, что WordPress ужасен. Для каких то задач это отличная система, но всегда стоит взвесить за и против.
Это я тоже пробовал.. Не появляется у меня опция "Экспорт в csv"
Спасибо. Счас попробую что за зверь..
https://www.drupal.org/node/1820452 инструкция. С картинками. ChatGPT точно на вопросы по модулю отвечает. Проверял.
Благодарю.. Попробую изучить тему..
Получилось выгрузить скриптом из Друпал 7 свои статьи.!! Большое спасибо за наводку.
Затем я почти сутки пытался загрузить это все через Drush Migrate .. но это просто гиблое дело ))
Потом я всё бросил и написал с помощью ИИ скрипт загрузки (если кому интересно могу потом поделиться своими скриптами)..
Ещё такой вопрос. Неудачную заливку через скрипты я могу обнулять njkmrj воcстановлением бекапа MySQL? Или в файлы тоже что то пишется системой и поэтому файлы тоже нужно восстанавливать?
Если картинки не переносили через скрипт, то в файлах ничего нет. Только в базе.
Восстанавливать из бекапа базу не обязательно, можно все, что перенесли так же удалить через функции дурпала. Он тогда и картинки удалит, если переносили.
Кстати удаление файлов в друпале может вызвать непонимание. Там получается так, что когда вы загружаете файл через админку друпала или связываете файл с материалом через код или используете CKEditor редактор, то физически файл размещается в папке sites\default\files. Так же информация о файле сохраняется в базе.
Так вот когда вы средствами друпала удаляете файл, то в базе файл помечается, как "На удаление", а физически с сервера он сразу не удаляется. Физическое удаление файла произойдет после запуска крона.
"Потом я всё бросил и написал с помощью ИИ скрипт загрузки" - ужас.
CKEditor и форматы ввода.
Через views data export когда экспорты делаете, там есть опция "сохранять HTML". По умолчанию отключена. Можно несколько экспортов для одного типа контента сделать.
В feeds можно указывать формат ввода при импорте. feeds ничего не режет (хотя умеет). Даже если формат ввода не поддерживает что-то это что-то можно в формат ввода добавить.
В админке модулей посмотрите: там CKEditor есть?
"Потом я всё бросил и написал с помощью ИИ скрипт загрузки" - ужас.
Видимо такие текущие реалии наступают с ИИ
Не заработал у меня этот модуль нормально.
feeds всё режет и не сохраняет в CSV
Согласен.. Но Drupal мне не оставил другого выбора.
В корне Drupal 7 нужно создать папку zExport. Файл D7_csv_export_utf8.php нужно скопировать в папку zExport.
Сейчас файл настроен на выгрузку всего Материала. Для выгрузки конкретного материала (только статей) уберите комментарий в этой строчке
<?php//->entityCondition('bundle', 'article')//Выгружать только статьи типа article?>
После запуска скрипт создаст в папке файл D11_articles_export.csv. Запускать можно из браузера.
Затем файл D11_articles_export.csv нужно будет скопировать в папку Dropal 11\Web туда же скопировать D11_import_articles.php и D11_chek_csv.php
D11_chek_csv.php - проверяет целостность CSV файла
D11_import_articles.php - запускает процесс экспорта.
Запускать нужно из командной строки.
Вот сами скрипты
D7_csv_export_utf8.php
<?php
// Bootstrap Drupal
define('DRUPAL_ROOT', dirname(__DIR__));
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); // Получаем статьи
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
//->entityCondition('bundle', 'article')//Выгружать только статьи типа article
->propertyCondition('status', 1)
->propertyCondition('title', '%404%', 'NOT LIKE')
->propertyOrderBy('created', 'DESC'); $result = $query->execute();
if (isset(
$result['node'])) {$nids = array_keys($result['node']);
$nodes = node_load_multiple($nids);
// Создаем CSV файл
$csv_file = DRUPAL_ROOT . '/zExport/D11_articles_export.csv';
$handle = fopen($csv_file, 'w');
// Заголовки CSV
fputcsv($handle, [
'nid', 'title', 'body', 'created', 'changed', 'author',
'url', 'image_fid', 'tags'
], ';');
foreach ($nodes as $node) {
// Пропускаем системные страницы
if (strpos(strtolower($node->title), '404') !== false) {
continue;
}
// Получаем тело статьи
$body = '';
if (!empty($node->body['und'][0]['value'])) {
$body = $node->body['und'][0]['value'];
}
// Получаем изображение (если есть)
$image_fid = '';
if (!empty($node->field_image['und'][0]['fid'])) {
$image_fid = $node->field_image['und'][0]['fid'];
}
// Получаем теги (если есть)
$tags = '';
if (!empty($node->field_tags['und'])) {
$tag_names = [];
foreach ($node->field_tags['und'] as $tag) {
$term = taxonomy_term_load($tag['tid']);
if ($term) {
$tag_names[] = $term->name;
}
}
$tags = implode(',', $tag_names);
}
// Приводим данные к UTF-8
$data = [
'nid' => mb_convert_encoding($node->nid, 'UTF-8'),
'title' => mb_convert_encoding($node->title, 'UTF-8'),
'body' => mb_convert_encoding($body, 'UTF-8'),
'created' => date("Y-m-d H:i:s", $node->created),
'changed' => date("Y-m-d H:i:s", $node->changed),
'author' => mb_convert_encoding($node->name, 'UTF-8'),
'url' => mb_convert_encoding(url('node/' . $node->nid, ['absolute' => TRUE]), 'UTF-8'),
'image_fid' => mb_convert_encoding($image_fid, 'UTF-8'),
'tags' => mb_convert_encoding($tags, 'UTF-8')
];
fputcsv($handle, $data, ';');
echo "Добавлена в CSV: " . $node->title . "\n";
}
fclose($handle);
echo "\nCSV файл создан: " . $csv_file . "\n";
echo "Всего статей: " . count($nodes) . "\n";
} else {
echo "Статьи типа 'article' не найдены!\n";
}
?>
D11_chek_csv.php
<?php /**
* Простая проверка CSV файла для Drupal 11
* Использование: php check_csv_simple.php
*/
// Проверяем, не запущено ли это через веб-сервер
if (php_sapi_name() !== 'cli') {die("Этот скрипт должен запускаться из командной строки\n");
} $csv_file = 'D11_articles_export.csv'; // Файл должен быть в той же папке
$delimiter = ';';
echo
"=== ПРОВЕРКА CSV ФАЙЛА ===\n\n"; // Проверка существования файлаif (!file_exists($csv_file)) {
die("❌ Файл не найден: $csv_file\n");
}
echo
"✅ Файл найден: $csv_file\n";echo "ߓʠРазмер: " . filesize($csv_file) . " байт\n\n"; // Чтение файла
$handle = fopen($csv_file, 'r');
if (!$handle) {
die("❌ Не удалось открыть файл\n");
} // Заголовок
$header = fgetcsv($handle, 0, $delimiter);
if (!$header) {
fclose($handle);
die("❌ Не удалось прочитать заголовок CSV\n");
}
echo
"ߓݠЗаголовки: " . implode(', ', $header) . "\n\n"; // Анализ данных$row_count = 0;
$empty_titles = 0;
$empty_bodies = 0;
$problem_rows = [];
echo
"ߔ͠Анализ данных...\n";while ((
$data = fgetcsv($handle, 0, $delimiter)) !== FALSE) {$row_count++;
// Пропускаем полностью пустые строки
if (count(array_filter($data, 'strlen')) === 0) {
continue;
}
// Проверяем количество колонок
if (count($data) !== count($header)) {
$problem_rows[] = [
'row' => $row_count + 1,
'issue' => 'Несоответствие количества колонок',
'data' => $data
];
continue;
}
$row_data = array_combine($header, $data);
// Проверяем обязательные поля
if (empty($row_data['title']) || trim($row_data['title']) === '') {
$empty_titles++;
$problem_rows[] = [
'row' => $row_count + 1,
'issue' => 'Пустой заголовок',
'data' => $row_data
];
}
if (empty($row_data['body']) || trim($row_data['body']) === '') {
$empty_bodies++;
$problem_rows[] = [
'row' => $row_count + 1,
'issue' => 'Пустое тело статьи',
'data' => $row_data
];
}
} fclose($handle);
echo
"\n=== РЕЗУЛЬТАТЫ ПРОВЕРКИ ===\n";echo "Всего строк: $row_count\n";
echo "Пустых заголовков: $empty_titles\n";
echo "Пустых тел статей: $empty_bodies\n";
echo "Проблемных строк: " . count($problem_rows) . "\n";
if (!empty(
$problem_rows)) {echo "\n⚠️ Внимание: Найдены проблемы:\n";
foreach (array_slice($problem_rows, 0, 5) as $problem) {
echo " Строка {$problem['row']}: {$problem['issue']}\n";
}
if (count($problem_rows) > 5) {
echo " ... и еще " . (count($problem_rows) - 5) . " проблем\n";
}
echo "\n❌ Рекомендуется исправить CSV перед импортом.\n";
} else {
echo "\n✅ Файл готов к импорту!\n";
}
exit(
0);?>
D11_import_articles.php
<?php
use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
use Drupal\taxonomy\Entity\Term;
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Language\LanguageInterface; // Проверяем, не запущено ли это через веб-сервер
if (php_sapi_name() !== 'cli') {
die("Этот скрипт должен запускаться из командной строки\n");
} // Функция логирования
function log_message($message, $level = 'INFO') {
$log_file = 'import_articles_fixed.log';
$timestamp = date('Y-m-d H:i:s');
$log_entry = "[$timestamp] [$level] $message\n";
file_put_contents($log_file, $log_entry, FILE_APPEND);
echo $log_entry;
}
try {
// Загрузка Drupal 11$autoloader = require_once 'autoload.php';
$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$site_path = DrupalKernel::findSitePath($request);
$kernel->setSitePath($site_path);
$kernel->boot();
$kernel->preHandle($request);
log_message("Drupal 11 bootstrap успешен");
} catch (Exception $e) {
die("❌ Ошибка загрузки Drupal: " . $e->getMessage() . "\n");
} // Настройки
$csv_file = 'D11_articles_export.csv';
$delimiter = ';'; // Проверка существования CSV файла
if (!file_exists($csv_file)) {
log_message("CSV файл не найден: $csv_file", 'ERROR');
exit(1);
}
log_message("CSV файл найден: $csv_file"); // Чтение CSV
$handle = fopen($csv_file, 'r');
if (!$handle) {
log_message("Не удалось открыть CSV файл", 'ERROR');
exit(1);
} $header = fgetcsv($handle, 0, $delimiter);
if (!$header) {
fclose($handle);
log_message("Не удалось прочитать заголовок CSV", 'ERROR');
exit(1);
}
log_message("Заголовки CSV: " . implode(', ', $header)); // Импорт данных
$imported_count = 0;
$skipped_count = 0;
$error_count = 0;
$row_count = 0; log_message("Начинаем импорт...");
while ((
$data = fgetcsv($handle, 0, $delimiter)) !== FALSE) {$row_count++;
try {
// Пропускаем пустые строки
if (count(array_filter($data, 'strlen')) === 0) {
continue;
}
if (
count($data) !== count($header)) {throw new Exception("Несоответствие количества колонок");
}
if (empty($article_data['title']) || trim($article_data['title']) === '') {
throw new Exception("Пустой заголовок");
}
if (empty($article_data['body']) || trim($article_data['body']) === '') {
throw new Exception("Пустое тело статьи");
}
$existing = \Drupal::entityQuery('node')
->condition('type', 'article')
->condition('title', $article_data['title'])
->accessCheck(FALSE)
->execute();
if (!empty($existing)) {
log_message("Пропущена строка $row_count: статья уже существует - '" . $article_data['title'] . "'", 'WARNING');
$skipped_count++;
continue;
}
$node_data = [
'type' => 'article',
'title' => $article_data['title'],
'body' => [
'value' => $article_data['body'],
'format' => 'basic_html',
],
'status' => 1,
];
$raw_date = trim($article_data['created']); // строка из CSV, ожидаем “Y-m-d H:i:s”
$format = 'Y-m-d H:i:s';
$dt = \DateTime::createFromFormat($format, $raw_date);
if ($dt && $dt->format($format) === $raw_date) {
$timestamp = $dt->getTimestamp();
log_message("Установлена дата создания из CSV: {$raw_date}", 'INFO');
}
else {
// Фоллбек на “2011-01-01 00:00:00”
$fallback = '2011-01-01 00:00:00';
$dt = \DateTime::createFromFormat($format, $fallback);
$timestamp = $dt->getTimestamp();
log_message("Некорректная дата '{$raw_date}', использовано '{$fallback}'", 'WARNING');
}
$node_data['created'] = $timestamp;
$node_data['changed'] = $timestamp;
if (!empty($article_data['author'])) {
$users = \Drupal::entityTypeManager()
->getStorage('user')
->loadByProperties(['name' => $article_data['author']]);
if (!empty($users)) {
$user = reset($users);
$node_data['uid'] = $user->id();
log_message("Найден автор: " . $article_data['author']);
} else {
$node_data['uid'] = 1;
log_message("Автор не найден, используется администратор", 'WARNING');
}
}
if (!empty($article_data['tags'])) {
$tag_ids = [];
$tags = explode(',', $article_data['tags']);
foreach ($tags as $tag_name) {
$tag_name = trim($tag_name);
if (!empty($tag_name)) {
$terms = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties([
'name' => $tag_name,
'vid' => 'tags',
]);
if (!empty($terms)) {
$term = reset($terms);
$tag_ids[] = $term->id();
log_message("Найден существующий тег: " . $tag_name);
} else {
$term = Term::create([
'name' => $tag_name,
'vid' => 'tags',
'status' => 1,
]);
try {
$term->save();
$tag_ids[] = $term->id();
log_message("Создан новый тег: " . $tag_name);
} catch (Exception $e) {
log_message("Ошибка создания тега '$tag_name': " . $e->getMessage(), 'WARNING');
}
}
}
}
if (!empty($tag_ids)) {
$node_data['field_tags'] = $tag_ids;
log_message("Добавлены теги: " . implode(', ', $tag_ids));
}
}
$node = Node::create($node_data);
$node->save();
if (!empty($article_data['url'])) {
try {
$url_alias = str_replace('https://zemius.ru/', '', $article_data['url']);
if (strpos($url_alias, '/') !== 0) {
$url_alias = '/' . $url_alias;
}
$path_alias_manager = \Drupal::service('path_alias.manager');
$existing_alias = $path_alias_manager->getAliasByPath('/node/' . $node->id());
if ($existing_alias !== '/node/' . $node->id()) {
$path_alias_repository = \Drupal::service('path_alias.repository');
$existing = $path_alias_repository->lookupByAlias($existing_alias, 'ru');
if ($existing) {
$path_alias_storage = \Drupal::entityTypeManager()->getStorage('path_alias');
$entities = $path_alias_storage->loadByProperties(['alias' => $existing_alias]);
if (!empty($entities)) {
reset($entities)->delete();
}
}
}
$path_alias = \Drupal\path_alias\Entity\PathAlias::create([
'path' => '/node/' . $node->id(),
'alias' => $url_alias,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
]);
$path_alias->save();
log_message("Создан URL alias: " . $url_alias . " → /node/" . $node->id());
} catch (Exception $e) {
log_message("Ошибка создания URL alias: " . $e->getMessage(), 'WARNING');
}
}
log_message("Успешно импортирована строка $row_count: '" . $article_data['title'] . "' (ID: " . $node->id() . ")");
}
catch (Exception $e) {
$error_count++;
log_message("Ошибка в строке $row_count: " . $e->getMessage(), 'ERROR');
}
} fclose($handle); // Итоговый отчет
log_message("=== ИТОГИ ИМПОРТА ===");
log_message("Всего обработано строк: " . $row_count);
log_message("Успешно импортировано: $imported_count");
log_message("Пропущено (дубликаты): $skipped_count");
log_message("Ошибок импорта: $error_count"); // Детальная проверка результатов
log_message("\n=== ДЕТАЛЬНАЯ ПРОВЕРКА РЕЗУЛЬТАТОВ ===");
try {
$imported_nodes = \Drupal::entityQuery('node')
->condition('type', 'article')
->accessCheck(FALSE)
->execute();
log_message("Всего статей в системе: " . count($imported_nodes));
$sample_nodes = \Drupal::entityTypeManager()
->getStorage('node')
->loadMultiple(array_slice($imported_nodes, 0, 3));
foreach ($sample_nodes as $node) {
$alias = \Drupal::service('path_alias.manager')->getAliasByPath('/node/' . $node->id());
$created_date = date('Y-m-d H:i:s', $node->getCreatedTime());
$tags = [];
if ($node->hasField('field_tags') && !$node->get('field_tags')->isEmpty()) {
foreach ($node->get('field_tags')->referencedEntities() as $term) {
$tags[] = $term->getName();
}
}
log_message("Статья: '" . $node->getTitle() . "'");
log_message(" - ID: " . $node->id());
log_message(" - URLalias: " . $alias);
log_message(" - Дата: " . $created_date);
log_message(" - Теги: " . (empty($tags) ? 'нет' : implode(', ', $tags)));
log_message(" ---");
}
} catch (Exception $e) {
log_message("Ошибка при проверке результатов: " . $e->getMessage(), 'WARNING');
}
exit(
0);?>
Да там есть CKEditor но он не позволяет править код как раньше, нет режима "PHP code". И если у меня в коде было заданно что то типа:
<img alt="гриб" longdesc="Про грибы " src="/sites/default/2020/mushrooms.jpg" style="width: 400px; height: 348px; border-width: 3px; border-style: solid; margin: 3px; float: left;" title="гриб" />Гриб</h3>
То после экспорта я не вижу данных строчек.. Хотя они есть..
Так вот вопрос: Есть какой то модуль (редактор) для Drupal 11 который может мне помочь?
Формат ввода "PHP код" он и на 7ке должен был быть отключен.
https://drupal.ru/docs/veb-masteram-i-vladelcam-saytov/ne-ispolzuyte-php...
"что то типа" - переносится.
Не видите данных строчек после экспорта в csv файле или после импорта?
В csv файле они есть.. И они переносятся в Д11, но отбражает он их как бы через раз. Когда ставишь "Ограниченный HTML" они возникают, но не отрабатывают..
А можно как то во время экспорта получить доступ к картинке которая есть у статьи Друпал 11 в базовой конфигурации? Можно как то из кода заполнять сущность Изображение?
Я так понял лучше пользовать базовый функционал Друпала тогда с переходом на новые версии проблем меньше будет
В csv файле они есть.. И они переносятся в Д11, но отбражает он их как бы через раз. Когда ставишь "Ограниченный HTML" они возникают, но не отрабатывают..
А можно как то во время экспорта получить доступ к картинке которая есть у статьи Друпал 11 в базовой конфигурации? Можно как то из кода заполнять сущность Изображение?
Я так понял лучше пользовать базовый функционал Друпала тогда с переходом на новые версии проблем меньше будет
"И они переносятся в Д11, но отображает он их как бы через раз. " - в формат ввода "полный HTML" импортируйте. Еще вариант кириллица в названиях файлов. Или абсолютный путь используется, а на новом тестовом сайте другое имя домена. Или изображение не перенесено в папку на новом сайте. Думайте.
"А можно как то во время экспорта получить доступ к картинке которая есть у статьи Друпал 11 в базовой конфигурации? Можно как то из кода заполнять сущность Изображение?" думаю можно, но я не пойму что.
"Я так понял лучше пользовать базовый функционал Друпала тогда с переходом на новые версии проблем меньше будет" - логично - меньше модулей, меньше проблем
Это базовая картинка которая предусматривается к каждой статье..
Как это правильно называется?
Это называется поле изображения.
Просто перенести изображение из этого поля в соотв. поле на новом сайте? Да, способами описанными выше.
Извлечь ссылку на изрображение из текста ноды и вставить в это поле? Возможно. Сначала контент перенесите, потом будете этим заниматься. Возможно с ИИ.
Разобрался.. у меня было небольшая путаница с путями к файлам и тем что режим был выбран не "полный HTML"
Я всё перенёс.. Благодаря скриптам выложенным выше. Надеюсь что они помогут кому то ещё.