BeginnerEngineerBlog
中の人
中の人

勝手にlaravelチュートリアル #7

公開: 2021-09-02 22:06
更新: 2023-04-06 15:02
3758
php laravel6.x 検索機能 eloquent
今回は投稿したブログの検索機能を追加します!キーワード検索、ソートの機能を実装します!

こんにちは!
前回はブログの一覧の表示、編集機能を実装しましたね!


今回はブログの検索とソートの機能を実装していきます!

検索やソート機能があると、言うまでもなくユーザービリティが向上して、ブログアプリの質もよくなります!

頑張っていきましょう!

  1. チュートリアル説明
  2. プログラミングの準備(エディタ、dockerの用意、環境構築)
  3. 認証機能を追加してログイン機能を実装する
  4. ブログのタイトルと記事を登録する
  5. ブログの記事へタグを登録する
  6. ブログの一覧表示、編集機能の実装
  7. ブログの検索機能の実装(今ここ)
  8. ユーザーへの通知機能の実装

さて、そしたら、まず最初にソート機能を実装していきましょう。


ソート機能を実装する


何を基準にソートするかですが、

  • 作成日
  • タイトルのあいうえお順

でソートしてみたいと思います。

では、まずはソートするためのボタンを設置しましょう。

list.blade.phpを以下のように編集してください。

@extends('layouts.app')
@section('content')
    //↓ここから
    <form action="{{route('article.list')}}">
        <button type="submit" name="sort" value="1">作成日順</button>
        <button type="submit" name="sort" value="">あいうえお順</button>
    </form>
    //↑ここまで追加
    @foreach ($articles as $article)
        <div style="border-bottom: solid 1px gray;">
            <div>
                user: {{$article->user->name}}
            </div>
            <div>
                title: <a href="{{route('article.show', ['id' => $article->id])}}">{{$article->title}}</a>
            </div>
            @if (Auth::check() && Auth::user()->id === $article->user_id)
                <a href="{{route('article.edit', ['id' => $article->id])}}">編集</a>
            @endif
        </div>
    @endforeach
@endsection

formタグのactionにブログ一覧画面へのルートを設定します。

<button type="submit" name="sort" value="1">作成日順</button>
<button type="submit" name="sort" value="">あいうえお順</button>

buttonタグはnameとvalueを設定できます。あいうえお順のvalueは一旦空にしておきましょう。
typeはボタンの属性を指定できます。今回は"submit"で、#4の1でrequestを送信するために指定した

<input type="submit">

とほぼ同じです。

ですが、buttonの場合valueを設定すると、押されたボタンのvalueをサーバーへ送信することができます。
ので、個人的にはsubmitはほとんどbuttonタグを使っています。

また、formタグはmethodを指定しないと、getのhttpリクエストを送信することができます。
で、getの場合@csrfの記述は不要です。

では一度ブログ一覧画面を確認してみましょう。


いい感じにボタンが出来上がりましたね!
これなら、ユーザーはこのボタンを押したら、ボタンに書かれた内容でソートできると直感でわかりますね!

それでは、このボタンをクリックしたらソートするように実装していきましょう。
まず作成日順からやっていきます。

ArticleControllerのlistアクションを以下のように編集してください。

public function list(Request $request)
{
    //↓ここから
    $sort = $request->get('sort');
    if ($sort) {
        if ($sort === '1') {
            $articles = Article::orderBy('created_at')->get();
        }
    } else {
        $articles = Article::all();
    }
    //↑ここまで編集


    return view(
        'article.list',
        [
            'articles' => $articles
        ]
    );
}

まず、$sortを取得します。
で、もし$sortがあったら(nullじゃなければ)、

if ($sort === '1') {

$sortが'1'の場合

$articles = Article::orderBy('created_at')->get();

$articlesへArticleモデルをcreated_atフィールドの過去から順に全てgetする処理をします。

orderByは、データの若い順にソートする書き方になります。参照するフィールドがintなどの場合、数字の若い順(1 -> 2 -> 3)でソートし、timestampなどの場合、過去から順(2020-01-01 -> 2020-01-02の順)になります。
ちなみに

orderBy('created_at', 'ASC')

これでも同じ意味になります。

このように取得するデータを操作するために記述する書き方は、eloquent(エロクエント)またはqueryBuilder(クエリビルダー)と言います。

モデルはeloquentに該当します。

eloquentもqueryBuilderもほぼ同じように記述するのですが、微妙?に違ったりして、私もちゃんと説明できません。
ただ、なんとなくですが、eloquentはmodelが主体で、queryBuilderはDBファサードが主体といった感じで私は認識しています。
とりあえず、取得するデータを操作したいときは、eloquentもしくはqueryBuilderということだけわかっていれば大丈夫です!
ということで気になった方は色々調べてみてください!

そしたら、このままではcreated_atでちゃんとソートできているか分かりづらいので、list.blade.phpを以下のように編集してください。

@extends('layouts.app')
@section('content')
    <form action="{{route('article.list')}}">
        <button type="submit" name="sort" value="1">作成日順</button>
        <button type="submit" name="sort" value="">あいうえお順</button>
    </form>
    @foreach ($articles as $article)
        <div style="border-bottom: solid 1px gray;">
            <div>
                user: {{$article->user->name}}
            </div>
            <div>
                title: <a href="{{route('article.show', ['id' => $article->id])}}">{{$article->title}}</a>
                <small>{{$article->created_at}}</small>//<-追加
            </div>
            @if (Auth::check() && Auth::user()->id === $article->user_id)
                <a href="{{route('article.edit', ['id' => $article->id])}}">編集</a>
            @endif
        </div>
    @endforeach
@endsection



<small>{{$article->created_at}}</small>

で、$articleが作成された時間を表示します。

では、一度ブログ一覧画面をリロードしてみましょう。


いい感じに作成日が表示されて分かりやすくなりましたね。

では、作成日順のボタンを押してみましょう。
あまり変化なかった方が大半かもしれませんが、日付を見て過去から順に並んでいればひとまずオッケーです!

それでは、今度は最近順でソートできるよう実装していきましょう。
ソートするタイミングは、過去から順でソート後にもう一度作成日順ボタンを押したら、最近順でソートできるようにします。
ようするに、作成日順ボタンを押すごとに、ソートの順番を変える仕様ということです。

ArticleControllerのlistアクションを以下のように編集してください。

public function list(Request $request)
{
    $sort = $request->get('sort');
    if ($sort) {
        if ($sort === '1') {
            $articles = Article::orderBy('created_at')->get();
          //↓ここから
          } elseif ($sort === '2') {
              $articles = Article::orderBy('created_at', 'DESC')->get();
          }
          //↑ここまで追加
      } else {
          $articles = Article::all();
      }
  
  
      return view(
          'article.list',
          [
              'sort' => $sort,//<-追加
              'articles' => $articles
          ]
      );
  }

} elseif ($sort === '2') {
    $articles = Article::orderBy('created_at', 'DESC')->get();
}

これは、最初のif文の条件に一致せず、さらに$sortに'2'が入っていたら、orderByでDESCでソートしたデータをgetする内容です。
DESCとはASCの逆の意味になります。

return view(
    'article.list',
    [
        'sort' => $sort,
        'articles' => $articles
    ]
);

そして、$sortをbladeへ渡します。つまり、初回アクセス時は$sortのvalueがないので、$sortにはnullが入ってbladeに渡され、ソートした場合は、$sortにソートした時のvalueが入りbladeへ渡されます。ということは、前回どのソートボタンが押されたかbladeで判断できるということですね!

そしたら、list.blade.phpを以下のように編集してください。

<form action="{{route('article.list')}}">
    <button type="submit" name="sort" value="@if (!isset($sort) || $sort !== '1') 1 @elseif ($sort === '1') 2 @endif">作成日順</button>
    <button type="submit" name="sort" value="">あいうえお順</button>
</form>

<button type="submit" name="sort" value="@if (!isset($sort) || $sort !== '1') 1 @elseif ($sort === '1') 2 @endif">作成日順</button>

これは、もし$sortがない(null)、または、$sortが'1'ではない場合、valueを1にして、$sortが'1'の場合、valueを2にしています。

では、もう一度作成日順のボタンを押してみて、DESCでソートされるか確認してみましょう!


はい、いい感じに最近順にソートされましたね!
OKです!


では、次にあいうえお順でソートするように実装してみましょう!

まず、list.blade.phpを次のように編集してください。

<form action="{{route('article.list')}}">
    <button type="submit" name="sort" value="@if (!isset($sort) || $sort !== '1') 1 @elseif ($sort === '1') 2 @endif">作成日順</button>
    <button type="submit" name="sort" value="3">あいうえお順</button>//<-編集
</form>

まず、あいうえお順のボタンのvalueへ3を設定します。

そしたらArticleControllerのlistアクションを編集しましょう。

if ($sort) {
    if ($sort === '1') {
        $articles = Article::orderBy('created_at')->get();
    } elseif ($sort === '2') {
        $articles = Article::orderBy('created_at', 'DESC')->get();
    //↓追加
    } elseif ($sort === '3') {
        $articles = Article::orderBy('title')->get();
    }
} else {
    $articles = Article::all();
}

はい、先ほどと同じようにsortの値を見て、今度はtitleをorderByします。
文字列のソートの場合、勝手にアルファベット順、あいうえお順にソートしてくれます。

では、あいうえお順のボタンを押してみましょう。


はい、いくつかブログを投稿してみて、試してみてください。
私の環境では、画像のようにtitleがあいうえお順にソートされました!
ちなみにですが、アルファベット、日本語自体の並びも変更することができます。気になった方は調べてみましょう!

