このエントリはMagento Advent Calendar 2016の22日目です。

Magento2で開発をしていて、色々と悩ましい点が多い箇所の一つに「決済エクステンション」があります。
今回はこの話題について解説をしたいと思います。

Magento2で開発者が考えを改めないといけないこと

まず、Magento2で決済エクステンションを作り始める前に、この課題に取り組もうという開発者には知っておかないといけないことがあります。
Magento1とMagento2ではアプリケーションの世代が異なり、一部設計思想に違いがあるため、これまでの概念のままで開発をしようとすると、大きな壁にぶつかる可能性があります。

継承か契約か

Magento2でMagentoのコア開発チームが目指している思想の一つに「契約によるモジュール間の関係」というものがあります。
公式では「Service Contract」と表現されていて、クラス同士の約束事つまりはインターフェイス定義による実装が目指されています。

Magento1ではこれとは反対に、継承による実装が大半を占めており、テストしにくくかつ関係性を把握しにくい構造になっていました。

Magento2では継承をできるだけ排除しつつ、インターフェイス定義を中心に据えています。
たとえ中身の実装が大きく変わったとしても、インターフェイスによって定義された入力値と戻り値の型が変わらない事によって、モジュールの差し替えやリファクタリングが容易にできる構造を目指しています。

Dependency Injection

Magento2では、Dependency Injection(以下、DI)が採用されています。日本語訳すると「依存性の注入」という物騒な言葉になりますが、単に直訳なので気にしないでください。
Javaでは10年ほど前から取り入れられている概念で、PHPフレームワークでも幾つか採用しているものが既にあります。
Magentoの場合は、DIをコンストラクタDIとして実装しています。設定ファイルであるdi.xmlの定義と、各クラスのコンストラクタ定義の組み合わせを用いて、より柔軟にモジュール間の依存関係をコントロールすることを目的とした設計になっているのですが、慣れない人にとっては何がなんだかわからずに戸惑う可能性が高いでしょう。

小さなモジュールを組み合わせる、ということ

インターフェイスによる型の定義、DIによる依存関係の整理と切り出しに加え、Magento2は1つ1つのクラスが担当する処理をできるだけ小さく・シンプルにすることを目指しています。
複雑で大きなクラスを作るのであれば、それぞれの処理を別のクラスに切り出し、単純化・共通化を図ろうという指針が出されています。
ここでもインターフェイスによる入出力の型が重要で、適切に定義をすることでユニットテストをより細かい粒度で書けるようになっています。

もちろんそういった実装を行うためには、オブジェクト指向プログラミングの経験と、デザインパターンに関する知識・経験が必要です。
闇雲にコードを書いてしまうと収集がつかなくなり、脱出不能な迷路に迷い込んでしまう羽目になります。

避けて通れないJavaScript

さらにMagento2は非常に多岐にわたるJavaScriptによる実装が行われています。
jQueryやRequireJS、knockout.jsを筆頭に、様々なJavaScriptが協調して動作します。
これまで以上に開発者に求められる知識・経験は広範囲に及び、初期学習コストが高くなっていることは間違いないでしょう。

Magento2決済エクステンションの構造

さて、前置きはこれくらいにして、Magento2の決済エクステンションの構造を解説しましょう。
決済エクステンションは他のエクステンションと同様に、

  • サーバーサイドの実装
  • クライアントサイドの実装

という大きく分けて2つの領域に分かれます。
それぞれMagento1とは異なる要素が追加されているので、順を追って説明しましょう。

サーバーサイドの実装

まずはサーバーサイドの実装です。
ここはMagento2のなかでも新旧の実装方法があるところです。

AbstractMethodとAdapter

Magento2.0.6までは、Magento1と非常によく似た実装をしていました。
それがAbstractMethodです。Magento1で決済エクステンションを実装したことがある方には取っ付き易い実装です。

Magento2.0.6以降はこれが全面的に改められ、Adapterを使用することが推奨されるようになりました。
AdapterはAbstractMethodとは全く異なる実装で、冒頭で取り上げたようなインターフェイスによる契約を主体とした実装です。

いつか廃止されるAbstractMethod

Magento2.0.6以降、AbstractMethodにはdeprecatedというステータスがつけられています。
これは「そのうち廃止します。使わないでください。」という公式サイドからの意思表示です。

