Laravel を使って簡単なCRUD機能を実装 ③

# キーワード検索機能の追加

![スクリーンショット 2025-03-19 074740.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4034432/06d346cf-4935-4394-afaa-11c4f17faf59.png)

今回はこのように検索機能をつけて投稿の中から検索キーワードの文字列に合致する記事を表示してくれるようにした。

## 1. Controllerへの追加点
以下の関数を追加する

```php
 public function search(Request $request)
 {
    $query = $request->input('query');

    $posts = Post::where('title', 'like', "%{$query}%")
                 ->orWhere('body', 'like', "%{$query}%")
                 ->latest()
                 ->get();

    return view('post.search', compact('posts'));
 }
```

### 以下説明
```php
public function search(Request $request)
```

**public function search**
→ search という名前の関数（メソッド）を作っています。これはコントローラー内で定義され、外部からアクセスできる関数です。

**(Request $request)**
→ ユーザーが送信したデータ（検索キーワードなど）を受け取るための Request オブジェクトを引数として受け取っています。

```php
$query = $request->input('query');
```
**$request->input('query')**
→ ユーザーが検索フォームで入力した値を取得しています。
→ 例えば、ユーザーが「Laravel」と入力した場合、$query には "Laravel" という文字列が入ります。

```php
$posts = Post::where('title', 'like', "%{$query}%")
             ->orWhere('body', 'like', "%{$query}%")
             ->latest()
             ->get();
```

ここでデータベースの posts テーブルから検索を行っています。

**Post::where('title', 'like', "%{$query}%")**
Post は、posts テーブルを操作するための Eloquent モデル です。
Eloquentモデルとは、LaravelのORM（Object-Relational Mapping）であるEloquentが提供する、データベースとPHPのオブジェクトを簡単に操作できる機能のことです。Eloquentモデルを使うことで、SQLを書かずにデータベースのレコードを操作できます。

**where('title', 'like', "%{$query}%")** 
は、title カラム（列）に $query の文字列を含むデータを検索します。

LIKE は SQL の検索条件で、**"%{$query}%"** は、query という単語を含む（前後にどんな文字があってもOK）データを探すための書き方です。例えば、**$query = "Laravel"** の場合、以下のような title を持つ投稿がヒットします： 

"Laravel 入門"
"Laravel の基本"
"初めての Laravel"
**orWhere('body', 'like', "%{$query}%")**
orWhere は "または（OR）" の条件を追加します。
body カラム（記事の本文）にも $query が含まれるデータも検索対象にします。
**->latest()**
latest() は、新しい投稿から順番に並べるためのものです。

**->get()**
get() を使うことで、検索結果をすべて取得します。

```php
return view('post.search', compact('posts'));
```

**view('post.search', compact('posts'))**
→ post.search というビュー（画面）に posts のデータを渡して表示します。

**compact('posts')**
→ これは ['posts' => $posts] と同じ意味で、ビューに posts という変数名で検索結果を渡しています。

## 2. ビューの作成　
index.blade.php に以下のコードを追加
```php
    <!-- 検索フォームをスタイリング -->
    <div class="max-w-7xl mx-auto px-6 mt-4">
        <form action="{{ route('post.search') }}" method="GET" class="flex gap-2 mb-6">
            <input type="text" name="query" placeholder="検索キーワードを入力"
                   class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
            <button type="submit" class="px-4 py-2 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-600">
                検索
            </button>
        </form>
    </div>
```

### 以下説明
```php
<form action="{{ route('post.search') }}" method="GET" class="flex gap-2 mb-6">
```

**action="{{ route('post.search') }}"**
→ post.search ルートに検索キーワードを GET 送信

**method="GET"**
→ URLのクエリパラメータとして検索ワードを送る（例: /search?query=Laravel）

また新しくsearch.blade.php を作成し以下のようにビューを作成

