Magento2系で開発をしていると、商品ページへの導線を設置しないといけないことがあります。
ところが、正直に商品データを取ってきてURLを取得すると、商品が所属するカテゴリのパスがURLについたりつかなかったりすることがあり、キレイに1つに定まらないことがあります。
もちろんMagentoではCanonical URLの設定を有効にしておくことで重複URLをGoogleから指摘されることはなくなるが、URLがブレるのはなんだかとても釈然としない思いをします。
(これはたとえ設定で商品URLにカテゴリパスを付けない設定にしていても起きます)
#普通に書くとどうなるか
何も考えずに商品データからURLを取得すると、以下のようになることが多いでしょう。
($this->productRepositoryは\Magento\Catalog\Api\ProductRepositoryInterfaceを利用するクラスにDIしている前提)
$product = $this->productRepository->get($sku);
$url = $product->getProductUrl();
この書き方をすると、状況によってはURLが以下のようにいくつかのバリエーションを持ってしまいます。
#なぜ複数のパターンがあり得るか
\Magento\Catalog\Model\ProductのgetProductUrl()の実装を見ていくと、以下の順に処理をしていることがわかります。
- \Magento\Catalog\Model\Product\Url::getProductUrl()
- \Magento\Catalog\Model\Product\getCategoryId()
この\Magento\Catalog\Model\Product\getCategoryId()がクセモノで、定義をみると以下のように書かれています。
public function getCategoryId()
{
$category = $this->_registry->registry('current_category');
if ($category && in_array($category->getId(), $this->getCategoryIds())) {
return $category->getId();
}
return false;
}
この処理の中に出てくる、$this->_registryというのはMagento\Framework\Registryクラスのインスタンスです。
Magento内部でクラスをまたいだ変数の引き回しによく使われています。既にこのクラスにはdeprecatedがつけられていますが、未だにコア実装のあちこちで使われていて、このクラスを使わないと始末できないような処理もかなり存在します。
先の処理では他のクラスで登録されているかもしれないcurrent_categoryというキー値の値を取得し、値がセットされていて、かつ商品が属しているカテゴリIDにその値があるかをみています。
さらに\Magento\Catalog\Model\Product\Url::getProductUrl()の処理を読みすすめると、以下のことがわかると思います。
- カテゴリIDがある場合はURL書き換えデータの検索にカテゴリIDを併用する
- カテゴリIDがない場合はURL書き換えデータの検索は商品IDだけになる
#カテゴリパスを付けたくないときはどうするか
ここまで処理を読んできましたが、getProductUrl()には大事な箇所があります。それが以下の部分です。
if (!isset($params['_ignore_category']) && $product->getCategoryId() && !$product->getDoNotUseCategoryId()) {
$categoryId = $product->getCategoryId();
}
\Magento\Catalog\Model\Product\Url::getProductUrl()は定義上以下の2つの引数を取ります。
- \Magento\Catalog\Model\Product
- オプションパラメータ配列
$categoryIdに値がセットされるのを防ぐためには、\Magento\Catalog\Model\Product\Url::getProductUrl()の第2引数を
$params['_ignore_category'] = true;
という構成にするか、\Magento\Catalog\Model\Product::getProductUrl()を呼び出す前に、
$product->setDoNotUseCategoryId(true);
とすればOKです。
実際、関連商品やアップセル・クロスセル商品のリストを表示する処理ではsetDoNotUseCategoryId(true)が呼ばれています。
#まとめ
- Magentoで商品URLを取得する際は、たとえカテゴリパスをつけない設定にしていてもカテゴリパスがつくことがある。
- 商品URLにカテゴリパスをつけたくない場合は商品オブジェクトに対してsetDoNotUseCategoryId(true)を行う。
- または、URL生成処理に与えるパラメータに_ignore_categoryをtrueで含める。
というほんの僅かな対応で解決できます。