【EC-CUBE 4】自動更新される新着商品一覧ページ / ブロックを作る方法

EC-CUBE 4で、新しく登録した商品が自動的に並ぶ「新着商品」の一覧ページとブロックを作る方法です。デフォルトの新着商品ブロックは表示する商品をTwigに直接書く方式のため、商品を登録するたびに手で書き換える必要があります。それを自動化します。

新着商品を自動で入れ替えるには?

ブロックだけでよいなら、いまは標準機能で済みます。「新着商品(自動取得)」ブロックをレイアウト管理で配置するだけです(後述)。

専用の一覧ページを作る、表示件数や並び順を自分で決める、といった場合はControllerを書きます。要はRepositoryで商品IDの降順に取り出すだけです。

$products = $this->productRepository->findBy([], ['id' => 'DESC']);

まずは標準の「新着商品(自動取得)」ブロックを確認してください。

EC-CUBEのアップデートにより、本記事の後半と同等の機能を持つ auto_new_item.twig(新着商品(自動取得))が標準で追加されました。コンテンツ管理 → レイアウト管理から、トップページの「新着商品」ブロックを「新着商品(自動取得)」に差し替えるだけで実装できます。

標準ブロックで足りるならそれが一番安全です。本記事は専用の一覧ページを作りたい場合や、件数・並び順・表示項目を自分で決めたい場合にお読みください。

標準の「新着商品(自動取得)」本記事で作るもの
用意するものなし(レイアウト管理で差し替えるだけ)Controller と Twig
表示件数最大5件(設定値で変更可)自由
非公開・廃止の商品自動で除外される自分で除外する必要がある
専用の一覧ページ作れない作れる
表示項目のカスタマイズTwigの上書きが必要自由

【動作環境】EC-CUBEのバージョン:4.3.0 / サーバー:XServer

エラーの内容を画面で確認できるよう、作業前にデバッグモードを設定しておくのがおすすめです(作業後の解除もお忘れなく)。

デバッグモードの設定/解除方法
目次

【自動更新】新着商品ページの作成

まず、新着商品の専用ページを作ります。

作成前と作成後

新着商品ページの作成前。見出しだけが表示され、商品が並んでいない状態

作成前:見出しだけのページ

最低限のControllerとTwigだけを置いた状態
新着商品ページの作成後。商品の画像・ID・品名・価格が商品IDの大きい順に並んでいる

作成後:新しい商品から順に並ぶ

左から右へ、商品ID の大きい順に「画像・ID・品名・価格」を表示

スタート地点のコード

必要最低限のControllerとTwigを置いた状態から始めます。同じ状態を作りたい方は 新規ページの作成方法 を参照してください。

項目
URL(ドメイン名)/sample
Twigapp/template/default/sample.twig
Controllerapp/Customize/Controller/SampleController.php
{% extends 'default_frame.twig' %}

{% block main %}
<h1>新着商品ページ</h1>

{% endblock %}
<?php

namespace Customize\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Eccube\Controller\AbstractController;

class SampleController extends AbstractController
{
    /**
     * @Route("/sample", name="sample")
     * @Template("sample.twig")
     */
    public function index(Request $request)
    {
            return [];
    }
}

作成手順