```php
<!-- resources/views/post/search.blade.php -->
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
            🔍 検索結果
        </h2>
    </x-slot>

    <div class="max-w-7xl mx-auto px-6">
        <!-- 検索フォーム -->
        <form action="{{ route('post.search') }}" method="GET" class="flex gap-2 my-6">
            <input type="text" name="query" placeholder="検索キーワードを入力" 
                   value="{{ request('query') }}"
                   class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
            <button type="submit" class="px-4 py-2 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-600">
                検索
            </button>
        </form>

        <!-- 検索結果 -->
        @if ($posts->isEmpty())
            <p class="text-gray-500 text-center py-6">該当する投稿がありません。</p>
        @else
            @foreach($posts as $post)
            <div class="mt-4 p-8 bg-white w-full rounded-2xl">
                <h1 class="p-4 text-lg font-semibold">
                    件名：
                    <a href="{{route('post.show',$post)}}" class="text-blue-600">
                        {{$post->title}}
                    </a>
                </h1>
                <hr class="w-full">
                <p class="mt-4 p-4">
                    {{$post->body}}
                </p>
                @if($post->image)
                    <img src="{{ asset('storage/' . $post->image) }}" alt="投稿画像" style="max-width:200px;">
                @endif
                <div class="p-4 text-sm font-semibold">
                    <p>
                        {{$post->created_at}} / {{ optional($post->user)->name ?? 'ゲスト' }}
                    </p>
                </div>
            </div>
            @endforeach
        @endif
    </div>
</x-app-layout>
```

### 以下説明
```php
<form action="{{ route('post.search') }}" method="GET" class="flex gap-2 my-6">
    <input type="text" name="query" placeholder="検索キーワードを入力" 
           value="{{ request('query') }}"
           class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
    <button type="submit" class="px-4 py-2 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-600">
        検索
    </button>
</form>
```

この form のコードは、検索キーワードを入力し、ボタンを押すことで、特定のページに遷移し、検索結果を表示する という動作を実現しています。

**action="{{ route('post.search') }}"**
→ 検索の GET リクエストを送信

**value="{{ request('query') }}"**
これにより、検索ワードを保持（再検索時に入力欄に前回の検索ワードを残す）

```php
@if ($posts->isEmpty())
    <p class="text-gray-500 text-center py-6">該当する投稿がありません。</p>
@else
```

**$posts->isEmpty()** → 結果がない場合、メッセージを表示

```php
@foreach($posts as $post)
    <div class="mt-4 p-8 bg-white w-full rounded-2xl">
```

**foreach($posts as $post)** → posts のデータを1件ずつ表示

```php
@if($post->image)
    <img src="{{ asset('storage/' . $post->image) }}" alt="投稿画像" style="max-width:200px;">
@endif
```

**if($post->image)** → 画像がある場合のみ表示

**asset('storage/' . $post->image)** 
→ storage フォルダ内の画像を表示

```php
<div class="p-4 text-sm font-semibold">
    <p>
        {{$post->created_at}} / {{ optional($post->user)->name ?? 'ゲスト' }}
    </p>
</div>
```
**$post->created_at** → 投稿日時を表示
**optional($post->user)->name ?? 'ゲスト'**

投稿者名を表示（ユーザー情報が null の場合 "ゲスト" にする）

![スクリーンショット 2025-03-19 095207.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4034432/856b6974-785c-4716-b316-ce8700ac61d8.png)

このようなフォームを作成することができる。

### form> の action とは？
form> タグの action 属性は、フォームの送信先（リクエストを送るURL） を指定します。
```php
<form action="送信先のURL" method="GET">
```
例えば、以下のように書くと、https://example.com/search にリクエストが送られます。

```php
<form action="/search" method="GET">
```
method="GET" は、HTML の <form> タグで使用される属性で、フォームデータを送信する際の HTTP メソッドを指定するものです。このフォームでは、「検索キーワードを入力」→「検索ボタンを押す」 と、GET リクエストが /search に送信されます。
GET は主に 検索やページ遷移など、データを取得する目的 で使われます。GET リクエストでは、フォームの入力データが URL にクエリパラメータとして付加されます。
```
https://example.com/search?query=Laravel
```

###  route('post.search') の意味

Laravelでは、ルート（ページのURL）を route('ルート名') という形で取得できます。
これは、routes/web.php に定義されたルート名に対応するURLを動的に取得するためのものです。ルートに例えば

```php
Route::get('/search', [PostController::class, 'search'])->name('post.search');
```

ここで name('post.search') を付けたことで、
route('post.search') は /search というURLを表す ようになります。