メインコンテンツに移動

shirane lab

メインナビゲーション

  • ホーム
  • ブログ
  • Drush
  • 検索

パンくず

  • ホーム
  • ブログ
  • Migrate API で変換の前処理フックを利用して複数画像のサブフィールドを設定する例

Migrate API で変換の前処理フックを利用して複数画像のサブフィールドを設定する例

2021/01/03(日) - 14:42
shirane

Migrate API のパイプラインを利用してマルチフィールドに複数画像をマイグレーションする例の続き。

サブフィールドの例

これまでの例では、画像フィールドの代替テキスト(alt 属性)を設定していなかった。こうした、フィールドに含まれるデータ項目はサブフィールドと呼ばれ、フィールド名の後にスラッシュで区切って名前を指定することができる。画像(画像ファイル エンティティの ID)と代替テキスト(alt 属性)を個別に指定する例を示す。

id: mgdemo_article
source:
  plugin: embedded_data
  data_rows:
    -
      myid: 1
      mytitle: 吾輩は猫である
      mytext: 吾輩は猫である。名前はまだない。
      myimg: /tmp/img/cat1.jpg
      myalt: 猫
    -
      myid: 2
      mytitle: 羅生門
      mytext: ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。
      myimg: /tmp/img/gate.jpg
      myalt: 門の外観
    -
      myid: 3
      mytitle: 雪国
      mytext: 好きよあなた。いまでも。いまでも。
      myimg: /tmp/img/snow.jpg
      myalt: 雪景色
  ids:
    myid:
      type: integer
process:
  nid: myid
  title: mytitle
  uid:
    plugin: default_value
    default_value: 1
  body: mytext
  field_image/target_id:
    plugin: migration_lookup
    migration: mgdemo_image
    source: myimg
  field_image/alt: myalt
destination:
  plugin: 'entity:node'
  default_bundle: article

field_image/target_id が画像ファイル エンティティの ID、field_image/alt が代替テキストをそれぞれ表すサブフィールドである。

このマイグレーションを実行すると、次のように画像と代替テキストがインポートされる。

画像フィールドの代替テキストに値をセットした例

フィールドタイプの多くはデフォルトのサブフィールドを定義している。その場合、サブフィールドを明示しなければデフォルトのサブフィールドに値がセットされる。たとえば、画像フィールドのデフォルトのサブフィールドは target_id なので、画像ファイル エンティティの ID を設定するだけならサブフィールドの指定は不要だ。しかし、代替テキストのようなデフォルト以外のサブフィールドに値をセットするには、サブフィールド名(alt)を明示的に指定する必要がある。

各種フィールドタイプにどんなサブフィールドがあるのかは、対象となるフィールドタイプの定義を調べる必要があるが、ありがたいことにフィールドのタイプ別にサブフィールドの一覧を整理した資料が公開されている。たとえば、画像フィールド(Entity reference image field)の項を見ると、次の5つのサブフィールドがあることがわかる。

  1. target_id(デフォルト)
  2. alt
  3. title
  4. width
  5. height

マルチのサブフィールドと subprocess プラグイン

Migrate API のパイプラインを利用してマルチフィールドに複数画像をマイグレーションする例では、参照先の画像ファイル エンティティの ID の配列を field_image にセットすることで、デフォルトのサブフィールドである target_id に各配列要素の値を反映させていた。では、マルチフィールドの各画像に、代替テキストのような非デフォルトのサブフィールドをセットするにはどうすれば良いか。

image_field に値をセットする処理の中で、ソースのある項目を target_id に、別の項目を alt に振り分ける、いわば子供の変換処理を image_field の配下に定義する必要がある。

こうした要求に応えるため、コアの Migrate API に subprocess プラグインが用意されている。これを利用すると(連想)配列の配列をソースとして、要素である配列のキーの値をサブフィールドにマッピングすることができる。

たとえば、次のような、画像のパスと代替テキストからなる配列を要素とする配列をソース imginfo として用意できれば、

source: Array
(
  [imginfo] => Array
    (
      [0] => Array
        (
          [0] => '/tmp/img/cat1.jpg'
          [1] => '猫'
        )
      [1] => Array
        (
          [0] => '/tmp/img/cat2.jpg'
          [1] => 'ぬこ'
        )
      [2] => Array
        (
          [0] => '/tmp/img/cat3.jpg'
          [1] => 'ねんネコ'
        )
    )
)

次のようなマイグレーションを記述することで、

  field_image:
    plugin: sub_process
    source: imginfo
    process:
      target_id:
        plugin: migration_lookup
        migration: mgdemo_image
        source: '0'
      alt: '1'

各配列要素の先頭要素(インデックス = 0)を参照先の画像ファイル エンティティ ID に、2番目の要素(インデックス = 1)を代替テキストにそれぞれセットする処理を、マルチフィールドの各画像について実行させることができる。

process サブキーが subprocess プラグインによって実行される子供の変換処理の定義。field_image の各サブフィールドに、ソース imginfo の配列要素の対応する位置の値をマッピングしている。

フック関数の利用

問題はソース配列 imginfo をどのようにして作るかだ。種々のプラグインを駆使すれば実現できるのかもしれないが、このくらいになるともう直接 PHP のコードを書いた方が早い気もする。