やることは3つです。

  1. 1Controllerを修正して、Repositoryで商品を新しい順に取り出す
  2. 2Twigを修正して、受け取った商品を順に並べる
  3. 3管理画面からキャッシュを削除する
  1. STEP1

    Controllerを修正する

    <?php
    
    namespace Customize\Controller;
    
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\Routing\Annotation\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    use Eccube\Controller\AbstractController;
    use Eccube\Repository\ProductRepository;
    
    class SampleController extends AbstractController
    {
        /**
         * @var ProductRepository
         */
        protected $productRepository;
    
        /**
         * @param ProductRepository $productRepository
         */
        public function __construct(ProductRepository $productRepository) {
            $this->productRepository = $productRepository;
        }
    
        /**
         * @Route("/sample", name="sample")
         * @Template("sample.twig")
         */
        public function index()
        {
            $products = $this->productRepository->findBy([], ['id' => 'DESC']);
    
            return [
                'Products' => $products,
            ];
        }
    }

    やっていることは2つです。

    • ProductRepository をコンストラクタで受け取り、プロパティに入れる(商品テーブルを扱う窓口)
    • findBy() で商品を取り出し、Products という名前でTwigへ渡す

    findBy() の引数

    • 第1引数 []:絞り込み条件(空なので全件)
    • 第2引数 ['id' => 'DESC']:並び順。DESC は降順(大きい順)、ASC なら昇順

    つまり「商品テーブルの全データをIDの大きい順で取得」しています。

    この書き方では非公開・廃止の商品も混ざります。公開中の商品だけを並べたい場合は、第1引数に ['Status' => 1] を足してください。詳しくは 公開状態に応じて商品を非表示にする方法 にまとめています。

    リポジトリでよく使われるメソッド解説(find, findByなど) リポジトリで独自の商品検索メソッドを作成する(expr)
  2. STEP2

    Twigを修正する

    {% extends 'default_frame.twig' %}
    
    {% block main %}
        <h1>新着商品ページ</h1>
        <div class="ec-shelfRole">
          <ul class="ec-shelfGrid">
              {% for Product in Products %}
                <li class="ec-shelfGrid__item">
                  <a href="{{ url('product_detail', {'id': Product.id}) }}">
                    {# 商品画像の表示 #}
                    <p class="ec-shelfGrid__item-image">
                        <img src="{{ asset(Product.main_list_image|no_image_product, 'save_image') }}" alt="{{ Product.name }}">
                    </p>
                    {# 商品IDの表示 #}
                    <p>ID: {{ Product.id }}</p>
                    {# 商品名の表示 #}
                    <p>{{ Product.name }}</p>
                    {# 価格の表示 #}
                    <p class="price02-default">
                        {% if Product.hasProductClass %}
                            {% if Product.getPrice02Min == Product.getPrice02Max %}
                                {{ Product.getPrice02IncTaxMin|price }}
                            {% else %}
                                {{ Product.getPrice02IncTaxMin|price }} ~ {{ Product.getPrice02IncTaxMax|price }}
                            {% endif %}
                        {% else %}
                            {{ Product.getPrice02IncTaxMin|price }}
                        {% endif %}
                    </p>
                  </a>
                </li>
              {% endfor %}
          </ul>
        </div>
    {% endblock %}

    長く見えますが、ほとんどがデフォルトの「商品一覧ページ」からの引用です。

    STEP1のControllerで Products にIDの大きい順で商品が入っているので、for でひとつずつ Product として取り出し、次の項目を並べています。

    書き方表示されるもの
    Product.main_list_imageメイン画像
    Product.id商品ID
    Product.name商品名
    Product.getPrice02IncTaxMin価格(税込)

    no_image_product は、画像が登録されていない商品に「No Image」の画像を当てるEC-CUBE独自のフィルタです。price は数値を価格表記へ整えるフィルタです。

    Twig まとめ(2) ~ if / for / set / with ~
  3. STEP3

    キャッシュを削除する

    ここまでで準備完了です。管理画面からキャッシュを削除して、ページを開いてみましょう。

    EC-CUBEの管理画面キャッシュ管理から、キャッシュを削除するボタンを押す画面

    コンテンツ管理 → キャッシュ管理から削除できる

    冒頭でお見せしたような一覧ページが表示されれば成功です。

【自動更新】新着商品ブロックの作成

次に、トップページの新着商品ブロックを自動更新にします。デフォルトのレイアウトでは、新着商品ブロックはトップページに置かれているので、トップページを見ながら進めます。

くり返しになりますが、ブロックだけなら標準の「新着商品(自動取得)」で済みます。こちらはレイアウト管理で差し替えるだけで、商品IDの大きい順に最大5件が自動で並びます。以降は「件数や表示項目を自分で決めたい」場合の手順です。

作成前と作成後

デフォルトの新着商品ブロック。Twigに直接書かれた3商品が固定で表示されている

作成前:中身が固定されている

新しく商品を登録しても、Twigを手で直さない限り表示は変わらない
自動更新に変更した新着商品ブロック。商品IDの大きい順に商品が並んでいる

作成後:新しい商品から順に並ぶ

商品を登録すると自動的に入れ替わる

初期状態の新着商品ブロック(new_item.twig)は次のようになっています。3つの商品がそれぞれ直接書かれているのが分かります。

<div class="ec-role">
    <div class="ec-newItemRole">
        <div class="ec-newItemRole__list">
            <div class="ec-newItemRole__listItem">
                <div class="ec-newItemRole__listItemHeading ec-secHeading--tandem">
                    <span class="ec-secHeading__en">{{ 'NEW ITEM'|trans }}</span>
                    <span class="ec-secHeading__line"></span>
                    <span class="ec-secHeading__ja">{{ '新着商品'|trans }}</span>
                    <a class="ec-inlineBtn--top" href="{{ url('product_list') }}">{{ 'more'|trans }}</a>
                </div>
            </div>
            <div class="ec-newItemRole__listItem">
                <a href="{{ url('product_detail', {'id': '1'}) }}">
                    <img src="{{ asset('cube-1.png', 'save_image') }}">
                    <p class="ec-newItemRole__listItemTitle">{{ '彩のジェラート"CUBE"'|trans }}</p>
                    <p class="ec-newItemRole__listItemPrice">{{ '¥1,200(税込)'|trans }}</p>
                </a>
            </div>
            <div class="ec-newItemRole__listItem">
                <a href="{{ url('product_detail', {'id': '2'}) }}">
                    <img src="{{ asset('sand-1.png', 'save_image') }}">
                    <p class="ec-newItemRole__listItemTitle">{{ 'チェリーアイスサンド'|trans }}</p>
                    <p class="ec-newItemRole__listItemPrice">{{ '¥800(税込)'|trans }}</p>
                </a>
            </div>
            <div class="ec-newItemRole__listItem">
                <a href="{{ url('product_detail', {'id': '1'}) }}">
                    <img src="{{ asset(''|no_image_product , 'save_image') }}">
                    <p class="ec-newItemRole__listItemTitle">{{ '彩のジェラート"CUBE" NEO'|trans }}</p>
                    <p class="ec-newItemRole__listItemPrice">{{ '¥600(税込)'|trans }}</p>
                </a>
            </div>
        </div>
    </div>
</div>

作成手順

ページ編とほぼ同じですが、Controllerを新しく作るのではなく既存のTopControllerを複製して直すのが違いです。

  1. 1TopControllerapp/Customize/Controller へ複製する
  2. 2複製した TopController を修正する
  3. 3新着商品ブロックのTwigを修正する
  4. 4管理画面からキャッシュを削除する
  1. STEP1

    TopControllerを複製する

    トップページのControllerはすでに用意されているので、src/Eccube/Controller にある TopController.phpapp/Customize/Controller へ複製し、そちらを修正します。

    src配下のTopController.phpをapp/Customize/Controller配下へ複製したファイル構成

    app/Customize/Controller へ複製する

    src 配下のControllerを直接書き換えても動きますが、元に戻せなくなったり、EC-CUBEのアップデートで不具合が出たりする可能性があるためおすすめしません。

    なぜ複製すると差し替わるのか

    EC-CUBEは本体のControllerを先に、app/Customize/Controller をあとに読み込みます。ルート名が同じ場合、あとから読まれたほうが前のものを置き換えます。複製した TopControllername="homepage" のままなので、こちらが採用されるわけです。

    この仕組みは意図せず他のページを壊すこともあります。詳しくは user_dataを含むページのController で解説しています。

  2. STEP2

    複製したTopControllerを修正する

    基本はページ編と同じですが、namespaceuse を書き換える点に注意してください。

    <?php
    
    namespace Customize\Controller;
    
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    use Symfony\Component\Routing\Annotation\Route;
    use Eccube\Controller\AbstractController;
    use Eccube\Repository\ProductRepository;
    
    class TopController extends AbstractController
    {
        /**
         * @var ProductRepository
         */
        protected $productRepository;
    
        /**
         * @param ProductRepository $productRepository
         */
        public function __construct(ProductRepository $productRepository) {
            $this->productRepository = $productRepository;
        }
    
        /**
         * @Route("/", name="homepage", methods={"GET"})
         * @Template("index.twig")
         */
        public function index()
        {
          $products = $this->productRepository->findBy([], ['id' => 'DESC'], 3);
    
          return [
              'Products' => $products,
          ];
        }
    }

    ページ編との違いは findBy()第3引数に 3 を渡しているところです。これで「IDの降順で3件だけ取得」になります。件数を変えたければこの数字を変えてください。

  3. STEP3

    新着商品ブロックのTwigを修正する

    直接書かれていた3つの商品を、for でまわす形に置き換えます。

    <div class="ec-role">
        <div class="ec-newItemRole">
            <div class="ec-newItemRole__list">
                <div class="ec-newItemRole__listItem">
                    <div class="ec-newItemRole__listItemHeading ec-secHeading--tandem">
                        <span class="ec-secHeading__en">{{ 'NEW ITEM'|trans }}</span>
                        <span class="ec-secHeading__line"></span>
                        <span class="ec-secHeading__ja">{{ '新着商品'|trans }}</span>
                        <a class="ec-inlineBtn--top" href="{{ url('product_list') }}">{{ 'more'|trans }}</a>
                    </div>
                </div>
    
                {% for product in Products %}
                    <div class="ec-newItemRole__listItem">
                        <a href="{{ url('product_detail', {'id': product.id}) }}">
                            <img src="{{ asset(product.main_list_image|no_image_product, 'save_image') }}" alt="{{ product.name }}">
                            <p class="ec-newItemRole__listItemTitle">{{ product.name }}</p>
                            <p class="ec-newItemRole__listItemPrice">
                                {% if product.hasProductClass %}
                                    {% if product.getPrice02Min == product.getPrice02Max %}
                                        {{ product.getPrice02IncTaxMin|price }}
                                    {% else %}
                                        {{ product.getPrice02IncTaxMin|price }} ~ {{ product.getPrice02IncTaxMax|price }}
                                    {% endif %}
                                {% else %}
                                    {{ product.getPrice02IncTaxMin|price }}
                                {% endif %}
                            </p>
                        </a>
                    </div>
                {% endfor %}
            </div>
        </div>
    </div>

    STEP2のControllerで Products にIDの大きい順で3件が入っているので、ページ編と同じ要領で並べているだけです。

  4. STEP4

    キャッシュを削除する

    ページ編と同じく、管理画面のキャッシュ管理から削除してトップページを開きます。商品登録の新しい順(=商品IDの大きい順)に並んでいれば成功です。

    このブロックはトップページ専用です。トップページ以外でそのまま使うと、エラーになるか意図しない表示になります。

    Twigの Products に商品を入れているのは、あくまでSTEP2で修正した TopController だからです。他のページのControllerは何も渡していないため、変数が空のままになります。他のページでも使いたい場合は、そのページのControllerも同じように修正してください。

まとめ

新着商品の一覧ページとブロックを自動更新にする方法を紹介しました。

  • ブロックだけなら標準の「新着商品(自動取得)」で足りる。まずはこちらを試す
  • 専用ページや件数・表示項目の自由度が要るなら、Controllerで findBy([], ['id' => 'DESC']) して並べる
  • 件数を絞るなら findBy() の第3引数を使う
  • 自作した場合は非公開・廃止の商品も混ざるので、絞り込み条件を足す
  • ブロック用に TopController を複製した場合、そのブロックはトップページ専用になる
  • 修正後は管理画面からキャッシュを削除する

応用すれば「古い商品順に並べる」といったこともできます。本格的なカスタマイズをお考えの方はぜひ試してみてください。

EC-CUBEのカスタマイズに関する記事

この記事に出てきた用語

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次