e コマースフレームワークは、任意の e コマースソリューションで使用できます。ここで取り上げる詳細や例は、hybris のソリューションに関係しています。
統合フレームワークには、API を備えた統合レイヤーが含まれます。以下を実行できます。
e コマースシステムを組み込んで、製品データを AEM に取り込む
特定の e コマースエンジンに依存しないコマース機能用の AEM コンポーネントを作成する
API に関するドキュメントもお読みください。
この統合レイヤーを使用するために、すぐに使える AEM コンポーネントが多数用意されています。現時点では、以下のようなものがあります。
検索のために、AEM 検索、e コマースシステムの検索、サードパーティ検索(Search&Promote など)、またはこれらを組み合わせた検索をおこなうための、統合フックが提供されています。
e コマースフレームワークは任意の e コマースソリューションと組み合わせて使用できますが、使用されるエンジンは AEM によって識別できる必要があります。
e コマースエンジンは、CommerceService
インターフェイスをサポートする OSGi サービスです。
commerceProvider
サービスプロパティによって区別できます。AEM では、CommerceService
と Product
に対して Resource.adaptTo()
をサポートしています。
adaptTo
実装は、リソースの階層内で cq:commerceProvider
プロパティを探します。
見つかった場合は、その値を使用してコマースサービスの検索をフィルタリングします。
見つからなかった場合は、最上位のコマースサービスが使用されます。
厳密に型指定されたリソースに cq:commerceProvider
を追加できるように、cq:Commerce
Mixin が使用されます。
適切なコマースファクトリ定義を参照するために、cq:commerceProvider
プロパティも使用されます。
例えば、cq:commerceProvider
という値を持つ hybris
プロパティは、向け Day CQ コマースファクトリ(com.adobe.cq.commerce.hybris.impl.HybrisServiceFactory)の OSGi 設定に関連付けられます(ここでも、commerceProvider
パラメーターの値は hybris
hybris となります)。
ここで、カタログバージョンなど、さらに別のプロパティを設定できます(適切かつ使用可能な場合)。
以下の例を参照してください。
cq:commerceProvider = geometrixx |
標準の AEM インストール内に具体的な実装が必要。例:geometrixx のサンプル(汎用 API に対する最小限の拡張が含まれます)。 |
---|---|
cq:commerceProvider = hybris |
hybris 実装 |
/content/store
+ cq:commerceProvider = hybris
+ mens
+ polo-shirt-1
+ polo-shirt-2
+ employee
+ cq:commerceProvider = jcr
+ adobe-logo-shirt
+ cq:commerceType = product
+ price = 12.50
+ adobe-logo-shirt_S
+ cq:commerceType = variant
+ size = S
+ adobe-logo-shirt_XL
+ cq:commerceType = variant
+ size = XL
+ price = 14.50
CRXDE Lite を使用して、hybris 実装の場合に製品コンポーネントでこれがどのように処理されるかを確認できます。
/apps/geometrixx-outdoors/components/hybris/product/product.jsp
e コマース統合フレームワークの Hybris 拡張が更新されて、Hybris 4 との下位互換性を保ちながら、Hybris 5 がサポートされるようになりました。
コード内のデフォルト設定は hybris 5 向けに最適化されています。
hybris 4 向けの開発をおこなうには、以下が必要です。
Maven を呼び出す場合は、次のコマンドライン引数をコマンドに追加します。
-P hybris4
事前設定済みの Hybris 4 ディストリビューションをダウンロードして、バンドル cq-commerce-hybris-server
に組み込みます。
OSGi 設定マネージャーで、
Default Response Parser サービスの Hybris 5 サポートを無効にします。
Hybris Basic Authentication Handler サービスのサービスランキングが Hybris OAuth Handler サービスより低くなるようにします。
hybris では、顧客の買い物かごなどの情報を格納するためにユーザーセッションを使用します。hybris はセッション ID を JSESSIONID
cookie に返し、以降の hybris への要求ではこの cookie を送信する必要があります。セッション ID がリポジトリに格納されないように、セッション ID は買い物客のブラウザーに保存される別の cookie 内でエンコードされます。以下のステップが実行されます。
最初の要求では、買い物客の要求に対して cookie は設定されません。そこで、セッションを作成するために要求が hybris インスタンスに送信されます。
その応答からセッション cookie が抽出され、新しい cookie 内でエンコードされ(例えば hybris-session-rest
)、買い物客への応答に設定されます。新しい cookie 内でエンコーディングが必要なのは、元の cookie は特定のパスでのみ有効であり、そうしないと以降の要求でブラウザーから送り返されてこないからです。したがって、cookie の値にパス情報も追加する必要があります。
以降のリクエストでは、これらの Cookie が hybris-session-<*xxx*>
Cookie からデコードされ、hybris のデータを要求するための HTTP クライアントに設定されます。
元のセッションが無効になっているときは、新しい匿名セッションが作成されます。
このセッションは、 買い物かごを「所有」しています
追加や削除などを実行します。
買い物かごについての様々な計算を実行します。
commerceSession.getProductPrice(Product product)
注文データの保存場所を管理します。
CommerceSession.getUserContext()
支払処理の接続も管理します。
フルフィルメントの接続も管理します。
hybris で管理されている製品データを AEM で使用できるようにする必要があります。以下のような構成が実装されています。
インポーター(b)は、カタログ用に AEM 内で最初のページツリー構造を設定するために使用されます。
hybris 内でのカタログ変更はフィードを通じて AEM に通知され、それが AEM(b)に反映されます。
カタログバージョンについて、製品が追加、削除、変更されます。
製品が承認されます。
hybris 拡張はポーリングインポーター("hybris" scheme")を提供します。このインポーターは、変更を指定の時間間隔で AEM に読み込むように設定できます(例えば 24 時間おきにする場合は、秒単位で以下のように指定します)。
http://localhost:4502/content/geometrixx-outdoors/en_US/jcr:content.json
{
* "jcr:mixinTypes": ["cq:PollConfig"],
* "enabled": true,
* "source": "hybris:outdoors",
* "jcr:primaryType": "cq:PageContent",
* "interval": 86400
}
AEM のカタログ構成は、ステージング済みカタログバージョンとオンラインカタログバージョンを認識します。
カタログバージョン間の製品同期には、対応する AEM ページのアクティベート(アクティベート解除)が必要です(a、c)。
製品をオンラインカタログバージョンに追加するには、製品のページをアクティベートします。
製品を削除するには、アクティベート解除します。
AEM 内のページをアクティベート(c)するには確認が必要で(b)、以下の場合にのみアクティベートできます。
製品が、製品ページのオンラインカタログバージョン内にある
参照される製品が、他のページ(キャンペーンページなど)のオンラインカタログバージョン内で利用できる
アクティベートされた製品ページは、製品データのオンラインバージョンにアクセスする必要があります(d)。
AEM パブリッシュインスタンスは、製品データやパーソナライズされたデータを取得するために、hybris にアクセスする必要があります(d)。
1 つの製品に複数のバリエーションがある場合があります。例えば、カラーやサイズで異なるバリエーションがある場合があります。製品では、バリエーションを構成するプロパティを定義する必要があります。このようなプロパティをバリアント軸と呼びます。
ただし、すべてのプロパティがバリアント軸になるわけではありません。バリエーションは、他のプロパティにも影響を与えることがあります。例えば、価格はサイズに依存することがあります。買い物客はこのようなプロパティを選択できないので、バリアント軸とは見なされません。
各製品やバリアントはリソースによって表現されるので、リポジトリノードに 1 対 1 でマップされます。必然的に、特定の製品やバリアントはそのパスによって一意に識別できます。
製品/バリアントリソースは、実際の製品データを持つとは限らず、本当は別のシステム(hybris など)に格納されているデータの表現にすぎない場合があります。例えば、製品の説明や価格などは AEM には格納されておらず、e コマースエンジンからリアルタイムで取得されます。
すべての製品リソースは、Product API
で表現できます。製品 API の呼び出しのほとんどはバリエーションごとに固有ですが(ただし、バリエーションは 1 つのオリジナルから共有される値を継承する場合があります)、一連のバリエーションを一覧で表示する呼び出しもあります(getVariantAxes()
や getVariants()
など)。
実際には、バリアント軸は Product.getVariantAxes()
の戻り値で決定されます。
(一般的に)製品には多数のバリアント軸を持たせることができますが、デフォルトの製品コンポーネントでは次の 2 つのバリアント軸のみが処理されます。
size
さらにもう 1 つ
この追加バリアントは、製品リファレンスの variationAxis
プロパティ(Geometrixx Outdoors の場合、通常は color
)を使用して選択されます。
一般的に、
製品データは /etc
の配下に配置され、
製品リファレンスは /content
の配下に配置されます。
製品バリエーションと製品データノードの間には 1 対 1 のマッピングが必要です。
製品リファレンスには、各バリエーションを表すノードも必要ですが、すべてのバリエーションを表す必要はありません。例えば、製品に S、M、L のバリエーションがある場合、製品データは次のようになります。
etc
|──commerce
| |──products
| |──shirt
| |──shirt-s
| |──shirt-m
| |──shirt-l
「Big and Tall」カタログには次のバリエーションだけが含まれます。
content
|──big-and-tall
| |──shirt
| |──shirt-l
最後に、製品データを使用するための要件はありません。 すべての製品データをカタログ内のリファレンスの配下に配置できますが、実際には、すべての製品データを複製せずに複数のカタログを用意することはできません。
API
public interface Product extends Adaptable {
public String getPath(); // path to specific variation
public String getPagePath(); // path to presentation page for all variations
public String getSKU(); // unique ID of specific variation
public String getTitle(); // shortcut to getProperty(TITLE)
public String getDescription(); // shortcut to getProperty(DESCRIPTION)
public String getImageUrl(); // shortcut to getProperty(IMAGE_URL)
public String getThumbnailUrl(); // shortcut to getProperty(THUMBNAIL_URL)
public <T> T getProperty(String name, Class<T> type);
public Iterator<String> getVariantAxes();
public boolean axisIsVariant(String axis);
public Iterator<Product> getVariants(VariantFilter filter) throws CommerceException;
}
/**
* Interface for filtering variants and AxisFilter provided as common implementation
*
* The <code>VariantFilter</code> is used to filter variants,
* e.g. when using {@link Product#getVariants(VariantFilter filter)}.
*/
public interface VariantFilter {
public boolean includes(Product product);
}
/**
* A {@link VariantFilter} for filtering variants by the given
* axis and value. The following example returns a list of
* variant products that have a value of <i>blue</i> on the
* <i>color</i> axis.
*
* <p>
* <code>product.getVariants(new AxisFilter("color", "blue"));</code>
*/
public class AxisFilter implements VariantFilter {
private String axis;
private String value;
public AxisFilter(String axis, String value) {
this.axis = axis;
this.value = value;
}
/**
* {@inheritDoc}
*/
public boolean includes(Product product) {
ValueMap values = product.adaptTo(ValueMap.class);
if(values != null) {
String v = values.get(axis, String.class);
return v != null && v == value;
}
return false;
}
}
一般的なストレージの構成
製品ノードは nt:unstructured
です。
製品ノードは次のどちらかになります。
リファレンス。製品データは他の場所に保存されています。
製品リファレンスには、productData
プロパティが含まれます。このプロパティは、製品データ(一般的には /etc/commerce/products
の下にあります)を指します。
製品データは階層化されています。製品属性は、製品データノードの祖先から継承されます。
製品リファレンスには、ローカルプロパティも含めることができます。このようなプロパティは、製品データ内で指定されるプロパティをオーバーライドします。
製品自体
productData
プロパティはありません。
すべてのプロパティをローカルに保持している(そして productData プロパティを含まない)製品ノードは、製品属性を自身の祖先から直接継承します。
AEM の汎用の製品構造
各バリアントには、独自のリーフノードが必要です。
製品インターフェイスは、製品とバリアントの両方を表しますが、関連リポジトリノードはそれぞれに固有です。
製品ノードは、製品属性とバリアント軸を示します。
+ banyan_shirt
- cq:commerceType = product
- cq:productAttributes = [jcr:title, jcr:description, size, price, color]
- cq:productVariantAxes = [color, size]
- jcr:title = Banyan Shirt
- jcr:description = Flowery, all-cotton shirt.
- price = 14.00
+ banyan_shirt_s
- cq:commerceType = variant
- size = S
+ banyan_shirt_s_red
- cq:commerceType = variant
- color = red
+ banyan_shirt_s_blue
- cq:commerceType = variant
- color = blue
+ banyan_shirt_m
- cq:commerceType = variant
- size = M
+ banyan_shirt_m_red
- cq:commerceType = variant
- color = red
+ banyan_shirt_m_blue
- cq:commerceType = variant
- color = blue
+ banyan_shirt_l
- cq:commerceType = variant
- size = L
+ banyan_shirt_l_red
- cq:commerceType = variant
- color = red
+ banyan_shirt_l_blue
- cq:commerceType = variant
- color = blue
+ banyan_shirt_xl
- cq:commerceType = variant
- size = XL
- price = 18.00
コンポーネント
買い物かごは、CommerceSession:
によって管理されます。
CommerceSession
は、追加や削除などを実行します。CommerceSession
は、買い物かごに対する様々な計算も実行します。買い物かごに直接関連はしませんが、CommerceSession
はカタログの価格情報も提供する必要があります(価格を管理しているので)。
価格には、次の複数の変更要素があります。
変更要素は、次のインターフェイスを使用して完全に変更できます。
int CommerceSession.getQuantityBreakpoints(Product product)
String CommerceSession.getProductPrice(Product product)
ストレージ
ストレージ
パーソナライズ機能
パーソナライズは、常に ClientContext から取得する必要があります。
買い物かごの ClientContext /version/
は、すべてのケースで作成されます。
CommerceSession.addCartEntry()
メソッドを使用して追加する必要があります。次の図は、ClientContext に格納される買い物かご情報の例を示しています。
買い物かごと注文データ
CommerceSession
は、次の 3 つの要素を管理します。
買い物かごコンテンツ
価格
注文の詳細
買い物かごコンテンツ
買い物かごコンテンツのスキーマは、API によって決められています。
public void addCartEntry(Product product, int quantity);
public void modifyCartEntry(int entryNumber, int quantity);
public void deleteCartEntry(int entryNumber);
価格
価格のスキーマも、API によって決められています。
public String getCartPreTaxPrice();
public String getCartTax();
public String getCartTotalPrice();
public String getOrderShipping();
public String getOrderTotalTax();
public String getOrderTotalPrice();
注文の詳細
しかし、注文の詳細は API によって決められていません。
public void updateOrderDetails(Map<String, String> orderDetails);
public Map<String, String> getOrderDetails();
public void submitOrder();
送料の計算
多くの場合、注文フォームには複数の出荷オプション(および価格)を表示する必要があります。
価格は、品目と注文の詳細(重さや配送先住所など)に基づきます。
CommerceSession
はすべての依存関係にアクセスするので、製品価格と同じ方法で扱うことができます。
CommerceSession
は送料を管理します。updateOrder(Map<String, Object> delta)
を使用して、配送の詳細情報を取得または更新できます。以下のような発送セレクターを実装することもできます。
yourProject/commerce/components/shippingpicker
の下)で、次の手順をおこないます。
これは基本的に foundation/components/form/radio
のコピーですが、以下を行うための CommerceSession
へのコールバックを備えています。
その方法が使用可能かどうかを確認する
価格情報を追加する
関連する CommerceSession
情報の公開に関する制御権を持ちつつ、買い物客が AEM 内の注文ページ(発送方法のスーパーセットとその説明テキストを含む)を更新できるようにする
支払い処理
CommerceSession
は、支払い処理の接続も管理します。
CommerceSession
実装には、選択された支払い処理サービスへの具体的な呼び出しを追加する必要があります。
注文のフルフィルメント
CommerceSession
は、フルフィルメントの接続も管理します。CommerceSession
実装には、選択された支払い処理サービスへの具体的な呼び出しを追加する必要があります。標準のサービス API モデルに従い、e コマースプロジェクトは、個々のコマースエンジンで実装できる一連の検索関連 API を提供しています。
現在、この検索 API をデフォルトで実装しているのは hybris エンジンだけです。
しかし、検索 API は汎用的なものであり、各 CommerceService で個別に実装できます。
e コマースプロジェクトには、デフォルトの検索コンポーネントが含まれます。このコンポーネントは次の場所にあります。
/libs/commerce/components/search
ここでは、検索 API を利用して、選択されたコマースエンジンをクエリしています(e コマースエンジンの選択を参照)。
コアプロジェクトは、いくつかの汎用クラスまたはヘルパークラスを提供します。
CommerceQuery
検索クエリの記述に使用します(クエリテキスト、現在のページ、ページサイズ、並べ替え、選択されているファセットについての情報を含みます)。検索 API を実装するすべての e コマースサービスは、検索を実行するために、このクラスのインスタンスを受け取ります。CommerceQuery
は、リクエストオブジェクト(HttpServletRequest
)からインスタンス化できます。
FacetParamHelper
1 つの静的メソッド(toParams
)を提供するユーティリティクラス。このメソッドを使用して、ファセットのリストと 1 つのトグル値から GET
パラメーター文字列を生成します。これは、UI 側で、ユーザーがハイパーリンクをクリックすると対応する値に切り替わる(選択された場合はクエリから削除され、選択されていない場合は追加される)ように、各ファセットの値ごとのハイパーリンクを表示する必要がある場合に役立ちます。これによって、複数または単一の値を持つファセットの処理や値のオーバーライドなど、すべてのロジックを扱うことができます。
検索 API のエントリポイントは、CommerceService#search
オブジェクトを返す CommerceResult
メソッドです。このトピックについて詳しくは、API ドキュメントを参照してください。
AEM は様々な e コマースシステムと統合できます。これを実現するには、AEM 固有のコードは AEM のみを把握すればよく、その逆も同様となるように、各種システム間で買い物客を同期するための戦略が必要です。
認証
AEM は唯一の Web フロントエンドと見なされるので、すべての認証は AEM が実行します。
Hybris のアカウント
AEM は、買い物客ごとに対応する(セカンダリ)アカウントを Hybris に作成します。 このアカウントのユーザー名は、AEM ユーザー名と同じです。暗号化されたランダムパスワードが自動生成され、暗号化された状態で AEM に保存されます。
AEM フロントエンドは、既存の hybris 実装の前面に置くことができます。また、hybris エンジンを既存の AEM インストールに追加することもできます。これを実現するには、双方のシステムが相手側システムの既存ユーザーを適切に扱えなければなりません。
AEM -> hybris の場合
hybris へのログイン時に、AEM ユーザーがまだ存在していない場合は、
参照先: com.adobe.cq.commerce.hybris.impl.HybrisSessionImpl#login()
hybris -> AEM の場合
AEM へのログイン時に、システムがユーザーを認識していない場合は、
上記のアルゴリズムは Sling の AuthenticationInfoPostProcessor
で実装されます。
com.adobe.cq.commerce.hybris.impl.user.LazyUserImporter.java
既存の機能に基づいてカスタムの読み込みハンドラーを作成するには、次のようにします。
ImportHandler
インターフェイスを実装する必要があります。
DefaultImportHandler
を拡張できます。
/**
* Services implementing the <code>ImportHandler</code> interface are
* called by the {@link HybrisImporter} to create actual commerce entities
* such as products.
*/
public interface ImportHandler {
/**
* Not used.
*/
public void createTaxonomie(ImporterContext ctx);
/**
* Creates a catalog with the given name.
* @param ctx The importer context
* @param name The catalog's name
* @return Path of created catalog
*/
public String createCatalog(ImporterContext ctx, String name) throws Exception;
/**
* Creates a product from the given values.
* @param ctx The importer context
* @param values The product's properties
* @param parentCategoryPath The containing category's path
* @return Path of created product
*/
public String createProduct(ImporterContext ctx, ValueMap values, String parentCategoryPath) throws Exception;
/**
* Creates a variant product from the given values.
* @param ctx The importer context
* @param values The product's properties
* @param baseProductPath The base product's path
* @return Path of created product
*/
public String createVariantProduct(ImporterContext ctx, ValueMap values, String baseProductPath) throws Exception;
/**
* Creates an asset for a product. This is usually a product
* image.
* @param ctx The importer context
* @param values The product's properties
* @param baseProductPath The product's path
* @return Path of created asset
*/
public String createAsset(ImporterContext ctx, ValueMap values, String productPath) throws Exception;
/**
* Creates a category from the given values.
* @param ctx The importer context
* @param values The category's properties
* @param parentPath Path of parent category or base path of import in case of root category
* @return Path of created category
*/
public String createCategory(ImporterContext ctx, ValueMap values, String parentCategoryPath) throws Exception;
}
カスタムハンドラーがインポーターに認識されるようにするには、service.ranking
プロパティに 0 より大きい値を指定する必要があります。以下に例を示します。
@Component
@Service
@Property(name = "service.ranking", value = 100)
public class MyImportHandler extends DefaultImportHandler
{
...
}