【Laravel】#3 商品検索ページが表示されるまでに起きていることを丁寧に追う


# していること
ララベルで実装されたECサイトのリーディングをしていきたいと思います。
使用するオープンソースのURLはこちらです。
https://github.com/jsdecena/laracom
# 見た目を確認しておく
商品を検索してみます。
http://localhost:8000/search?q=accusamus
![スクリーンショット 2023-06-12 23.54.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3150767/99d54ee3-3b7b-4959-a93b-0c062189f775.png)
accusamusで検索をすると、URLクエリパラメタqにaccusamusが代入され、accusamusを含む商品のみが表示されます。
# 見えないところで起きていること
1. ルーティングの定義
1. リクエストの送信
1. ルーティングのマッチング
1. ミドルウェアの処理（今回は関連づけられていません）
1. コントローラーのアクションの実行
1. レスポンスの生成と送信

1つ1つ具体的に掘り下げていきます。
# 1. ルーティングの定義
これは我々が記述するルーティングのことです。web.phpファイルには、ルーティングを定義します。ルーティングは、特定のURLパスに対してどのコントローラーのアクションを実行するかを指定するものです。実際に、このECサイトの商品検索ページについて、このようなルーティングの記述があります。
```php:web.php
Route::get("search", 'ProductController@search')->name('search.product');
```
ルートパス「search」というgetリクエストがあったとき、'ProductController'の'search'メソッドを実行するよう定義されています。
# 2.リクエストの送信
クライアント側から特定のURLに対してHTTPリクエストが送信されます。ブラウザから'search'へのGETリクエストが送信されます。

# 3. ルーティングのマッチング
Laravelのルーターが、送信されたリクエストのルートパスと定義されたルーティングを照合します。ルートパスと一致するルーティングが見つかった場合、そのルートに関連付けられたアクションが実行される準備が整います。
# 4 ミドルウェアの処理
今回は割愛します。'auth','web'ミドルウェアが適用されているページがあるため、それはその時に言及します。

# 5. コントローラーのアクションの実行
ルートに関連付けられたコントローラーのアクションが実行されます。
どのようにしてProductControllerを見つけるかを厳密に追います。
そもそもルーティングの定義で触れたルーティングの記述は、このような記述に囲まれています。
```php:web.php
Route::namespace('Front')->group(function () {
...
Route::get("search", 'ProductController@search')->name('search.product');
...
}
```
一行目に関して、'Front'という指定した名前空間（namespace）内にあるコントローラーに対して、グループ内のすべてのルートが適用されるようになります。ルーティングのグループ化です。

Laravelの名前空間の指定するには、ファイルの先頭に名前空間を指定する必要があります。実際にProductController.phpの先頭にはこのような記述があり、名前空間を宣言しています。
```php:ProductController.php
<?php

namespace App\Http\Controllers\Front;
//以下Frontという名前空間
```
**しかし、Laravelにおいて、名前空間とディレクトリ構成を一致させる必要はありませんが、一致させることが推奨されます。あくまで推奨であり、Laravel側はディレクトリ構成は参照せずに、名前空間から探します。**　私はこのことを知らなかったため、名前空間の概念の理解に時間がかかりました。このサンプルコードは名前空間とディレクトリ構成が一致しているため（私が調べた範囲では）、名前空間＝ディレクトリ構成という認識で話を進めていきます。なお、名前空間自体の理解に関しては、以下のサイトを参考にしてください。

https://qiita.com/yukari_k1124/items/84ed9a8c0d98e06582b4

コントローラーファイルは、大きく3種類に分かれており、Admin（管理社）、Auth（認証に関するもの）、Front（利用者）の3種類に大きく分かれています。
![スクリーンショット 2023-06-13 0.26.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3150767/43891ba1-8231-1064-e6f3-c5d15f46b5f4.png)
名前空間＝ディレクトリですので、これら3種類のディレクトリがあります。
![スクリーンショット 2023-06-13 0.39.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3150767/966136eb-66c5-3472-e793-b445ac1fc3d5.png)

