Миграция является одним из наиболее авторитетных модулей в экосистеме Drupal. Настолько, что в Drupal 8 было принято решение перенести некоторые его функции и добавить их в ядро Drupal. Важной причиной было то, что традиционное обновление между основными выпусками было заменено миграцией контента и конфигурации Drupal 6 или 7 на Drupal 8.
Не все функциональные возможности модуля Migrate были перенесены в ядро. Скорее, у нас есть базовая структура в модуле migrate
ядра и функциональность, обслуживающая путь обновления с Drupal 6 и 7 в модуле ядра migrate_drupal
. То, что осталось, можно найти в множестве модулей contrib. Наиболее важным является Migrate Tools, который содержит, помимо прочего, команды drush и пользовательский интерфейс для запуска миграций. Кроме того, модули Migrate Source CSV , Migrate Source XML и Migrate Source JSON предоставляют плагины для наиболее используемых типов источников миграции.
В этой статье мы рассмотрим, как работает миграция в Drupal 8 путем миграции некоторого контента в сущности узлов. Для простоты данные, с которыми мы играем, находятся в таблицах в той же базе данных, что и наша установка Drupal. Если вы хотите увидеть окончательный код, вы можете проверить его в этом репозитории .
Drupal 8 Migration Theory
Структура миграции в Drupal 8 состоит из трех основных частей: источника, процесса и места назначения, причем все три строятся с использованием новой системы плагинов Drupal 8 . Эти три части образуют трубопровод. Исходный плагин представляет необработанные данные, которые мы переносим, и отвечает за доставку отдельных строк. Эти данные передаются одному или нескольким плагинам процессов, которые выполняют любые необходимые манипуляции с каждым полем строки. Наконец, как только плагины процесса заканчивают подготовку данных, плагины назначения сохраняют их в объектах Drupal (либо контент, либо конфигурация).
Создание миграции предполагает использование таких плагинов (путем расширения или непосредственного использования основных классов). Все они затем объединяются в специальный объект конфигурации миграции. Это поставляется как конфигурация модуля, которая импортируется при первом включении модуля или может быть сконструирована с использованием шаблона (случай, который мы не будем изучать сегодня). Объект конфигурации миграции предоставляет ссылки на основные используемые плагины + дополнительные метаданные о миграции.
Миграция фильмов
Давайте теперь приступим к этому и напишем пару миграций. Наша модель данных следующая: в нашей базе данных есть две таблицы: movies
и movies_genres
. Первый имеет идентификатор, имя и описание, а второй — идентификатор, имя и идентификатор фильма, который сопоставляется с фильмом из первой таблицы. Для простоты это отображение один на один, чтобы избежать усложнения третьей таблицы. Вот скрипт MySQL, который вы можете использовать для создания этих таблиц и добавления нескольких тестовых записей (если вы хотите следовать):
CREATE TABLE ` movies ` ( ` id ` int ( 11 ) unsigned NOT NULL AUTO_INCREMENT , ` name ` varchar ( 255 ) DEFAULT NULL , ` description ` text , PRIMARY KEY ( ` id ` ) ) ENGINE = InnoDB AUTO_INCREMENT = 3 DEFAULT CHARSET = utf8 ; CREATE TABLE ` movies_genres ` ( ` id ` int ( 11 ) unsigned NOT NULL AUTO_INCREMENT , ` movie_id ` int ( 11 ) DEFAULT NULL , ` name ` varchar ( 255 ) DEFAULT NULL , PRIMARY KEY ( ` id ` ) ) ENGINE = InnoDB AUTO_INCREMENT = 5 DEFAULT CHARSET = utf8 ; INSERT INTO ` movies ` ( ` id ` , ` name ` , ` description ` ) VALUES ( 1 , 'Big Lebowsky' , 'My favorite movie, hands down.' ) , ( 2 , 'Pulp fiction' , 'Or this is my favorite movie?' ) ; INSERT INTO ` movies_genres ` ( ` id ` , ` movie_id ` , ` name ` ) VALUES ( 1 , 1 , 'Comedy' ) , ( 2 , 1 , 'Noir' ) , ( 3 , 2 , 'Crime' ) ;
Чего мы хотим добиться, так это перенести фильмы в основные узлы Drupal Article и жанры в термины таксономии словаря Tags (на которые ссылаются узлы Article через поле). Естественно, мы также хотим, чтобы миграция отражала связь между фильмами и жанрами.
Жанры
Давайте сначала позаботимся о миграции жанров, потому что в Drupal фильмы будут зависеть от них (они будут ссылаться на них).
Внутри config/install
нашего модуля нам нужен следующий файл конфигурации с именем migrate.migration.genres.yml
:
id : genres label : Genres migration_group : demo source : plugin : genres key : default destination : plugin : entity : taxonomy_term process : vid : plugin : default_value default_value : tags name : name
Первые три элемента этой конфигурации — это идентификатор миграции, метка и группа, к которой он принадлежит. Следующие три ключа отвечают за три основные части конвейера миграции, упомянутые ранее. Давайте поговорим о последних трех отдельно.
Источник
Под ключом источника мы указываем, какой плагин миграция должна использовать для представления и доставки исходных данных (в нашем случае плагин с идентификатором genres
). key
используется, чтобы указать, какую базу данных должен использовать наш исходный запрос (именно там находятся наши данные).
Итак, в папке Plugin/migrate/source
нашего модуля, давайте создадим наш исходный плагин на основе SQL для наших жанров:
Genres.php
namespace Drupal \ demo \ Plugin \ migrate \ source ; use Drupal \ migrate \ Plugin \ migrate \ source \ SqlBase ; /** * Source plugin for the genres. * * @MigrateSource( * id = "genres" * ) */ class Genres extends SqlBase { /** * {@inheritdoc} */ public function query ( ) { $query = $this - > select ( 'movies_genres' , 'g' ) - > fields ( 'g' , [ 'id' , 'movie_id' , 'name' ] ) ; return $query ; } /** * {@inheritdoc} */ public function fields ( ) { $fields = [ 'id' = > $this - > t ( 'Genre ID' ) , 'movie_id' = > $this - > t ( 'Movie ID' ) , 'name' = > $this - > t ( 'Genre name' ) , ] ; return $fields ; } /** * {@inheritdoc} */ public function getIds ( ) { return [ 'id' = > [ 'type' = > 'integer' , 'alias' = > 'g' , ] , ] ; } }
Поскольку мы используем источник SQL, нам нужен собственный класс плагина источника, чтобы предоставить некоторую информацию о том, как данные должны быть прочитаны. Недостаточно использовать существующий из ядра. Метод query()
создает запрос для данных жанра, метод fields()
определяет каждое отдельное поле строки, а метод getIds()
определяет исходное поле строки, которое действует как уникальный идентификатор. Ничего сложного здесь не происходит.
Мы расширяемся от SqlBase
который, помимо прочего, ищет элемент конфигурации плагина с именем key
чтобы узнать, на какой базе данных он должен выполнить запрос. В нашем случае, по умолчанию, как мы подробно описали в объекте конфигурации миграции.
И это все, что нам нужно для нашего простого исходного плагина.
Место назначения
Для пункта назначения мы используем плагин основного термина таксономии по умолчанию, который обрабатывает все для нас. Не нужно ничего уточнять.
Процесс
Под ключом процесса миграции мы перечисляем каждое поле назначения, которое мы хотим заполнить, и один или несколько плагинов процесса, которые преобразуют поля строки источника в данные для полей назначения. Поскольку мы хотим, чтобы жанры были всеми терминами словаря Tags, для поля vid
мы используем плагин default_value
который принимает ключ default_value
который указывает значение, которое будет иметь каждая запись. Так как все будут в одном словаре, это хорошо работает для нас.
Наконец, для поля имени термина мы можем просто указать имя поля строки источника без явного имени плагина. Это будет скрыто использовать плагин get
который просто берет данные из источника и копирует их без изменений в место назначения.
Для получения дополнительной информации о том, как вы можете связать несколько плагинов процессов в конвейере, или о том, какие другие плагины у вас есть в ядре, я рекомендую вам ознакомиться с документацией .
Кино
Теперь, когда наши жанры импортируемы, давайте взглянем на конфигурацию миграции фильмов, которая находится в той же папке, что и предыдущая ( config/install
):
migrate.migration.movies.yml
id : movies label : Movies migration_group : demo source : plugin : movies key : default destination : plugin : entity : node process : type : plugin : default_value default_value : article title : name body : description field_tags : plugin : migration migration : genres source : genres migration_dependencies : required : - genres
Мы отмечаем те же метаданные, что и раньше, три основные части миграции (источник, процесс и место назначения), но также и явную зависимость, которую необходимо встретить, прежде чем эта миграция может быть успешно выполнена.
Источник
Как и прежде, давайте посмотрим на плагин исходного кода movies
, расположенный там же, где и плагин исходного кода genres
( Plugin/migrate/source
):
Movies.php :
namespace Drupal \ demo \ Plugin \ migrate \ source ; use Drupal \ migrate \ Plugin \ migrate \ source \ SqlBase ; use Drupal \ migrate \ Row ; /** * Source plugin for the movies. * * @MigrateSource( * id = "movies" * ) */ class Movies extends SqlBase { /** * {@inheritdoc} */ public function query ( ) { $query = $this - > select ( 'movies' , 'd' ) - > fields ( 'd' , [ 'id' , 'name' , 'description' ] ) ; return $query ; } /** * {@inheritdoc} */ public function fields ( ) { $fields = [ 'id' = > $this - > t ( 'Movie ID' ) , 'name' = > $this - > t ( 'Movie Name' ) , 'description' = > $this - > t ( 'Movie Description' ) , 'genres' = > $this - > t ( 'Movie Genres' ) , ] ; return $fields ; } /** * {@inheritdoc} */ public function getIds ( ) { return [ 'id' = > [ 'type' = > 'integer' , 'alias' = > 'd' , ] , ] ; } /** * {@inheritdoc} */ public function prepareRow ( Row $row ) { $genres = $this - > select ( 'movies_genres' , 'g' ) - > fields ( 'g' , [ 'id' ] ) - > condition ( 'movie_id' , $row - > getSourceProperty ( 'id' ) ) - > execute ( ) - > fetchCol ( ) ; $row - > setSourceProperty ( 'genres' , $genres ) ; return parent : : prepareRow ( $row ) ; } }
У нас есть те же три обязательных метода, что и раньше, которые выполняют одно и то же: запрашивают и определяют данные. Однако здесь мы также используем метод prepareRow()
для изменения данных строки и доступных полей. Цель состоит в том, чтобы выбрать идентификатор жанра фильма, который соответствует текущей строке (фильму). Это значение заполняется в новом поле источника, называемом genres
, и через минуту мы увидим, как оно используется для сохранения ссылки на термин таксономии тегов.
Место назначения
В этом случае мы используем плагин назначения сущности узла и нам больше ничего не нужно.
Процесс
В узле Article есть четыре поля, которые мы хотим заполнить данными фильма. Во-первых, для type
узла мы используем ту же технику, что и раньше, для словаря таксономии и устанавливаем article
как значение по умолчанию. Во-вторых и в-третьих, для полей title и body мы сопоставляем исходные поля названия фильма и описания без изменений.
Наконец, для поля тегов мы используем плагин процесса миграции, который позволяет нам преобразовать идентификатор жанра (который мы добавили ранее в поле строки источника genres
) в идентификатор соответствующего термина таксономии. Этот плагин делает это для нас, проверяя миграцию по жанрам и читая эти идентификаторы. И именно поэтому миграция жанров также помечена как зависимость для импорта фильмов.
Активация и запуск миграции
Теперь, когда у нас есть два объекта конфигурации миграции и все соответствующие плагины, пришло время впервые включить наш модуль и импортировать конфигурацию в Drupal. Если ваш модуль уже был включен, удалите его, а затем включите его снова. Это сделает импорт конфига.
Кроме того, для запуска миграции через Drush (что является рекомендуемым способом), установите модуль Migrate Tools . Тогда все, что осталось сделать, это использовать команды для переноса или отката фильмов и жанров.
Чтобы увидеть доступные миграции и их статус:
drush migrate-status
Чтобы импортировать все миграции:
drush migrate-import --all
Чтобы откатить все миграции назад:
drush migrate-rollback --all
Вывод
И у нас это есть — простая миграция, чтобы показать, как мы можем теперь импортировать, отслеживать и откатывать миграции в Drupal 8. Мы видели, как система плагинов используется для представления всех этих различных компонентов функциональности и как определение миграции был превращен в конфигурацию, которая объединяет эти элементы.
Впрочем, многому еще предстоит научиться. Вы можете использовать разные исходные плагины, например, для данных в CSV или JSON, сложные плагины процессов (или написать свои собственные) или даже собственные плагины назначения для любой структуры данных, которая у вас может быть. Удачи!