EC-CUBE4 リポジトリのカスタマイズ 使い方を調べてみた

#リポジトリのカスタマイズって？

[リポジトリカスタムのドキュメント](https://doc4.ec-cube.net/customize_repository)

ドキュメントにある程度説明がありますが、ぶっちゃけよくわからんです。
わからんので、使っていなかったのですが、頑張って使ってみると意外に簡単。

実装例が１つしかなく、難しく感じたので使い方を調べてみました。

ここでカスタム出来るリポジトリは基本的に検索系だけですが、EccubeEventsを使ってのカスタムは非推奨なので、出来るだけこのカスタム方法を使いましょう。

#カスタム出来るリポジトリクラス
（リストはドキュメントから）

|リポジトリクラス|QueryKey|
|:--|:--|
|ProductRepository::getQueryBuilderBySearchData()|QueryKey::PRODUCT_SEARCH|
|ProductRepository::getQueryBuilderBySearchDataForAdmin()|QueryKey::PRODUCT_SEARCH_ADMIN|
|ProductRepository::getFavoriteProductQueryBuilderByCustomer()|QueryKey::PRODUCT_GET_FAVORITE|
|CustomerRepository::getQueryBuilderBySearchData()|QueryKey::CUSTOMER_SEARCH|
|OrderRepository::getQueryBuilderBySearchData()|QueryKey::ORDER_SEARCH|
|OrderRepository.getQueryBuilderBySearchDataForAdmin()|QueryKey::ORDER_SEARCH_ADMIN|
|OrderRepository::getQueryBuilderByCustomer()|QueryKey::ORDER_SEARCH_BY_CUSTOMER|


**利用画面**

|QueryKey|利用画面|
|:--|:--|
|QueryKey::PRODUCT_SEARCH|フロント：商品一覧|
|QueryKey::PRODUCT_SEARCH_ADMIN|管理画面：商品一覧、受注登録・編集の商品追加の商品検索|
|QueryKey::PRODUCT_GET_FAVORITE|**関数が無い。**|
|QueryKey::CUSTOMER_SEARCH|管理画面：会員一覧、受注登録・編集の会員検索|
|QueryKey::ORDER_SEARCH|**関数が無い。**|
|QueryKey::ORDER_SEARCH_ADMIN|管理画面：受注一覧|
|QueryKey::ORDER_SEARCH_BY_CUSTOMER|フロント：マイページ・購入一覧|

`２つ関数が無いものがありました。。。`

#カスタマイズするためのインターフェイス

（リストはドキュメントから）

|インターフェイス/クラス|概要|
|:-----------------|:------------------|
|QueryCustomizer|QueryBuilderを自由に変更|
|OrderByCustomizer|ソート順を変更する|
|WhereCustomizer|検索条件を追加する|
|JoinCustomizer|結合するテーブルを追加する|


カスタマイズするために、上記から必要な物を利用します。
自分的には、QueryBuilderを自由に変更できるQueryCustomizerを覚えておけば良いのではないかと思ってます。

ではでは、使い方を見ていきましょう。

#カスタマイズ例
##OrderByCustomizer（ソート順を変更する）

OrderByCustomizerはソート順だけ変更できます。
orderBy、複数設定した場合は、２つ目以降はaddOrderByになります。

###実装例
（実装例はドキュメントから：常に商品IDでソートするサンプル）

```php

<?php

namespace Customize\Repository;

use Eccube\Doctrine\Query\OrderByClause;
use Eccube\Doctrine\Query\OrderByCustomizer;
use Eccube\Repository\QueryKey;

class AdminProductListCustomizer extends OrderByCustomizer
{
    /**
     * 常に商品IDでソートする。
     *
     * @param array $params
     * @param $queryKey
     * @return OrderByClause[]
     */
    protected function createStatements($params, $queryKey)
    {
        return [new OrderByClause('p.id')];
    }

    /**
     * ProductRepository::getQueryBuilderBySearchDataForAdmin に適用する.
     *
     * @return string
     * @see \Eccube\Repository\ProductRepository::getQueryBuilderBySearchDataForAdmin()
     * @see QueryKey
     */
    public function getQueryKey()
    {
        return QueryKey::PRODUCT_SEARCH_ADMIN;
    }
}
```

###降順にする
降順にしたい場合は、OrderByClauseの第二引数に「desc」を設定しましょう。

```php

<?php

protected function createStatements($params, $queryKey)
{
    return [new OrderByClause('p.id','desc')];
}
```

###複数設定
複数設定する場合は「new OrderByClause」を追加してやればOKです。
意味ないですが、idでソートした後に、商品名でソートする場合。

```php

<?php

protected function createStatements($params, $queryKey)
{
    return [
        new OrderByClause('p.id'),
        new OrderByClause('p.name'),
    ];
}
```

以上です。

##WhereCustomizer（検索条件を追加）

条件はすべてandWhereされます。

###実装例
（実装例：常に商品名が商品の物だけ表示する）
これやると商品名が商品の物以外の商品が表示されなくなります。あまりいい例題が思い浮かばない。。。

```php

<?php

namespace Customize\Repository;

use Eccube\Doctrine\Query\WhereClause;
use Eccube\Doctrine\Query\WhereCustomizer;
use Eccube\Repository\QueryKey;

class AdminProductListCustomizer extends WhereCustomizer
{
    /**
     * 常に商品名が商品の物だけ表示する
     *
     * @param array $params
     * @param $queryKey
     * @return WhereClause[]
     */
    protected function createStatements($params, $queryKey)
    {
        return [WhereClause::eq('p.name', ':name', ['name' => '商品'])];
    }

    /**
     * ProductRepository::getQueryBuilderBySearchDataForAdmin に適用する.
     *
     * @return string
     * @see \Eccube\Repository\ProductRepository::getQueryBuilderBySearchDataForAdmin()
     * @see QueryKey
     */
    public function getQueryKey()
    {
        return QueryKey::PRODUCT_SEARCH_ADMIN;
    }
}
```

###複数設定する
複数設定する場合は「WhereClause」を追加してやればOKです。

```php

<?php

protected function createStatements($params, $queryKey)
{
    return [
        WhereClause::eq('p.name', ':name', ['name' => 'hoge']),
        WhereClause::eq('p.name', ':name', ['name' => 'hogehoge']),
    ];
}
```

条件ごとの関数は以下になります。

###条件例
[参考：WhereClause.php](https://github.com/EC-CUBE/ec-cube/blob/4.0/src/Eccube/Doctrine/Query/WhereClause.php)

```php

//=
WhereClause::eq('name', ':Name', 'hoge')
WhereClause::eq('name', ':Name', ['Name' => 'hoge'])

//<>
WhereClause::neq('name', ':Name', 'hoge')
WhereClause::neq('name', ':Name', ['Name' => 'hoge'])

//IS NULL
WhereClause::isNull('name')

//IS NOT NULL
WhereClause::isNotNull('name')

//LIKE
WhereClause::like('name', ':Name', '%hoge')
WhereClause::like('name', ':Name', ['Name' => '%hoge'])

//NOT LIKE
WhereClause::notLike('name', ':Name', '%hoge')
WhereClause::notLike('name', ':Name', ['Name' => '%hoge'])

//IN
WhereClause::in('name', ':Names', ['foo', 'bar'])
WhereClause::in('name', ':Names', ['Names' => ['foo', 'bar']])

//NOT IN
WhereClause::notIn('name', ':Names', ['foo', 'bar'])
WhereClause::notIn('name', ':Names', ['Names' => ['foo', 'bar']])

//BETWEEN
WhereClause::between('price', ':PriceMin', ':PriceMax', [1000, 2000])
WhereClause::between('price', ':PriceMin', ':PriceMax', ['PriceMin' => 1000, 'PriceMax' => 2000])

//>
WhereClause::gt('price', ':Price', 1000)
WhereClause::gt('price', ':Price', ['Price' => 1000])

//>=
WhereClause::gte('price', ':Price', 1000)
WhereClause::gte('price', ':Price', ['Price' => 1000])

//<
WhereClause::lt('price', ':Price', 1000)
WhereClause::lt('price', ':Price', ['Price' => 1000])

//<=
WhereClause::lte('price', ':Price', 1000)
WhereClause::lte('price', ':Price', ['Price' => 1000])

```

以上です。

##JoinCustomizer(結合するテーブルを追加する）
[参考：JoinClause.php](https://github.com/EC-CUBE/ec-cube/blob/4.0/src/Eccube/Doctrine/Query/JoinClause.php)

###実装例
（実装例：常に商品に代理店情報を接続して代理店名で検索する。）

前提として、EntityにAgency(代理店情報)を追加カスタムしている事を想定します。
ProductTraitにManyToOneでAgencyとつなげてると想定します。
FromTypeに「agency」を追加していると想定します。


```php

<?php

namespace Customize\Repository;

use Eccube\Doctrine\Query\JoinClause;
use Eccube\Doctrine\Query\WhereClause;
use Eccube\Doctrine\Query\JoinCustomizer;
use Eccube\Repository\QueryKey;

class AdminProductListCustomizer extends JoinCustomizer
{
    /**
     * 常に商品に代理店情報を接続して代理店名で検索する。
     *
     * @param array $params
     * @param $queryKey
     * @return JoinClause[]
     */
    protected function createStatements($params, $queryKey)
    {
        $Obj = JoinClause::leftJoin('p.Agency', 'a');
        $Obj->addWhere(WhereClause::eq('a.name', ':agency_name', ['agency_name' => $params['agency']]));
        return [$Obj];
    }

    /**
     * ProductRepository::getQueryBuilderBySearchDataForAdmin に適用する.
     *
     * @return string
     * @see \Eccube\Repository\ProductRepository::getQueryBuilderBySearchDataForAdmin()
     * @see QueryKey
     */
    public function getQueryKey()
    {
        return QueryKey::PRODUCT_SEARCH_ADMIN;
    }
}
```

###Joinする
まずは、「innerJoin」「leftJoin」で「JoinClause」のオブジェクトが返ってきます。

```php

$Obj = JoinClause::innerJoin($join, $alias);
$Obj = JoinClause::leftJoin($join, $alias);
```

###条件とソート
「addWhere」に「WhereClause」のオブジェクトを、「addOrderBy」に「OrderByClause」のオブジェクトを渡してやることによって、検索条件やソート条件を追加できます。

```php

use Eccube\Doctrine\Query\OrderByClause;
use Eccube\Doctrine\Query\WhereClause;

$Obj->addWhere(WhereClause::eq('name', ':Name', ['Name' => 'hoge']));
$Obj->addOrderBy(new OrderByClause('p.id'));
```

「createStatements」で複数のオブジェクトを配列れreturnすれば、複数のjoinができます。

以上。

##QueryCustomizer（QueryBuilderを自由に変更）

[EC-CUBE4のQueryCustomizerでOR条件を追加したい場合](https://qiita.com/okazy/items/db33c600450567adedba)

こちらの記事をどうぞー！！！

以上。

#最後に

実装例が良くないので、理解するまで時間かかるかもですが、参考なれば幸いです。

ここが分かり難いぞーなどあればコメントください。