2021/02/06
LaunchCartで直属の子カテゴリー一覧を取得する
LaunchCartの商品やページのカテゴリーで、あるカテゴリー直属の子カテゴリー一覧を取得したい場合、
少しコツが必要です。
MySQLなどのRDBといわれるデータベースでは、カテゴリーのような階層構造を保存するために様々な方法がありますが(参考: 階層構造(入れ子集合モデル)について – Qiita)、
LaunchCartのカテゴリーでは、「入れ子集合モデル」という、構造が使われています(参考: 第5回 SQLで木構造を扱う~入れ子集合モデル (1)入れ子集合モデルとは何か :SQLアタマアカデミー|gihyo.jp … 技術評論社)。
例)
id | lft | rgt | root |
---|---|---|---|
1 | 1 | 4 | 1 |
2 | 2 | 3 | 1 |
3 | 1 | 1 | 3 |
↑2が1の子であることを示している
入れ子集合モデルは複雑なツリー構造をRDBで実現するために誕生した画期的な階層構造の仕組みですが、
直属の子や親を取得するのには、ちょっと複雑な手続きが必要になります。
そのため、これまで直属の子カテゴリーを取得するために、様々な方法を試してきてましたが、
どれも重く、特にカテゴリーが大量に存在するモール系のECでは、実用に耐えない状況になっていました。
しかしながら、今回、比較的軽量で直属の子カテゴリー一覧を取得する方法に行き着くことができたので、紹介いたします。
LaunchCartの任意のテンプレート
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | {# 親となるカテゴリーを取得 ※カテゴリーアーカイブでは不要 #} {% set category = get_entity_manager().find('EcCoreBundle:ProductCategory', 12) %}{# ←数字はカテゴリーID #} {# 上で取得したカテゴリーより下層のカテゴリーを取得 #} {% set qb = get_entity_manager().getRepository('EcCoreBundle:ProductCategory').createQueryBuilder('c') %} {% set children = qb.where('c.root = :root').andWhere('c.lft > :lft').andWhere('c.rgt < :rgt').orderBy('c.lft', 'ASC').setParameter('lft', category.lft).setParameter('rgt', category.rgt).setParameter('root', category.root).getQuery().getResult() %} {# 直下のカテゴリーに絞り込んで表示 #} <ul> {% set previousCategory = false %} {% for child in children %} {# lftの昇順なので、最初のカテゴリーは必ず子カテゴリーになる #} {% if previousCategory %} {# 直前に取得した子カテゴリーの下層かどうかを判定 #} {% if not ((child.lft > previousCategory.lft) and (child.rgt < previousCategory.rgt)) %} <li> <a href="{{ path('ec_product_category', {'id': child.id}) }}"> {{ child.name }} </a> </li> {% set previousCategory = category %} {% endif %} {% else %} <li> <a href="{{ path('ec_product_category', {'id': child.id}) }}"> {{ child.name }} </a> </li> {% set previousCategory = category %} {% endif %} {% endfor %} </ul> |
こちらは、商品カテゴリーの例です。ページカテゴリーの場合は、’EcCoreBundle:ProductCategory’ を ‘PageCoreBundle:PageCategory’ に差し替えます。
ポイント
get_entity_manager()から始まるqueryBuilderで、rootが親カテゴリーと同じで [ .where(‘c.root = :root’) ]、且つ、lft、rgtがそれぞれ親カテゴリーの範囲に入っている[ .andWhere(‘c.lft > :lft’).andWhere(‘c.rgt < :rgt’) ]カテゴリーを取得します。
このとき、[ .orderBy(‘c.lft’, ‘ASC’) ] により lft の昇順で並び替えるのが、後から直属の子カテゴリーに絞り込むときに重要になります。
上記で取得したカテゴリーには、親の直属だけでなく、更に下層のカテゴリーが含まれています。
さらに、取得したカテゴリーを for文 でループさせ、それぞれのカテゴリーが、直前に絞り込んだ子カテゴリーの下層のカテゴリーでないかを判定し、
直属かどうかを絞り込みます。
このループで直属かどうかを判定できるのは、lftとrgtを使った階層構造の性質と、lft昇順で並び替えているためです。
今まで自分が模索した方法の中では一番早い方法でした。
もっといい方法もあるかもしれませんが、よろしければご利用ください。
キーワード:Symfony、Twig
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE