質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

タグ

特殊な記法により文書に埋め込む形で記述される付加情報をタグと呼びます。文書構造や書式、文字飾りなどを指示したり、画像や他の文書へのリンクを埋め込むことができる。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

2回答

3470閲覧

【Laravel6】複数タグ検索機能を実装できない

gyu

総合スコア3

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

タグ

特殊な記法により文書に埋め込む形で記述される付加情報をタグと呼びます。文書構造や書式、文字飾りなどを指示したり、画像や他の文書へのリンクを埋め込むことができる。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2020/10/04 07:33

編集2020/10/04 08:27

前提・実現したいこと

複数タグ検索機能を実装したい。

検索フォームで "#"を先頭に持つキーワードをタグと認識し、検索。
2つ以上タグがあったとしても絞り込みで検索できるようにしたい。

https://tic40.hatenablog.com/entry/2017/02/25/135528
上記の記事を参考に、下記のMySQL文で複数タグ検索が可能とわかった。

SELECT * FROM posts JOIN post_tags ON posts.id = post_tags.post_id JOIN tags ON post_tags.tag_id = tags.id WHERE tags.name IN ('aaa','bbb') GROUP BY posts.id HAVING COUNT(posts.id) = 2;

これを参考にしながら、Laravelのクエリビルダに変換して、実装したい。

イメージ説明

環境

Laravel6
DB MySQL

####モデル

postsテーブル

idtitlebody

post_tagsテーブル

post_idtag_id

tagsテーブル

idname

post.php

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class post extends Model { protected $fillable = [ 'title', 'body', 'user_id' ]; public function user() { return $this->belongsTo('App\User'); } public function tags() { return $this->belongsToMany('App\Models\Tag', 'post_tags'); } }

tag.php

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class tag extends Model { protected $fillable = [ 'name' ]; public function posts() { return $this->belongsToMany('App\Models\Post', 'post_tags'); } }

post_tag.php

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class post_tag extends Model { protected $fillable = [ 'post_id', 'tag_id' ]; }

実装の試み

まずは純粋にSQLで取得できる確認をした。

https://tic40.hatenablog.com/entry/2017/02/25/135528
上記の記事より下記のMySQL文で複数タグ検索が可能とわかった。

mysql> SELECT item.* -> FROM item -> JOIN item_to_tag AS itt ON item.id = itt.item_id -> JOIN tag ON itt.tag_id = tag.id -> WHERE tag.name IN ('tag4','tag5') -> GROUP BY item.id -> HAVING COUNT(item.id) = 2;

これを元に自分のDBに合わせて作成。

SELECT * FROM posts JOIN post_tags ON posts.id = post_tags.post_id JOIN tags ON post_tags.tag_id = tags.id WHERE tags.name IN ('aaa','bbb') GROUP BY posts.id HAVING COUNT(posts.id) = 2;

結果、SQL上では無事にデータを取得できた
イメージ説明

無事に取得できたので、これをクエリビルダに変換する

###クエリビルダに変換する

検索キーワードは下記の処理で、タグのみ取得します

準備:検索キーワードを取得

php

1 2$keyword = $request->input('search'); 3 4//全角スペースを半角スペースに変換 5$keyword_space_half = mb_convert_kana($keyword, 's'); 6 7//正規文を利用して、#を含むキーワードを取得 8preg_match_all('/#([a-zA-z0-90-9ぁ-んァ-ヶ亜-熙]+)/u', $keyword, $match); 9//->$match[0]で”#”ありの検索時のタグ名 10//->$match[1]が”#”なしタグの名前。 11 12 13//半角スペースでキーワードをsplitで分割 14$keywords = preg_split('/[\s]+/', $keyword_space_half); 15 16 17//$match[0]が#ありのキーワード -> これを削除して、純粋なキーワードだけにする。 18$no_tag_keywords = array_diff($keywords, $match[0]); 19 20//$match[1]が”#”なしのtagの名前 21$tags = $match[1]; 22

本題のクエリビルダを記述

先ほど定義した$tagsを使って、タグ検索する。

$query = DB::table('posts'); $query ->join('post_tags', 'posts.id', '=', 'post_tags.post_id') ->join('tags', 'post_tags.tag_id', '=', 'tags.id') ->whereIn('tags.name', $tags) ->groupBy('posts.id') ->having(count('posts.id'), '=', [count($tags)]);

下記の実行したいSQLとほぼ同じにしているはずです。

SELECT * FROM posts JOIN post_tags ON posts.id = post_tags.post_id JOIN tags ON post_tags.tag_id = tags.id WHERE tags.name IN ('tag1','tag2') GROUP BY posts.id HAVING COUNT(posts.id) = 2;

tag1tag2はタグ名の一例なので、タグのnameが入っている$tagsを入れています。
HAVING COUNT(posts.id) = 2;の2は、検索時のタグの数なので、count($tags)で値を出しています。

