※ これから記載する事項は、私が所属する会社とは一切関係のない事柄です。
コントローラを修正・拡張する際の方法を紹介します。SFRAの基礎となるapp_storefront_base
を拡張したいときや自作で作ったカートリッジを修正・拡張したいときに便利です。
今回覚えて欲しいこと
- カートリッジはカートリッジパスの左が優先
- append, prepend, replaceのそれぞれの役割
- コントローラのミドルウェアは左のファンクションから実行される
(準備)まずはサンプルに使うコントローラとテンプレートを作る
2つの名前のカートリッジを作成します。カートリッジの作成方法はこちら。
- controller_sample_base(ベース)
- controller_sample_custom (カスタム)
ベースとしてcontroller_sample_baseに Test.js
と言うコントローラを作り、Show
という名前のルートを作ります。
Show
ルートは controller_sample_base
カートリッジ内の templates/default/sample/test.isml
をテンプレートとしてブラウザに描画します。
さらにcontroller_sample_customに同様のTest.js
と言うコントローラを作り、controller_sample_baseのTest.js
を上書きしながらappend, prepend, replaceの動きを確認していきます。
作成したフォルダ構成は下記の画像の通りです。
ベースに利用するコードとテンプレート
'use strict';
var server = require('server');
server.get('Show', function (req, res, next) {
res.render('sample/test', {
param1: "Test",
});
next();
});
module.exports = server.exports();
<html>
<body>
<h1>${pdict.param1}</h1>
<h1>${pdict.param2}</h1>
<h1>${pdict.param3}</h1>
</body>
</html>
ベースのみで実行した場合
ベースのみのカートリッジで「https://your-env.commercecloud.salesforce.com/on/demandware.store/Sites-YourSite-Site/ja_JP/Test-Show」のようにブラウザからアクセスした場合このような見た目になります。
1.カートリッジはカートリッジパスの左が優先
カートリッジパスは左が優先になります。
関連するドキュメントとしてInfo Center、Qiitaがあります。
今回は作成したカートリッジを優先させた上でさらにcustom→baseと言うようにカスタムが優先となるようにカートリッジパスを設定します。
カートリッジパス:
controller_sample_custom:controller_sample_base:その他のカートリッジ...
これで、カスタムカートリッジでTest.jsを上書くor拡張することができます。
例えば、controller_sample_base/cartridge/controller/Test.js
のコードをcontroller_sample_custom/cartridge/controller/Test.js
へコピペして、"Test"
を
"Test2"
へ書き換えた場合、controller_sample_custom
のTest.js
が優先されるため画像のような見た目になります。(ただし本当にベースを完全に置き換えたい場合は、このやり方ではカスタムの内容がベースのような意味合いになるので、ルートを置き換える場合は、後述するreplaceをお勧めします。)
Tips
今回はコントローラの上書きの例でしたが、他にもscripts配下やtempletes配下のファイルも同じディレクトリでかつ同じ名前の場合はカートリッジパスの左側にあるカートリッジのファイルが優先されます。
2.append, prepend, replaceのそれぞれの役割
append, prependはベースカートリッジの挙動を活かしつつ、処理をしたいときに使い、 replaceは文字通りルートを丸っと置き換えたい場合に利用します。ルートの処理はprepend→(get/post/use)→appendの順で処理が走ります。そのため、既存のルートの前に処理を挟みたい場合はprepend、後に挟みたい場合はappendとなります。
※get/post/useの違い
append, prependの挙動の確認
実際にappend, prependを使ってみて挙動を確認してみましょう。
下記に実験するコードを記載します。
==省略==
server.get('Show', function (req, res, next) {
res.render('sample/test', {
param1: "Test from get",
param2: "Test from get",
param3: "Test from get",
});
next();
});
==省略==
※ append, prepend, replaceを利用する場合は server.extend(module.superModule)
を呼ぶ必要がありますのでご注意ください。
"use strict";
var server = require("server");
server.extend(module.superModule);
server.prepend('Show',function (req, res, next) {
var viewData = res.getViewData();
viewData.param2 = 'Test from prepend';
res.setViewData(viewData);
next();
});
server.append("Show", function (req, res, next) {
var viewData = res.getViewData();
viewData.param3 = "Test from append";
res.setViewData(viewData);
next();
});
module.exports = server.exports();
上記のコードを実行した場合、このような結果になります。
なぜこのような結果になったかというと、このような順序で処理が行われたからです。
(1) prependの実行
param1 → null
param2 → Test from prepend
param3 → null
(2) getの実行
param1 → Test from get
param2 → Test from prepend をTest from getで上書き
param3 → Test from get
(3) appendの実行
param1 → Test from get
param2 → Test from get
param3 → Test from getをTest from appendで上書き
replaceの挙動の確認
上記の「append, prependの挙動の確認」で使用したベースのTest.jsのコードに対して下記のカスタムコードを利用します。
"use strict";
var server = require("server");
server.extend(module.superModule);
server.replace('Show', function (req, res, next) {
var viewData = res.getViewData();
viewData.param1 = 'Test from replace';
res.setViewData(viewData);
res.render('sample/test');
next();
});
module.exports = server.exports();
このカスタムコードを実行した場合、画像のようになります。
なぜこのような結果になったかというと、ベースのTest.jsのShowは全く実行されず、カスタムのTest.jsのShowが実行されたためです。
replace はベースが get や post だったとしても置き換えるため、結果的に get と post どっちでもいいようなルートになるのでご注意ください。get のみに制限したい場合は、下記のヘルプのようにミドルウェアを利用します。
https://github.com/SalesforceCommerceCloud/storefront-reference-architecture/blob/master/cartridges/modules/server/README.md#replacing-a-route
追記 2022/6/8
カートリッジが3つ以上の場合の挙動も記載しておきます。
例えば、このようなカートリッジパスがあり、それぞれのカスタムカートリッジにappendもprependもあった場合、
controller_sample_custom1:controller_sample_custom2:controller_sample_base
下記のように1から順番に実行されるようです。
- custom1のprepend
- custom2のprepend
- baseの(get/post/use)
- custom2のappend
- custom1のappend
replaceを途中で入れた場合も記載しておきます。
例えば、このようなカートリッジパスがあり、カスタムカートリッジにappendもprependもあった場合、
controller_sample_custom:controller_sample_replace:controller_sample_base
下記のように1から順番に実行されるようです。
- customのprepend
- replaceの(get/post/use)
- customのappend
つまり、baseは置き換えられ、完全に実行されなくなります。
3.コントローラのミドルウェアは左のファンクションから実行される
コントローラには本処理以外の処理を挟む事もできます。それをミドルウェアと呼んでいます。
ミドルウェアを作成する。
'use strict';
function sampleOne(req, res, next) {
var viewData = res.getViewData();
viewData.param1 = viewData.param1 + ' :Test from middleware sampleOne';
res.setViewData(viewData);
next();
}
function sampleTwo(req, res, next) {
var viewData = res.getViewData();
viewData.param1 = viewData.param1 + ' :Test from middleware sampleTwo';
res.setViewData(viewData);
next();
}
module.exports = {
sampleOne: sampleOne,
sampleTwo: sampleTwo,
};
ミドルウェアをコントローラに設定。
'use strict';
var server = require('server');
var sample = require('~/cartridge/scripts/middleware/sample');
server.get('Show', sample.sampleOne, sample.sampleTwo, function (req, res, next) {
var viewData = res.getViewData();
viewData.param1 = viewData.param1 + ' :Test';
res.setViewData(viewData);
res.render('sample/test');
next();
});
module.exports = server.exports();
なぜこうなるかというと、このような順序で処理が行われたからです。
(1) sampleOneの実行
param1 → undifined + :Test from middleware sampleOne
(2) sampleTwoの実行
param1 → param1 + :Test from middleware sampleTwo
(3) 本処理の実行
param1 → param1 + :Test
こちらのヘルプを見たらわかるのですが、
get(name : string, arguments : Array.<function()>)
なので、左から設定されているファンクションが順に実行されます。
そのため、例えば、
server.get('Show', sample.sampleOne, function (req, res, next) {
==省略本処理==
}, sample.sampleTwo);
みたいな感じで、sampleOneの実行 → 本処理の実行 → sampleTwoの実行のように順番を変えることも可能です。
Tips
外部のモジュールをインポートする際に利用している ~
は自ファイルが所属しているカートリッジのみを探索します。その他のカートリッジ内のモジュールを探したい時は *
を利用します。
今回の例で言うと、
var sample = require('~/cartridge/scripts/middleware/sample')
または、
var sample = require('*/cartridge/scripts/middleware/sample')
まとめ
append, prependとミドルウェアを合わせた場合の処理の順番を記載します。
- prependに設定された左からの関数が順に実行される
- get/post/useに設定された左からの関数が順に実行される
- appendに設定された左からの関数が順に実行される
ミドルウェアがあるからappendとかprependいらないんじゃ?と思う方いらっしゃるかも知れませんが、SFRAの作りとしてはベースのコードは修正せずにカスタムして行った方がいいです。
appendやprependはベースとなっているコードを一切触らずに処理を追加できるので非常に便利です。