そしたら再度、同じボタンを押された場合、DESCでソートするように実装しましょう。

ArticleControllerのlistアクションを編集してください。

} elseif ($sort === '3') {
    $articles = Article::orderBy('title')->get();
//↓追加
} elseif ($sort === '4') {
    $articles = Article::orderBy('title', 'DESC')->get();
}

はい、作成日順と一緒ですね!

(2021/10/26追記
あいうえお順のボタンのbladeの実装を記載し忘れていました🙇‍♂️

<button type="submit" name="sort" value="@if (!isset($sort) || $sort !== '3') 3 @elseif ($sort === '3') 4 @endif">あいうえお順</button>

やっていることは作成日順とほとんど同じです!
)

では、もう一度あいうえお順のボタンを押してみましょう!

ソートされましたか?されていれば成功です!

ソートの実装は以上になります!

こんな感じでみなさん思い思いのソートを実装してみてください!(☝︎ ՞ਊ ՞)☝︎


ということで早速次にいきます。

検索機能を実装していきましょう!


検索機能を実装する


list.blade.phpを以下のように編集しましょう!

//↓追加
<form action="#">
    word: <input type="text" name="word">
    <input type="submit" value="検索">
</form>
//↑
<form action="{{route('article.list')}}">
<button type="submit" name="sort" value="@if (!isset($sort) || $sort !== '1') 1 @elseif ($sort === '1') 2 @endif">作成日順</button>
<button type="submit" name="sort" value="@if (!isset($sort) || $sort !== '3') 3 @elseif ($sort === '3') 4 @endif">あいうえお順</button>
</form>
//省略

今回は、別のアクションを用意する予定なので、もう一つformを追加します。

というのも、これ以上listアクションにif文が増えるとよくわからなくなっちゃうかなと感じたので。

それで、検索機能なので、

<input type="text" name="word">

と、テキストの入力フォームを作成します。

それでは、ArticleControllerへ以下のアクションを追加してください。

public function search(Request $request)
{
    $word = $request->get('word');
    if ($word !== null) {
        $escape_word = addcslashes($word, '\\_%');
        $articles = Article::where('title', 'like', '%' . $escape_word . '%')->get();
    } else {
        $articles = Article::all();
    }
    return view(
        'article.list',
        [
            'articles' => $articles
        ]
    );
}

$word = $request->get('word');

まず、requestからwordのvalueを取得します。

$escape_word = addcslashes($word, '\\_%');

これは、エスケープ処理です。
addcslashes関数は、第二引数に羅列した文字列の直前に、バックスラッシュを追加してくれる関数です。
この処理を追加しないと、すぐ下で紹介しているlike検索が正確に行えません。

$articles = Article::where('title', 'like', '%' . $escape_word . '%')->get();

これは、Articleモデルのtitleフィールドを対象に、like検索を行い、検索結果を取得しています。
like検索とは、部分一致でレコードを検索することで、あいまい検索とも呼ばれています。
like検索について簡単に説明すると、例えば、保存されているブログのタイトルが
  • 初ブログ投稿
  • 初タグ投稿
と二つあるとします。
それで、$wordに「」という文字列が入っていた場合、like検索すると
  • ブログ投稿
  • タグ投稿
の二つがヒットします。
また、$wordに「ブロ」という文字列が入っていた場合、like検索すると
  • ブログ投稿
の一つがヒットします。
詳しくは参考サイトをご覧ください。

また、
'%' . $escape_word . '%'

これは文字列を連結する書き方になります。例えば、$wordに「テスト」という文字列が入っていた場合、「%テスト%」という文字列が作成されます。

少し説明が長くなりましたが、$wordに何かしら値が入っていたら、articlesテーブルのtitleフィールドからlike検索を行い、結果を$articles変数へ代入します。

で、$wordに何も入っていない(null)場合、全てのブログを取得し、$articles変数へ代入します。

最後に、return viewでブログ一覧画面を指定し、$articlesをbladeへ渡します。


ということで、routeを設定して早速テストしてみましょう。

web.phpへ以下のルートを追加してください。

Rotue::get('/article/list/search', 'ArticleController@search')->name('article.list.search');

次にlist.blade.phpを以下のように編集してください。

<form action="{{route('article.list.search')}}">//<-編集
    word: <input type="text" name="word">
    <input type="submit" value="検索">
</form>

formのactionを指定します。


では適当に検索フォームを入力して検索ボタンを押してみましょう。




オッケーそうですね!

以上で今回の記事を終わりにしたいと思います!

だいぶブログアプリの機能が充実してきましたね!

さぁ、この勝手にlaravelチュートリアルも残すところ後一つです!

あとちょいです!

最後まで頑張っていきましょう!


0
0
0
0
通信エラーが発生しました。
似たような記事