本記事では、独自に作成した商品一覧ページを以下のように複数ページに分けて表示する、ページネーションのカスタマイズについて紹介します。
デフォルトの商品一覧ページ(カテゴリー別の商品一覧を含む)には、商品を20件 / 40件 / 60件ごとにページを分けて表示させるページネーションが実装されています。本記事では、これとは別にオリジナルの商品一覧ページ(タグ別の商品一覧など)にページネーションを導入する方法について紹介します。
商品一覧の下側にある矢印ボタンをクリックすると、次または前、最初または最後の商品一覧が表示されます。
デバッグモードを設定しておくと、エラーが起きたときに詳細情報が表示されるようになります。
エラー箇所を探しやすくなるので、開発前に設定しておくのをオススメします。
デバッグモードの設定方法については 以下記事 で解説しています。
カスタマイズ後は、デバッグモードの解除を忘れないように。
ページネーションの実装手順
- リポジトリ(Repository)を修正し、ページネーションに対応するクエリを作成
- コントローラ(Controller)を修正し、リポジトリに作成したクエリを使ってページングの設定
- Twigテンプレートでページネーションリンクを作成
リポジトリ(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側で引数を設定することで変更可)
コントローラ(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, // 現在のページ番号
];
}
}
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 %}