この記事では、既存のリポジトリを拡張して新しい機能(メソッド)を追加するカスタマイズについて紹介します。

デバッグモードを設定しておくと、エラーが起きたときに詳細情報が表示されるようになります。
エラー箇所を探しやすくなるので、開発前に設定しておくのをオススメします。
デバッグモードの設定方法については 以下記事 で解説しています。
カスタマイズ後は、デバッグモードの解除を忘れないように。
ProductRepositoryを拡張する
ProductRepositoryに新しい関数を定義したい場面を考えます。
例として、以下のような【特定のタグをもつ商品のみを表示する一覧ページ】を作成してみます。

こちらのページは 【EC-CUBE 4】特定のタグを付けた商品のみを抽出し、一覧ページを作る方法 にて既に紹介済ですが、商品の検索機能(クエリ)をControllerに定義しています。(リポジトリについては触れていません。)
ただ再利用性やメンテナンス性の観点から、このようなデータベースに関するクエリはリポジトリに移行することが推奨されています。そこで今回は、以下のような手順で前回記事の内容を改善したいと思います。
- ProductRepositoryの機能を引き継いだ- CustomProductRepositoryという新しいリポジトリを作成する。
- 前回記事で紹介しているControllerファイルのクエリ部分を、CustomProductRepositoryに移行する。
- Controllerで、CustomProductRepositoryに移行したクエリを呼び出す。
CustomProductRepositoryは ProductRepositoryで定義されたすべてのメソッドを継承するため、ProductRepository の find(), findAll(), findOneBy() などをそのまま使うことができます!
CustomProductRepositoryの作成
以下コードを記したファイルを作成し、「app/Customize/Repository」にアップします。
<?php
namespace Customize\Repository;
use Eccube\Repository\ProductRepository;
class CustomProductRepository extends ProductRepository
{
    public function findProductsByTagId($tagId)
    {
        $qb = $this->createQueryBuilder('p');
        $qb->innerJoin('p.ProductTag', 'pt')
            ->innerJoin('pt.Tag', 't')
            ->andWhere('t.id = :Tag')
            ->setParameter('Tag', $tagId);
        return $qb->getQuery()->getResult();
    }
}クラスを定義する際にextends ProductRepositoryを付け加えることで、既存のProductRepositoryの機能を引き継いだRepositoryとして利用できます。
今回はこちらのクラスに、新しいメソッドfindProductsByTagId($tagId)を定義し、Controller側で定義していたクエリを移行しました。
あとは、Controller側でこのメソッドを呼び出せばいいですね。
Controllerで移行したクエリを利用
続いて、以下コードを記したControllerファイルを「Customize/Controller」下にアップします。
<?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)
    { 
        $products = $this->customProductRepository->findProductsByTagId(4);
        return [
            'Products' => $products,
        ];
    }
}前回はuseステートメントでProductRepositoryを利用していましたが、今回はSTEP 1で作成したCustomProductRepositoryを利用します。
また、indexメソッドに定義していたクエリはリポジトリに移行したので削除し、$TagProducts =$this->customProductRepository->findProductsByTagId(4);としてCustomProductRepositoryに定義したメソッドを呼び出します。
メソッドの引数には抽出したいタグのIDを設定します。本コードでは、idが4のタグをもつ商品を取得するようになっています。
リポジトリのカスタマイズ および クエリの移行はこれで完了です。あとは、前回記事 や こちらの記事 を参考に、Twigテンプレートも用意してページを作ってみてください!
メソッドをリポジトリに移行するメリットのまとめ
- 再利用性
- 
同じクエリを、他のコントローラやサービスでも使用できるようになる。 今回の例では、メソッドを呼び出す際に引数のIDを変えるだけで、任意のタグが付いた商品をピックアップして表示できるようにもなっています。 
- テストの容易性
- 
リポジトリのメソッドを、単体でテストしやすくなる。 
- コードの整理
- 
コントローラは、HTTPリクエストとレスポンスのハンドリングがメイン、リポジトリはデータの取得や保存がメインとなり、各ファイルの責務が明確になる。 
- メンテナンス性
- 
仮にデータベースのスキーマやクエリのロジックが変更された場合、リポジトリの中だけで変更を行うことができ、他の部分のコードは変更不要となる。 
デフォルトのリポジトリを拡張するメリットのまとめ
- 継承した親クラスのメソッドをすべてそのまま使える。
- 独自メソッドを追加できる。
- 必要に応じて、親クラスのメソッドをオーバーライド(上書き)できる。

