【EC-CUBE4.3】全角カタカナでLIKE検索すると半角カタカナの濁点・半濁点がヒットしない

## はじめに
保守しているECサイトで、商品検索の際に全角カタカナで検索すると、半角カタカナの商品が引っかからないという要望があった。（その逆も然り）

ググってみると、参考になる記事があったためそちらを参考にChatGPTの力を借りて修正してみる。
参考サイト：https://dodosu.hatenablog.jp/entry/2021/01/26/125325

## とりあえずやってみる
とりあえず、
```
SELECT * FROM skill WHERE(REPLACE(REPLACE(bigName, 'ﾞ', ''), 'ﾟ', '') collate utf8_unicode_ci LIKE REPLACE(REPLACE(?0, 'ﾞ', ''), 'ﾟ', ''))
```
のようなことは、できるのかと聞いてみると`Doctrine`では、`REPLACE`は標準では使えないよう。

`カスタムDQL関数`なるものを定義すればできるとのことで指示に従ってやってみる。

### 1. `app/Customize/Doctrine/ORM/Query/Replace.php` を作成
```php
namespace Customize\Doctrine\ORM\Query;

use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;

class Replace extends FunctionNode
{
    public $stringPrimary;
    public $stringFrom;
    public $stringTo;

    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);     // REPLACE
        $parser->match(Lexer::T_OPEN_PARENTHESIS); // (
        $this->stringPrimary = $parser->StringPrimary();
        $parser->match(Lexer::T_COMMA);           // ,
        $this->stringFrom = $parser->StringPrimary();
        $parser->match(Lexer::T_COMMA);           // ,
        $this->stringTo = $parser->StringPrimary();
        $parser->match(Lexer::T_CLOSE_PARENTHESIS); // )
    }

    public function getSql(SqlWalker $sqlWalker)
    {
        return sprintf(
            'REPLACE(%s, %s, %s)',
            $this->stringPrimary->dispatch($sqlWalker),
            $this->stringFrom->dispatch($sqlWalker),
            $this->stringTo->dispatch($sqlWalker)
        );
    }
}
```
ほぼ、ChatGPTが提案してくれたものそのまま。

### 2. `doctrine.yaml` にカスタムDQL関数なるものを登録
```php
    orm:
        auto_generate_proxy_classes: '%kernel.debug%'
        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true
        dql:
            string_functions:
                NORMALIZE: Eccube\Doctrine\ORM\Query\Normalize
                REPLACE: Customize\Doctrine\ORM\Query\Replace # ←ここ
            numeric_functions:
                EXTRACT: Eccube\Doctrine\ORM\Query\Extract

```

### 3. 該当箇所のコードを修正
`src/Eccube/Repository/ProductRepository.php`を編集する。
↑ここに、商品検索のクエリを作成しているメソッドがある。
※ 本来は、app/Customize配下に継承するなりして作る。

```php
    public function getQueryBuilderBySearchData($searchData)
    {
        $qb = $this->createQueryBuilder('p')
            ->andWhere('p.Status = 1');

        // category
        $categoryJoin = false;
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
            $Categories = $searchData['category_id']->getSelfAndDescendants();
            if ($Categories) {
                $qb
                    ->innerJoin('p.ProductCategories', 'pct')
                    ->innerJoin('pct.Category', 'c')
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
                    ->setParameter('Categories', $Categories);
                $categoryJoin = true;
            }
        }

        // name
        if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
            $keywords = preg_split('/[\s　]+/u', str_replace(['%', '_'], ['\\%', '\\_'], $searchData['name']), -1, PREG_SPLIT_NO_EMPTY);

            foreach ($keywords as $index => $keyword) {
                $key = sprintf('keyword%s', $index);
                $qb
                    ->andWhere(sprintf( // ↓ココ
                        'REPLACE(REPLACE(NORMALIZE(p.name), \'ﾞ\', \'\'), \'ﾟ\', \'\') LIKE REPLACE(REPLACE(NORMALIZE(:%s), \'ﾞ\', \'\'), \'ﾟ\', \'\') OR
                        NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR
                        EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s))',
                        $key,
                        $key,
                        $index,
                        $index,
                        $index,
                        $index,
                        $key
                    ))
                    ->setParameter($key, '%' . $keyword . '%');
            }
        }

```
これで、問題なく検索ができるようになった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3850968/d390db93-34e5-4976-abd2-d0286c6bf3c8.png)