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・品名・価格」を表示スタート地点のコード
必要最低限のControllerとTwigを置いた状態から始めます。同じ状態を作りたい方は 新規ページの作成方法 を参照してください。
| 項目 | 値 |
|---|---|
| URL | (ドメイン名)/sample |
| Twig | app/template/default/sample.twig |
| Controller | app/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つです。
- 1Controllerを修正して、Repositoryで商品を新しい順に取り出す
- 2Twigを修正して、受け取った商品を順に並べる
- 3管理画面からキャッシュを削除する
-
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の大きい順で取得」しています。
リポジトリでよく使われるメソッド解説(find, findByなど) リポジトリで独自の商品検索メソッドを作成する(expr)この書き方では非公開・廃止の商品も混ざります。公開中の商品だけを並べたい場合は、第1引数に
['Status' => 1]を足してください。詳しくは 公開状態に応じて商品を非表示にする方法 にまとめています。 -
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価格(税込) Twig まとめ(2) ~ if / for / set / with ~no_image_productは、画像が登録されていない商品に「No Image」の画像を当てるEC-CUBE独自のフィルタです。priceは数値を価格表記へ整えるフィルタです。 -
STEP3
キャッシュを削除する
ここまでで準備完了です。管理画面からキャッシュを削除して、ページを開いてみましょう。
コンテンツ管理 → キャッシュ管理から削除できる
冒頭でお見せしたような一覧ページが表示されれば成功です。
【自動更新】新着商品ブロックの作成
次に、トップページの新着商品ブロックを自動更新にします。デフォルトのレイアウトでは、新着商品ブロックはトップページに置かれているので、トップページを見ながら進めます。
くり返しになりますが、ブロックだけなら標準の「新着商品(自動取得)」で済みます。こちらはレイアウト管理で差し替えるだけで、商品IDの大きい順に最大5件が自動で並びます。以降は「件数や表示項目を自分で決めたい」場合の手順です。
作成前と作成後
作成前:中身が固定されている
新しく商品を登録しても、Twigを手で直さない限り表示は変わらない
作成後:新しい商品から順に並ぶ
商品を登録すると自動的に入れ替わる初期状態の新着商品ブロック(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
TopControllerをapp/Customize/Controllerへ複製する - 2複製した
TopControllerを修正する - 3新着商品ブロックのTwigを修正する
- 4管理画面からキャッシュを削除する
-
STEP1
TopControllerを複製する
トップページのControllerはすでに用意されているので、
src/Eccube/ControllerにあるTopController.phpをapp/Customize/Controllerへ複製し、そちらを修正します。
app/Customize/Controllerへ複製するsrc配下のControllerを直接書き換えても動きますが、元に戻せなくなったり、EC-CUBEのアップデートで不具合が出たりする可能性があるためおすすめしません。なぜ複製すると差し替わるのか
EC-CUBEは本体のControllerを先に、
app/Customize/Controllerをあとに読み込みます。ルート名が同じ場合、あとから読まれたほうが前のものを置き換えます。複製したTopControllerはname="homepage"のままなので、こちらが採用されるわけです。この仕組みは意図せず他のページを壊すこともあります。詳しくは user_dataを含むページのController で解説しています。
-
STEP2
複製したTopControllerを修正する
基本はページ編と同じですが、
namespaceとuseを書き換える点に注意してください。<?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件だけ取得」になります。件数を変えたければこの数字を変えてください。 -
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件が入っているので、ページ編と同じ要領で並べているだけです。 -
STEP4
キャッシュを削除する
ページ編と同じく、管理画面のキャッシュ管理から削除してトップページを開きます。商品登録の新しい順(=商品IDの大きい順)に並んでいれば成功です。
このブロックはトップページ専用です。トップページ以外でそのまま使うと、エラーになるか意図しない表示になります。
Twigの
Productsに商品を入れているのは、あくまでSTEP2で修正したTopControllerだからです。他のページのControllerは何も渡していないため、変数が空のままになります。他のページでも使いたい場合は、そのページのControllerも同じように修正してください。
まとめ
新着商品の一覧ページとブロックを自動更新にする方法を紹介しました。
- ブロックだけなら標準の「新着商品(自動取得)」で足りる。まずはこちらを試す
- 専用ページや件数・表示項目の自由度が要るなら、Controllerで
findBy([], ['id' => 'DESC'])して並べる - 件数を絞るなら
findBy()の第3引数を使う - 自作した場合は非公開・廃止の商品も混ざるので、絞り込み条件を足す
- ブロック用に
TopControllerを複製した場合、そのブロックはトップページ専用になる - 修正後は管理画面からキャッシュを削除する
応用すれば「古い商品順に並べる」といったこともできます。本格的なカスタマイズをお考えの方はぜひ試してみてください。
EC-CUBEのカスタマイズに関する記事
- カスタマイズのまとめ
- 公開状態に応じて商品を非表示にする方法
- 特定のタグを付けた商品のみを抽出し、一覧ページを作る方法
- 新規ページの作成方法(user_dataなし)
- リポジトリでよく使われるメソッド解説(find, findByなど)
- リポジトリで独自の商品検索メソッドを作成する(expr)
- Twig まとめ(2) ~ if / for / set / with ~