Magento側は時期を明示していませんが、間違いなく将来のリリースでAbstractMethodを廃止します。
そのため今後Magento2用の決済エクステンションを実装する場合は、Adapterを用いた実装をすることが強く推奨されます。

Adapter関連各クラスの構成

まず、次の図を見てください。この図はMeet Magento 2016 Indonesiaで講演した際に使用したものです。(資料そのものはこちら
Adapterを使った実装をする場合、Adapterに関連する多くのクラス・定義が必要になり、AbstractMethodよりも多くの知識が開発者に求められます。

  Adapterと関連クラス

特に上図の薄い緑背景色のコンポーネントはインターフェイスやクラスです。
これらがどのような振る舞いをするのか、について理解を深めることが重要です。

ConfigProvider

ConfigProviderは、先程の図には含まれていませんが、Magentoの設定パラメータをクライアントサイドに橋渡しする役割を担っています。
Magento2では特に公開画面側の支払方法選択画面がknockout.jsを用いて描画されるため、PHPを使用してMagento1のように設定パラメータを直接渡すことができません。
ですが利用可能カードブランドや、有効期限年月、支払い回数の候補データはどうしてもサーバー側でHTML文書を生成する際に渡してやる必要があります。
ConfigProviderはそのような用途に用いるクラスで、di.xmlで指定することによって、設定パラメータをJSONの形式でHTML文書中に出力することができます。
knockout.jsはそのJSONデータを参照し、画面の描画を行うという仕組みになっています。

クライアントサイドの実装

クライアントサイドの実装は、Magento2がRequireJSとknockout.jsを主体とした構造になっていることと、Magento独自のUIComponentを利用している点がMagento1と大きく異なります。

RequireJS

RequireJSはMagento2が使用しているJavaScriptコードのかなりの領域がこのライブラリに基づく形で管理されているように、とても重要です。
RequireJSに非互換のライブラリを使いたい場合、実装する側がそれなりに知恵をひねらねばなりません。特に決済エクステンションの場合はトークン決済などでJavaScriptを使用しますが、決済サービス側のJavaScriptライブラリが必ずしもRequireJS対応のものである保証がないため、各社それぞれに実装を調整する必要があります。

knockout.js

knockout.jsはJavaScriptのテンプレートエンジンで、前述の通りJSON形式のデータを利用して様々な画面に表示される内容の描画を行うことができます。
特にチェックアウト画面全域はknockout.jsを多用しているため、テンプレートはPHPコードを直接記述できるphtmlファイルではなく、knockout.jsが使用するhtmlテンプレートを使用します。
このテンプレートにはPHP側から直接値を渡すことができないため、あらかじめConfigProviderを通じてJSON形式のデータを画面上に配置しておく必要があります。

UIComponent

GitHub上や、様々な開発者のブログ等で意見が交わされているMagento独自の実装がUIComponentです。
管理画面でオリジナルの管理機能を開発する際にも使用するこのライブラリは、チェックアウト画面でも使用されています。
チェックアウト画面では主に入力フォームの生成などに用いられていますが、決済エクステンションの場合は極めて限られた範囲でのみ理解しておけば支障はないと思います。

レイアウトXML

Magento独自のもの、といえばテーマカスタマイズの際に避けて通れない「レイアウトXML」があります。
Magento2でもレイアウトXMLは健在ですが、XSDによる記法定義の明確化が図られ、また定義方法についてもMagento1から大きく改められています。

決済エクステンションでもレイアウトXMLを定義しなければならない箇所が一部存在するので、理解しておくことは不可欠です。

まとめ

このエントリは3回に分けてお送りします。
1回目の今回は概論の部分に触れました。ざっくりとまとめておきましょう。

  • Magento2のエクステンション開発には、DIやインターフェイスによる契約を理解する必要がある
  • 決済エクステンションの実装方法にはAbstractMethodとAdapterがある
  • AbstractMethodは簡単だがやがて廃止される
  • Adapterが推奨されているが、関連するモジュールの振る舞いを理解する必要があるため、多くの知識を要求する
  • クライアントサイドの実装ではJavaScriptが担う領域が増えている

明日は23日目。kzkick2ndさんによる「デザイン変更のハウツー」です!