こんにちは。スタジオ・ウミの大野です。もう今年も残すところわずかとなりましたね。スタジオ・ウミでは年末の締めに追われてヒーヒーいいながら毎日を過ごしてます。

さて、今回は Drupal Advent Calendar 2015 に参加させていただくことになりましたので、その18日目の記事として投稿します。皆様がリリースされたてホヤホヤの Drupal 8 の記事を書かれている最中、場の空気を読まずに Drupal 7 の Entity Metadata Wrapper を紹介する内容です!

Entity Metadata Wrapper とは

Entity Metadata Wrapper はご存知でしょうか。ひとことで言うとノードなどのエンティティをラッパークラスで包んで値の出し入れをメチャクチャ楽にする API です。日本語の記事があまり無いようなので国内ではあまり使われていない感じでしょうか?

Drupal のモジュールなどを作られたことがある方でしたらわかると思いますが、Drupal のエンティティオブジェクトは多重配列になっていて、凄く読みにくいし取り扱いが面倒ですよね。具体的に Entity Metadata Wrapper を使うとどの位楽になるか、ノードのテキストフィールドに値をセットする例で見てみましょう。

ノードオブジェクトに直接値を入れる場合:

$node->field_text[LANGUAGE_NONE][]['value'] = 'テキスト';

Entity Metadata Wrapper を使った場合:

$wrapper->field_text = 'テキスト';

こんなにスッキリします。Drupal のフィールドの構造上、仕方なく書いていた LANGUAGE_NONE['value'] が必要ありません。そこら辺の面倒な処理をこなしてくれるのが Entity Metadata Wrapper です。ここまで読んで、便利じゃない!と思った方はこのブログをそっと閉じてください。

多少の学習コストは必要ですが、Drupal でエンティティまわりのモジュールを作成するエンジニアには必須の API だと思います。ちなみに "Entity Metadata Wrapper" で検索すると Death to Field Arrays! みたいな過激なタイトルのブログが見つかりますが、「確かに」と言わざるを得ません。

Entity Metadata Wrapper の使い方

Entity Metadata Wrapper を使用するには Entity API モジュールを入れる必要があります。これに依存しているコントリビュートモジュールは多いので、名前だけでも聞かれたことがあるのではないでしょうか。この API は独自のカスタムモジュールで利用すると思いますので、.info ファイルの dependencies に忘れずに入れるようにしましょう。このブログではノードエンティティを例として解説していますが、タクソノミーやコメントなどのエンティティも同様にラッパークラスを追加することができます。

実際にコード上でラッパークラスを使うには以下の様にします。

// ノードオブジェクトから使う
$node = node_load(1);
$wrapper = entity_metadata_wrapper('node', $node);

// ノード ID から使う
$nid = 1;
$wrapper = entity_metadata_wrapper('node', $nid);

// 新しくノードオブジェクトを作成してラッパーを追加
$node = entity_create('node', array('type' => 'article'));
$wrapper = entity_metadata_wrapper('node', $node);

entity_metadata_wrapper() の第一引数にはエンティティのタイプを、ノードの場合は node となります。第二引数にはエンティティ(ノード)の ID もしくは エンティティ(ノード)オブジェクトを入れることができます。

値をセットする set() メソッド

オブジェクトに値をセットする際は set() メソッドを使用しますが、便利なマジックメソッドが用意されていますので set() を省略して直接代入することができます。以下の例はどちらも同じ値が代入されます。

// set() メソッドを使用する場合
$wrapper->field_text->set('text data');

// マジックメソッドを利用して直接代入する例
$wrapper->field_text = 'text data';

set() が書かれていないほうがコードが断然見やすいと思いますので積極的にマジックメソッドを利用しましょう。

復数の値を持てるフィールドの場合は配列を使って一度に代入することができます。

$wrapper->field_texts = array('一つ目', '二つ目', '三つ目');

また、配列の最後に要素を追加する [] リテラルも使用できます。便利ですね。

$wrapper->field_texts[] = '一つ目';
$wrapper->field_texts[] = '二つ目';
$wrapper->field_texts[] = '三つ目';

値を取得する value() メソッド

value() メソッドはフィールドタイプによって戻り値が異なります。以下の例は通常のテキストフィールドの値を取得する例です。

$value = $wrapper->field_text->value();

フィールドに復数の値がある場合は配列のキーで指定することができます。

// 2番目の値を取得
$value = $wrapper->field_text[1]->value();

フィールド毎の特性については一つ一つ解説すると長くなってしまいますので、また今度紹介します。

復数の値を持つフィールドの場合は getIterator() メソッドを使って、イテレータでループさせることが可能です。ただし、取得した値は EntityFieldWrapper クラスのオブジェクトになっていることに注意してください。値は上記と同様に value() メソッドなどを使って取得します。

$texts = array();
foreach ($wrapper->field_texts->getIterator() as $delta => $text_wrapper) {
  $texts[] = $text_wrapper->value();
}

ノードエンティティの基本的なプロパティの取得の仕方

ノードオブジェクトのプロパティは以下の様に取得します。

// ノードのタイトルを取得
$wrapper->title->value();
// ノード ID を取得
$wrapper->nid->value();
// ノードのコンテンツタイプを取得
$wrapper->type->value();

上記の例はノードオブジェクトの構造に依存した取得方法となりますが、共通のメソッドでノード ID や コンテンツタイプ(バンドル)を取得することもできます。

// ノードのタイトルを取得
$wrapper->label();
// ノード ID を取得
$wrapper->getIdentifier();
// ノードのコンテンツタイプを取得
$wrapper->getBundle();

ラッパークラス内で使用しているノード(エンティティ)オブジェクトを取得したい場合は以下のようにします。

$wrapper->value();

エンティティの色々な保存方法

以下の例はどれも同じ結果となります。

// Entity API を利用しない場合
node_save($node);

// Entity API を利用する場合
entity_save('node', $node);

// Entity Metadata Wrapper を利用する場合
$wrapper->save();

いかがでしたでしょうか。最初のラーニングコストはかかってしまいますが、覚えてしまえば可読性に優れた綺麗なコードを書くことができるようになります。個人的にはこの API 無しではもう開発できません。

軽い気持ちでこのお題を選んだのですが、想像以上に書かなければいけないことが多く、value() メソッドについては基本的なことしか解説できませんでした。。またの機会にフィールド毎の例や、ラッパークラスを追加した場合のオーバーヘッドについても記事にしたいと思います。

残り7日の Drupal Advent Calendar 2015、皆様の記事を楽しみにしています!


共に働く新しい仲間を
募集しています

スタジオ・ウミは「Drupal」に特化したサービスを提供する Drupal のエキスパートチーム。
フルリモート&フレックス制だから、働く場所を選ばず時間の使い方も自由です。
そんなワークライフバランスの整った環境で、当ブログに書かれているような
様々な技術を共に学びながら、Drupalサイト開発に携わってみたい方を募集しています。
まずはお話だけでも大歓迎!ぜひお気軽にご連絡ください。