【EC-CUBE 4】商品データベースの複雑な検索方法

【トップ画像】データベースを複雑な条件で検索できるexprクラスの紹介
「findBy」より複雑な条件で、商品を検索するには?

Repositoryに独自のメソッドを追加し、exprメソッドを使ってレコードを取り出してみよう!

データベースから欲しい情報(レコード)を取り出すRepositoryには、「find」や「findBy」といった標準機能が備わっています。以下記事で紹介したとおり、特に「findBy」は使い勝手がよく、これで十分な場合も多々あります。

ただ、検索条件を複数設定したり、あいまい検索(キーワードを含むものすべてを検索)したりというような場合には、さすがに「findBy」だけでは使い勝手が悪くなってしまいます。

そこで本記事では、Repositoryを拡張(オリジナルメソッドを作成)し、より細かな条件でレコードを取り出す方法を紹介します。

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

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

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

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

目次

まずはRepositoryを新規に作成する

EC-CUBE(Symfony)でデータベースから特定のレコードを取り出すには、取り出したいレコードが保存されているテーブルに対応したRepositoryを使います。

商品テーブルのようなデフォルトで用意されているRepositoryは「src/Eccube/Repository」下にまとめて保存されていますが(商品テーブル ⇒ ProductRepository.php)、src下のファイルには直接修正等加えない方がよいので、「app/Customize/Repository」下に以下のようなRepositoryファイルを新規作成します。

<?php

namespace Customize\Repository;

use Eccube\Entity\Product;
use Eccube\Repository\AbstractRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;

/**
 * CustomProductRepository
 */
class CustomProductRepository extends AbstractRepository
{
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, Product::class);
    }
}

ベースとなるシンプルなRepositoryファイルです。
(こちらは商品テーブルに紐づくRepositoryです。必要に応じてEntityなどを変更してください。)

ここで作成したCustomProductRepositoryクラスに、オリジナルの検索条件をもつ新しいメソッドを追加していきます。

Exprクラスを使って複雑な検索条件を設定する

QueryBuilderの「expr」というメソッドを呼び出すことで、検索の条件となる式を作成できます。
といってもピンとこないかと思いますので、以下の例を用いて解説します。

Exprクラスの基本的な使い方

下記コードは、引数に「商品名」を指定するとそれに合致したレコードを返してくれるメソッドです。

public function findByName($value)
{
  $builder = $this->createQueryBuilder('p');
  return $builder
  ->where($builder->expr()->eq('p.name', '?1'))
  ->setParameter(1, $value)
  ->getQuery()
  ->getResult();
}

$builder = $this->createQueryBuilder('p');

まずQueryBuilderインスタンスを作成し、変数$builderに代入しておきます。
引数には「’p’」が指定されており、これ以降のメソッドで使用するテーブルを「p」として指定できるようになります。

->where($builder->expr()->eq('p.name', '?1'))

続いて、「where」の引数に条件を指定します。
ここでは、exprメソッドがもつ「eq」メソッドを利用しています。これは「=」の式を追加するメソッドで、第一引数に対象となるフィールドを、第二引数に値を指定します。

すなわちこの検索条件は、「nameフィールド(商品名)= ?1」となります。

->setParameter(1, $value)

「setParameter」で、第二引数の値を第一引数にセットします。

$valueはこのメソッド(findByName)の引数なので、このメソッドをControllerで呼び出すときに指定した引数が「?1」となります。

->getQuery()
->getResult();

最後に「getQuery」メソッドでQueryクラスのインスタンスを取得し、その実行結果を「getResult」で取得します。


慣れないとちょっとややこしいかもしれませんが、この型さえ掴めれば、あとは検索したい条件に応じたexprのメソッドを呼び出すだけです。(exprがもつ代表的なメソッドは次項の通りです。)

Exprに用意されている主なメソッド一覧

