前回作った受け取った質問一覧ページは未回答のものも回答済みのものも表示されていて未回答のものを探しにくいため、未回答の質問を絞り込む機能を作成します。
まずはアクション内の絞り込みの部分を調整します。URLにno_answer=1
というパラメータが付いた場合だけ未回答の絞り込みを行うようにします。
検索条件は質問に紐づく回答が存在するかになります。SQLでいうとquestionsに紐づくanswersが存在しないもの、ということになります。具体的にはLaravelのQuestionControllerのindexメソッドを下記のように変更しました。
public function index(Request $request)
{
$query = Question::with('answer')
->where('received_user_id', Auth::id())
->orderByDesc('id');
if ($request->input('no_answer')) {
$query->whereDoesntHave('Answer');
}
$questions = $query->paginate();
return view('question.index', compact('questions'));
}
これで例えばhttp://localhost:8000/questions?no_answer=1
のようなURLにアクセスすると未回答のものだけを表示できるようになりました。
前回Debugbarを導入して、実際に発行されているSQLを簡単に見ることができるようになったため見てみると、下記のようなSQLが発行されるようになりました(整形及び一部省略しています)
select
*
from
`questions`
where
`received_user_id` = 1
and not exists(
select
*
from
`answers`
where
`questions`.`id` = `answers`.`question_id`
)
order by
`id` desc
このままだと、ページ分けされた際に別ページのリンクをクリックした際にno_answer=1
のパラメータが解除されてしまいます。そのため、パラメータを維持できるようにページネーションの表示を下記のように変更します。
{{ $questions->appends(request()->only(['no_answer']))->links() }}
このようにlinksメソッドを呼ぶ前にappendsを挟んで追加パラメータを指定することで、ページネーションのリンクにも追加のパラメータが維持されるようになります。
ちなみに$query->paginate(2)
の様に表示されるデータより少ない数のlimitを指定すればデータを増やさなくても確認できます。
あとは実際にno_answer付きと無しのURLを切り替えられるようにしておきます。テンプレートに下記のような記述を追記しました。
<nav class="nav justify-content-center m-3">
<a class="nav-link @if (!request()->input('no_answer')) active @endif" href="{{ url('questions') }}">
すべて
</a>
<a class="nav-link @if (request()->input('no_answer')) active @endif" href="{{ url('questions?no_answer=1') }}">
未回答のみ
</a>
</nav>
Bootstrapの下記のコンポーネントを利用しています。
ちなみにこの横並びの表記にはFlexboxが利用されています。センタリングしておきたかったのでnav
のクラスにjustify-content-center
を追加しています。
Justify content - Flex · Bootstrap
これで一通り対応はできました。ただしこのサービスの場合、質問と回答のデータはメインになる、且つ一番増える可能性が高いため、上記で実装したようにリレーションを利用しての検索は後々重くなりすぎて問題になる可能性があります。そのため一旦ちょっと軽量化のための対策を行います。
動作としてはすでに出来ており、必須ではありませんので読み飛ばしても問題ありません。
具体的な対応方法として、リレーションして判断させるのではなく、回答がついた際に質問データ側に回答済みフラグを付けておくようにします。そのようにすることで質問データのみを使ったSQLで機能を実現できるようになります。
まずはその回答済みフラグをquestionsテーブルに追加するためのマイグレーションを追加します。
php artisan make:migration add_has_answer_to_questions --table questions
下記のようなマイグレーションを書きます。has_answerというカラムを追加します。
public function up()
{
Schema::table('questions', function (Blueprint $table) {
$table->boolean('has_answer')->default(false)->after('body');
});
}
public function down()
{
Schema::table('questions', function (Blueprint $table) {
$table->dropColumn('has_answer');
});
}
これを実行すればDB側の調整は完了です。あとは実際の処理を入れていきます。
AnswerControllerのstoreアクションの回答データ登録後に下記の処理を追加しておきます。
$question->has_answer = true;
$question->save();
問題なければ回答時にhas_answerフラグがオンになります。(※answersテーブルの作成マイグレーションの外部キー作成が間違っていたので修正済みです。質問に回答する機能を作る)
先程作成した回答済みのみを抽出するSQLをフラグを使ったシンプルな形に変更します。さきほど実装したwhereDoesntHaveを変更します。
if ($request->input('no_answer')) {
$query->where('has_answer', true);
}
これで一通り完了です。動作確認しておきましょう。クエリも下記のようなシンプルな形になりました。
select * from `questions` where `received_user_id` = 1 and `has_answer` = 1 order by `id` desc
これで未回答のものだけで絞り込みができるようになりました。
次はナビゲーションなどがまだ未整備で使いづらいためそのあたりを調整していきたいと思います。
第5回 | 質問に回答する機能を作る |
第6回 | Twitterシェア用のOGPを作成する |
第7回 | 受け取った質問一覧ページを作る |
第8回 | LaravelのDebugbarを導入する |
第9回 | 未回答のものを絞り込む機能を追加する |
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント