CakePHPで1つの語句や複数の語句で検索機能を実装する方法

CakePHPで1つの語句や複数の語句で検索機能を実装する方法

112 回閲覧されました

みなさんこんにちは、jonioです。

今回はCakePHPで1つの語句か複数の語句を含むカラムの値を検索する方法を解説します。

カラムはtitleとcontentを使います。

バージョン

私は4.5.4で確認しましたが3系・4系のどちらでも機能すると思います。

アクション

コードを下記にします。

public function action()
{
  $all_posts = $this->Posts->find();

  $keywords = $this->request->getQuery('keywords');
  
  $and_ok_keyword = $keywords;

  if (!empty($keywords)) {
    $keywords = explode(' ', $keywords);

    $conditions = [];
    
    foreach ($keywords as $keyword) {
      $conditions = [
        'OR' => [
          'Posts.title LIKE' => '%' . $keyword . '%',
          'Posts.content LIKE' => '%' . $keyword . '%',
        ]
      ];
    }
    
    $all_posts->where(['AND' => $conditions]);
  }

  $all_posts = $this->paginate($all_posts);

  $this->set(compact('all_posts', 'and_ok_keyword'));
}

17行目の「title」・18行目の「content」はカラム名です。

多分7行目が引っかかると思うのですが7行目がないと複数の語句で検索した時に検索結果の画面で検索フォームの中に表示される文字は検索キーワードの最後の文字になります。

例えば「デモ」・「タイトル」で検索すると検索結果のフォームは下記の表示になります。

7行目があることで検索キーワードを全て表示することができます。

ビュー

コードを下記にします。

<div class="posts index content">
    <?= $this->Form->create(null, ['type' => 'get']) ?>
    <?= $this->Form->control('keywords', ['label' => 'Search', 'value' => $and_ok_keyword]) ?>
    <?= $this->Form->button(__('Search')) ?>
    <?= $this->Form->end() ?>

    <h3><?= __('Posts') ?></h3>
    <table>
        <tbody>
            <?php foreach ($all_posts as $all_post):?>
                <div>
                    タイトル : <?= $all_post->title ?>
                </div>
                <div>
                    本文 : <?= $all_post->content ?>
                </div>
                <hr>
            <?php endforeach?>
        </tbody>
    </table>
    <div class="paginator">
        <ul class="pagination">
            <?= $this->Paginator->first('<< ' . __('first')) ?>
            <?= $this->Paginator->prev('< ' . __('previous')) ?>
            <?= $this->Paginator->numbers() ?>
            <?= $this->Paginator->next(__('next') . ' >') ?>
            <?= $this->Paginator->last(__('last') . ' >>') ?>
        </ul>
        <p><?= $this->Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?></p>
    </div>
</div>

これでひとまず検索機能はできています。

欠点1

今のままでは複数語句で検索した時に語句ご語句の間のスペースは半角の時しかうまくいきません。

全角スペースの場合も考えられるのでコードを修正します。

アクションのコードを下記にします。

public function action()
{
  $all_posts = $this->Posts->find();

  $keywords = $this->request->getQuery('keywords');
  
  $and_ok_keyword = $keywords;

  if (!empty($keywords)) {
    $keywords = str_replace('  ', '', $keywords);     //この行を追加
    
    $keywords = explode(' ', $keywords);

    $conditions = [];
    
    foreach ($keywords as $keyword) {
      $conditions = [
        'OR' => [
          'Posts.title LIKE' => '%' . $keyword . '%',
          'Posts.content LIKE' => '%' . $keyword . '%',
        ]
      ];
    }
    
    $all_posts->where(['AND' => $conditions]);
  }

  $all_posts = $this->paginate($all_posts);

  $this->set(compact('all_posts', 'and_ok_keyword'));
}

10行目の第一引数のスペースは全角にして第二引数のスペースは半角にします。

これで全角スペースの時にも対応できます。

欠点2

今のままではカタカナの語句に対してひらがなで検索してもひっかかりません。

それができるようにする為にコードを下記にします。

public function action()
{
  $all_posts = $this->Posts->find();

  $keywords = $this->request->getQuery('keywords');
  
  $and_ok_keyword = $keywords;

  if (!empty($keywords)) {
    $keywords = str_replace('  ', '', $keywords);
    
    $keywords = explode(' ', $keywords);

    $conditions = [];
    
    foreach ($keywords as $keyword) {
    
      $katakana_keyword = mb_convert_kana($keyword, 'C');     //この行を追加
    
      $conditions = [
        'OR' => [
          'Posts.title LIKE' => '%' . $keyword . '%',
          'Posts.content LIKE' => '%' . $keyword . '%',
          
          
          //ここから追加
          'Posts.title LIKE' => '%' . $katakana_keyword . '%',
          'Posts.content LIKE' => '%' . $katakana_keyword . '%',
          //ここまで追加
          
          
        ]
      ];
    }
    
    $all_posts->where(['AND' => $conditions]);
  }

  $all_posts = $this->paginate($all_posts);

  $this->set(compact('all_posts', 'and_ok_keyword'));
}

18行目の「mb_convert_kana」関数でカタカナをひらがなに変換できます、「C」はオプションです。

オプションに関してはリファレンスを見ると分かります。

ひらがなとカタカナの組み合わせで検索しても引っ掛かるようになります。

これで完成です。