【EC-CUBE 4】商品一覧を複数ページに分割して表示する(ページネーション)

本記事では、独自に作成した商品一覧ページを以下のように複数ページに分けて表示する、ページネーションのカスタマイズについて紹介します。

デフォルトの商品一覧ページ(カテゴリー別の商品一覧を含む)には、商品を20件 / 40件 / 60件ごとにページを分けて表示させるページネーションが実装されています。本記事では、これとは別にオリジナルの商品一覧ページ(タグ別の商品一覧など)にページネーションを導入する方法について紹介します。

商品一覧の下側にある矢印ボタンをクリックすると、次または前、最初または最後の商品一覧が表示されます。

【動作環境】
EC CUBEのバージョン:4.2.2
サーバー:Xserver

開発前にデバッグモードの設定をお薦めします

デバッグモードを設定しておくと、エラーが起きたときに詳細情報が表示されるようになります。
エラー箇所を探しやすくなるので、開発前に設定しておくのをオススメします。
デバッグモードの設定方法については 以下記事 で解説しています。

カスタマイズ後は、デバッグモードの解除を忘れないように。

目次

ページネーションの実装手順

  1. リポジトリ(Repository)を修正し、ページネーションに対応するクエリを作成
  2. コントローラ(Controller)を修正し、リポジトリに作成したクエリを使ってページングの設定
  3. Twigテンプレートでページネーションリンクを作成
STEP

リポジトリ(Repository)を修正し、ページネーションに対応するクエリを作成

まず商品データを取得するリポジトリを作成します。
本記事では、既存のProductRepositoryを拡張したCustomProductRepositoryを作成し、IDが小さい順に商品データを取得するfindProductsOrderByIdメソッドと、ページネーション処理を行うpaginateメソッドを追加しました。

<?php

namespace Customize\Repository;

use Eccube\Repository\ProductRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;

class CustomProductRepository extends ProductRepository
{
    public function findProductsOrderById($currentPage = 1, $limit = 20)
    {
        // クエリビルダーを使って、商品(Product)をID順に昇順(小さい順)で並べる準備をする
        $qb = $this->createQueryBuilder('p')->orderBy('p.id', 'ASC');

        // ページごとのデータを取得するために、ページネーションの処理をする
        $pagenator = $this->paginate($qb, $currentPage, $limit);
        return $pagenator; // ページネーションされた結果を返す
    }

    public function paginate($qb, $page = 1, $limit = 20)
    {
        // Paginatorクラスを使って、クエリビルダーの結果をページネーションする準備をする
        $paginator = new Paginator($qb);

        // クエリに、どのページのデータを取るかと、1ページに何件表示するかを設定する
        $paginator->getQuery()
            ->setFirstResult($limit * ($page - 1)) // 何件目からデータを取るかを設定する(オフセット)
            ->setMaxResults($limit); // 1ページあたりの最大件数を設定する

        return $paginator; // ページネーションされた結果を返す
    }
}

このリポジトリでは、IDが小さい順(昇順)に商品を取得し、指定されたページ($currentPage)に対応する最初の商品から、1ページあたり最大20件($limit)の商品を表示するように設定しています。(Controller側で引数を設定することで変更可)

$currentPage が2、$limitが10の場合は、11件目から10件ごとに商品が表示されるようになります。

STEP

コントローラ(Controller)を修正し、リポジトリに作成したクエリを使ってページングの設定

次に、コントローラでページ数を受け取り、findProductsOrderByIdメソッドを呼び出して商品を取得します。

<?php

namespace Customize\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Eccube\Controller\AbstractController;
use Customize\Repository\CustomProductRepository;

class SampleController extends AbstractController
{
    /**
     * @var CustomProductRepository
     */
    protected $customProductRepository;

    /**
     * @param CustomProductRepository $customProductRepository
     */
    public function __construct(CustomProductRepository $customProductRepository) {
        $this->customProductRepository = $customProductRepository;
    }
    
    /**
     * @Route("/sample", name="sample")
     * @Template("sample.twig")
     */
    public function index(Request $request)
    {
        // リクエストから現在のページ番号を取得する。デフォルトは1ページ目。
        $page = $request->query->getInt('page', 1);

        // 1ページあたりに表示する商品の数を設定(今回は4件)。
        $limit = 4;

        // 商品をID順に並べ、指定されたページのデータを取得する。
        $paginator = $this->customProductRepository->findProductsOrderById($page, $limit);

        // 全ページ数を計算する。ceil関数で小数点以下を切り上げる。
        $maxPages = ceil($paginator->count() / $limit);

        // 現在のページに表示する商品のリストを取得する。
        $products = $paginator->getIterator();

        return [
            'products' => $products,
            'maxPages' => $maxPages, // 全ページ数
            'thisPage' => $page, // 現在のページ番号
        ];
    }
}

$page$limitの数値を変更することで、商品を何件目から何件ずつ取得するのか変更できます。

STEP

Twigテンプレートでページネーションリンクを作成

最後に、ページを進む・戻るためのリンクをTwigテンプレートに記述して完成です。

{% extends 'default_frame.twig' %}

{% block main %}
  <!-- 商品リストの表示 -->
  <div class="ec-shelfRole">
      <ul class="ec-shelfGrid">
          {# TagProductsに格納されているデータを順に取り出し、TagProductに代入 #}
          {% for Product in products %}
              <li class="ec-shelfGrid__item">
                {# 商品画像の表示 #}
                <p class="ec-shelfGrid__item-image">
                    <img src="{{ asset(Product.main_list_image|no_image_product, 'save_image') }}" alt="{{ Product.name }}" loading="lazy">
                </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>
              </li>
          {% endfor %}
      </ul>
  </div>

  <!-- 商品が複数ページにまたがる場合、ページネーションリンクを表示 -->
  {% if maxPages > 1 %}
    <ul class="ec-pager">
      <!-- 2ページ目以降にいる場合、前のページへ戻る2つのリンクを表示 -->
      {% if thisPage > 1 %}
      <li class="ec-pager__item">
        <!-- 「最初のページへ」リンク: 最初のページに移動 -->
        <a class="page-link" href="{{ url('sample', {page:1}) }}">|≪</a>
      </li>
      <li class="ec-pager__item">
        <!-- 「前のページへ」リンク: 現在のページの1つ前に移動 -->
        <a class="page-link" href="{{ url('sample', {page:thisPage - 1 < 1 ? 1 : thisPage - 1}) }}"><</a>
      </li>
      {% endif %}

      <!-- 最後のページより前にいる場合、次のページへ進む2つのリンクを表示 -->
      {% if thisPage < maxPages %}
      <li class="ec-pager__item">
        <!-- 「次のページへ」リンク: 現在のページの1つ後に移動 -->
        <a class="page-link" href="{{ url('sample', {page:thisPage + 1 <= maxPages ? thisPage + 1 : thisPage}) }}">></a>
      </li>
      <li class="ec-pager__item">
        <!-- 「最後のページへ」リンク: 最後のページに移動 -->
        <a class="page-link" href="{{ url('sample', {page:maxPages}) }}">≫|</a>
      </li>
      {% endif %}
    </ul>
  {% endif %}
{% endblock %}

コントローラから渡されるmaxPagesthisPageを使い、「前へ」「次へ」のリンクを作成します。


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