前項の「->where($builder->expr()」に続くメソッドを以下の通り変えることで、色々な条件で検索できるようになります。

値が等しい

eq ( フィールド名, 値 )

値が異なる

neq ( フィールド名, 値 )

値より小さい

lt ( フィールド名, 値)

値以下

lte ( フィールド名, 値)

値より大きい

gt ( フィールド名, 値)

値以上

gte ( フィールド名, 値)

あいまい検索

like ( フィールド名, 値)

複数検索

in ( フィールド名, 配列)

あいまい検索

Exprのlikeメソッドを使うと「あいまい検索(キーワードの一部を含む場合に取得)」ができます。

public function findByLikeName($value)
{
  $builder = $this->createQueryBuilder('p');
  return $builder
  ->where($builder->expr()->like('p.name', '?1'))
  ->setParameter(1, '%' . $value . '%')
  ->getQuery()
  ->getResult();
}

setParameterに値をセットする方法が少し特殊です。

「%」はワイルドカードと呼ばれ、ここにはどのような文字(1字でも複数でも可)も当てはめることができます。この例では「%$value%」となり、メソッドの引数に指定した値が含まれるすべてのレコードを検索し、取得できます。

複数検索

Exprのinメソッドを使うと「複数検索」ができます。

public function findByNames($values)
{
  $builder = $this->createQueryBuilder('p');
  return $builder
  ->where($builder->expr()->in('p.name', $values))
  ->getQuery()
  ->getResult();
}

setParameterがなく、引数$values(配列)をそのままinメソッドの第二引数に指定します。
こうすることで、配列の値すべてが検索対象となります。

条件を複数設定する(AND / OR)

「andWhere」または「orWhere」を用いることで、条件を複数追加できます。

andWhere

設定した条件すべてに当てはまるもののみを検索します(AND検索)。

where( 条件, 値 ) -> andWhere( 条件, 値 )

whereに続けてandWhereで同じように条件を設定します。andWhereは複数追加してもOKです。

以下は、「第一引数以上のID かつ 第二引数以下のID をもつ商品レコード」を検索するメソッドです。

public function findByIDs($value1, $value2)
{
  $builder = $this->createQueryBuilder('p');
  return $builder
  ->where($builder->expr()->gte('p.id', '?1'))
  ->andWhere($builder->expr()->lte('p.id', '?2'))
  ->setParameters([1 => $value1, 2 => $value2])
  ->getQuery()
  ->getResult();
}

値を複数セットする場合はsetParameter’s’を用い、配列で値を指定します。

orWhere

設定した条件のいずれかに当てはまるものを検索します(OR検索)。

where( 条件, 値 ) -> orWhere( 条件, 値 )

whereに続けてandWhereで同じように条件を設定します。orWhereは複数追加してもOKです。(AND検索と同じです。)

以下は、「第一引数の値に合致するID または 第二引数の値に合致する名前 をもつ商品レコードを検索する」メソッドです。

public function findByIDOrName($value1, $value2)
{
  $builder = $this->createQueryBuilder('p');
  return $builder
  ->where($builder->expr()->eq('p.id', '?1'))
  ->orWhere($builder->expr()->eq('p.name', '?2'))
  ->setParameters([1 => $value1, 2 => $value2])
  ->getQuery()
  ->getResult();
}

値を複数セットする場合はsetParameter’s’を用い、配列で値を指定します。

レコードの取得順を変える(orderBy)

「orderBy」を用いることで、レコードの並び順を昇順(ASC)か降順(DESC)に指定できます。

orderBy( フィールド名, 並び順 ) -> addOrderBy( フィールド名, 並び順 )

第一引数に基準となるフィールド名、第二引数に並び順(ASC / DESC)を指定します。
また、並び順を追加したい場合はaddOrderByを使います。

以下は、「商品レコードをすべて取得し、IDの大きい順(降順)で並べ替える」というメソッドです。

public function findAllwithDESC()
{
  $builder = $this->createQueryBuilder('p');
  return $builder
  ->orderBy('p.id', 'DESC')
  ->getQuery()
  ->getResult();
}

レコードの取得範囲を変える(setFirst / setMax)

「setFirstResult」でレコードの取得開始位置を、「setMaxResult」で取得する最大レコード数を指定できます。

setFirstResult( 整数 ) -> setMaxResult( 整数 )

setFirstResultの第一引数に0を指定すると1番目、1を指定すると2番目のレコードから取得されます。
setMaxResultは、指定された値の数だけレコードを取得します。(足りない場合はその数まで。)

以下は、「商品レコードをすべて取得し、2番目から4番目のレコード(計3つ)を検索する」メソッドです。

public function findSome()
{
  $builder = $this->createQueryBuilder('p');
  return $builder
  ->setFirstResult(1)
  ->setMaxResults(3)
  ->getQuery()
  ->getResult();
}

取得するレコードの数が膨大になる場合、負荷を低減するためにこのメソッドを使うと良いでしょう。

まとめ

以上、データベースからレコードを取り出すための条件設定について、Exprの使い方を中心に解説しました。

実はExprを使わずとも、条件式をテキストで用意したり「DQL」と呼ばれる方法を利用したりすれば同じようにレコードを検索できます。が、SQLに慣れていない方にはちょっと難しいので、まずはEC-CUBE(Symfony)独特の機能であるExprを、ぜひ使ってみていただければと思います!

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