Migrate API には、ソースデータを追加/加工する目的で利用できるフック関数が用意されている。

  • function hook_migrate_prepare_row
  • function hook_migrate_MIGRATION_ID_prepare_row

前者はすべてのマイグレーション共通、後者は特定のマイグレーション ID 限定で、変換処理が実行される前に呼び出される。ここでは後者を使ってみよう。

まず、マルチ画像フィールドに画像と代替テキストをインポートするためのソースデータとマイグレーションを次のように定義した。

id: mgdemo_article
source:
  plugin: embedded_data
  data_rows:
    -
      myid: 1
      mytitle: 吾輩は猫である
      mytext: 吾輩は猫である。名前はまだない。
      myimg: /tmp/img/cat1.jpg, /tmp/img/cat2.jpg, /tmp/img/cat3.jpg
      myalt: 猫, ぬこ, ねんネコ
    -
      myid: 2
      mytitle: 羅生門
      mytext: ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。
      myimg: /tmp/img/gate.jpg
      myalt: 門の写真
    -
      myid: 3
      mytitle: 雪国
      mytext: 好きよあなた。いまでも。いまでも。
      myimg: /tmp/img/snow.jpg, /tmp/img/ski.jpg
      myalt: 雪景色, スキーヤー
  ids:
    myid:
      type: integer
process:
  nid: myid
  title: mytitle
  uid:
    plugin: default_value
    default_value: 1
  body: mytext
  field_image:
    plugin: sub_process
    source: imginfo
    process:
      target_id:
        plugin: migration_lookup
        migration: mgdemo_image
        source: '0'
      alt: '1'
destination:
  plugin: 'entity:node'
  default_bundle: article

myimg 列が画像のフルパス、myalt 列が各画像に対応する代替テキストである。

次に、mgdemo というマシン名で簡単なモジュールを作り、マイグレーション mgdemo_article 用のフック関数を定義した。

mgdemo.module

<?php

use Drupal\migrate\Row;
use Drupal\migrate\Plugin\MigrateSourceInterface;
use Drupal\migrate\Plugin\MigrationInterface;

/**
 * Implements hook_migrate_MIGRATION_ID_prepare_row().
 */
function mgdemo_migrate_mgdemo_article_prepare_row(Row $row,
    MigrateSourceInterface $source, MigrationInterface $migration) {
  // myimage列の値をカンマ位置で分割して配列にする
  $myimg = $row->getSourceProperty('myimg');
  $images = explode(',', $myimg);
  array_walk($images, '_trim_value');
  
  // myalt列の値をカンマ位置で分割して配列にする
  $myalt = $row->getSourceProperty('myalt');
  $alts = explode(',', $myalt);
  array_walk($alts, '_trim_value');

  // 各配列の同位置の要素を束ねた配列の配列を作る
  $imginfo = array_map(null, $images, $alts);
  // imginfoという名前のソース列として追加
  $row->setSourceProperty('imginfo', $imginfo);
}

/*
 * array_walk用のコールバック
 */
function _trim_value(&$value) {
  $value = trim($value);
}

myimg 列、myalt 列それぞれの値を explode 関数で分割して配列にし、各要素を trim 関数で処理して前後の空白を除去している。_trim_value() 関数は array_walk 関数 で trim 関数を反復適用するためのコールバックである。

最後に、array_map 関数の第1引数に null を指定することで、両配列の各要素を合体した配列の配列を生成し、imginfo という名前でソース列($row)に追加している。こうすることで、マイグレーション側では、imginfo というソース列の値として、変数 $imginfo の配列を利用できるようになる。

モジュールを有効化し、キャッシュをクリアしてから、mgdemo_article のマイグレーションを実行すると、画像フィールドに画像と代替テキストが反映する形で記事ノードがインポートされる。

代替テキストが反映されたマルチ画像フィールド

まとめ

subprocess プラグインを使用して、マルチフィールドのサブフィールドに値を設定するサンプルを示した。Drupal 開発者にはおなじみのフック関数を利用すれば、マイグレーションの過程で必要となる込み入ったソースデータの構築を PHP コードで記述することも簡単にできる。

Migrate
Drupal9

Migrate API 関連記事

  • Migrate API の概要
  • Migrate API で画像ファイルを取り込むサンプル
  • Migrate API のパイプラインを利用してマルチフィールドに複数画像をマイグレーションする例
  • Migrate API で変換の前処理フックを利用して複数画像のサブフィールドを設定する例
‹ 前の記事次の記事 ›

書籍

『D9 おいしいレシピ集2』がパワーアップして商業誌に

『D9 おいしいレシピ集2』がパワーアップして商業誌に

 書籍の一覧はこちら

タグ一覧

DrushDrupal9Drupal6ffdsmVagrant開発環境Drupal7VirtualBoxComposerコミュニティDocker書籍MigrateDrupal5モジュール勉強会ubercartMariaDBDrupal4ArtisteerCKEditorWindowsDrupal ONSENVueNuxtヘッドレスデカップルドDruxtJSpsyshREPLAnalyticsインストールTomethemingデバッグTwigQuizテーマH5P仮想マシンCentOSMac

サイト運営

シナジークエスト

© shirane lab