**Laravelでは、コントローラーのクラス名とファイル名を一致させる必要はありませんが、一致させることが推奨されています。** これも同様に知りませんでした。今回実行するのはProductControllerですので、クラス名とファイル名が一致していることを信じて、ProductController.phpを開きます。
```php:ProductController.php
<?php

namespace App\Http\Controllers\Front;

...

class ProductController extends Controller
{
    ...
        ));
    }
}
```
確かに、Frontという名前空間にあるProductControllerが見つかりました。呼び出されたProductControllerはインスタンス化されます。インスタンス化された時、まず初めに__constrctが呼び出されます。ProductRepositoryInterfaceの実装クラスのインスタンスが注入されます。コンストラクタ？？インターフェース？？といった疑問が生まれると思うため、一旦飛ばします。次回に詳しく追いたいと思います。とりあえず、ProductControllerのsearchメソッドが実行されるようです。メソッドによって返されるデータとその後の流れだけ追います。今回の記事では雰囲気だけ掴み取ってください。
```php:ProductController.php
public function search()
    {
        // request()はララベルのヘルパ関数
        // 引数を空にすると「Illuminate\Http\Request」のインスタンスが返ってくる
        $list = $this->productRepo->searchProduct(request()->input('q'));

        // map:配列それぞれに関数を通す
        $products = $list->where('status', 1)->map(function (Product $item) {
            // transformProduct関数
            // 商品それぞれの情報をインスタンスにまとめる関数
            return $this->transformProduct($item);
        });

        // 10個ずつ返す
        return view('front.products.product-search', [
            'products' => $this->productRepo->paginateArrayResults($products->all(), 10)
        ]);
    }
```
＄listに検索された商品が代入されます。＄productsに＄listを色々こねてから代入します。＄listを「status=1（削除された商品でないこと）」で絞り込み、map関数で一つ一つの商品を整形をし、返り値をそのまま＄productsに代入（アロー関数）　をしています。
front.products.product-searchにビューを返します。フロントで表示されるサイトです。それと同時に、productsに諸々整形したデータ（＄product）を持たせます。これはビューファイルで＄productsとして扱えます。ちなみに、paginateArrayResults()はページネーション（10個ずつ表示など）のために用意されたヘルパ関数のはずです。
# 6. レスポンスの生成と送信
アクションが実行された後、結果としてレスポンスが生成されます。レスポンスは、クライアントに返されるデータやHTTPステータスコードなどの情報を含みます。例えば、アクションがビューを返す場合、そのビューがレンダリングされてHTMLコンテンツとしてレスポンスに含まれます。最終的なレスポンスが生成されたら、それがHTTPレスポンスとしてクライアントに送信されます。クライアントは、受け取ったレスポンスを処理し、表示または別の操作を行います。
ビューはどんなファイルなのでしょうか。コントローラーが返したビューファイルを見てみます。
```php:product-search.blade.php
@extends('layouts.front.app')

@section('content')
    <div class="container">
        <hr>
        <div class="row">
            <div class="category-top col-md-12">
                <h2>Search Results</h2>
            </div>
        </div>
        <hr>
        <div class="col-md-3">
            @include('front.categories.sidebar-category')
        </div>
        <div class="col-md-9">
            <div class="row">
                @include('front.products.product-list', ['products' => $products])
            </div>
        </div>
    </div>
@endsection
```
共通部分は別のファイルlayouts.front.appに切り分けてあります。これについてもいつか触れるかもしれません。商品の陳列部分だけ、といいたいところですが、商品がforeachやらで回される部分はさらに他のページに切り分けられていますね。front.products.product-listを参照します。（layouts.front.category-sidebar-subの方は一回触れないでおきます）
```php:product-list.blade.php
@if (!empty($products) && !collect($products)->isEmpty())
    <ul class="row text-center list-unstyled">
        @foreach ($products as $product)
            <li class="col-md-3 col-sm-6 col-xs-12 product-list">
               //（中略）
                <h4>{{ $product->name }}</h4>
                //（中略）
            </li>
        @endforeach
        @if ($products instanceof \Illuminate\Contracts\Pagination\LengthAwarePaginator)
            <div class="row">
                <div class="col-md-12">
                    <div class="pull-left">{{ $products->links() }}</div>
                </div>
            </div>
        @endif
    </ul>
@else
    <p class="alert alert-warning">No products yet.</p>
@endif
```
しっかりforeachが出てきましたね。省略した部分で＄product->nameなどといった形で渡された値がやっと使われます。＄productsが空っぽだった場合は条件分岐させることによりNo products yet.が表示されるようにしてあるのもバッチリです。
# あとがき
ドラーマーク（$）のエスケープがうまくいかず、大文字のドラーマークで代用した部分があります。どういう仕様なのかわかりませんが、詳細わかる方教えてください（；；）