しかし、->having(count('posts.id'), '=', [count($tags)]);でエラーが発生します

エラー

イメージ説明

実際のエラー文

count(): Parameter must be an array or an object that implements Countable

日本語:パラメータは配列か Countable を実装したオブジェクトでなければなりません。

count()の中にarrayを入れるように指示があることから、配列でないことが問題だとわかります。
しかし、ここからどうすればよいかわかりません。

SQL文を再現したつもりでしたが、うまくいってません。
どのように記述するべきか、ご教授お願いします。
よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

diff

1$query = DB::table('posts'); 2$query 3 ->join('post_tags', 'posts.id', '=', 'post_tags.post_id') 4 ->join('tags', 'post_tags.tag_id', '=', 'tags.id') 5 ->whereIn('tags.name', $tags) 6 ->groupBy('posts.id') 7- ->having(count('posts.id'), '=', [count($tags)]); 8+ ->havingRaw('count(distinct tags.id)', '=', count($tags));

多分こんな感じだと思います。

投稿2020/10/04 08:19

phper.k

総合スコア3923

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gyu

2020/10/04 08:44

早速、ありがとうございます!! 実施したところ、 ```php Symfony\Component\Debug\Exception\FatalThrowableError Argument 2 passed to Illuminate\Database\Query\Builder::havingRaw() must be of the type array, string given, called in /Applications/MAMP/htdocs/qiita-laravel/app/Http/Controllers/PostController.php on line 45 ``` [![Image from Gyazo](https://i.gyazo.com/d55fd2fcc9b5b0288844da60e81f93ef.png)](https://gyazo.com/d55fd2fcc9b5b0288844da60e81f93ef) readouble.comで確認してみたところ、 https://readouble.com/laravel/6.x/ja/queries.html havingRawとorHavingRawメソッドは、文字列をhaving節の値として指定するために使用します。両メソッドは、第2引数にオプションとして、`バインドの配列`を渡すことができます。 ```php $orders = DB::table('orders') ->select('department', DB::raw('SUM(price) as total_sales')) ->groupBy('department') ->havingRaw('SUM(price) > ?', [2500]) ->get(); ``` と記載があり、 `->havingRaw('count(distinct tags.id)', '=', count($tags));` の第二引数'='がエラーになるようです。 [![Image from Gyazo](https://i.gyazo.com/d55fd2fcc9b5b0288844da60e81f93ef.png)](https://gyazo.com/d55fd2fcc9b5b0288844da60e81f93ef) そこで、少し変えてみました! ```php ->havingRaw('count(distinct tags.id) = ?', [count($tags)]); ``` これで出ました!! 本当にありがとうございます!! もし、よろしければ、なぜ、`posts.id`ではなく、`tag.id`に変更する必要があったのか、ご教授いただければ幸いです!
phper.k

2020/10/04 08:52

そうしないと取得する結果おかしくありませんか?
gyu

2020/10/04 08:56

確かにそうですね! 勉強になりました! ありがとうございます!
guest

0

PHP

1$tags_count = count($tags); 2 3 4 ->having(count('posts.id'), '=', $tags_count);

でよいのでは?

追記
TableSelect::having
こちらでの書き方

PHP

1$result = $table->select('count(*) as count', 'age') 2 ->groupBy('age')->orderBy('age asc') 3 ->having('count > 1') 4 ->execute();

の様に一度'count('posts.id') as count'の様にカウントに項目名を付けて

PHP

1 ->having('count', '=', $tags_count);

と書かれては?

投稿2020/10/04 07:56

編集2020/10/04 08:25
kuma_kuma_

総合スコア2506

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gyu

2020/10/04 08:15

速やかな対応に感謝致します!! 早速試したみたところ、 https://gyazo.com/34b0ed6aaabfc1288dd0c3c12af15fa2 同じエラーが表示されました ``` count(): Parameter must be an array or an object that implements Countable ``` これで count('posts.id')に原因がある可能性が高いかもしれません。
gyu

2020/10/04 08:32

追記文、試みます!
gyu

2020/10/04 08:58

asでやる方法非常に参考になりました!! 私の実力不足で、どこでasで定義しようか思いつきませんでしたが、本当にありがとうございます! 次に活かします!
kuma_kuma_

2020/10/04 09:07

今回は項目名(別名)が表示上意味をなさない為、この書き方が最良ではありません。 回答もあえて遠回りな書き方をしています。(いいかえれば意地悪な) できればこのときに >having(count('posts.id'), '=', $tags_count); 「count('posts.id')」表記が文字列でなくなっている事に気が付いてほしかった。 これは私が同じ場面にあった際どうやってデバックするかを質問者様に追体験してもらいたかったからです。意地悪でもうしわけない。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問