Laravelでsession（カート機能実装）についてまとめてみた

#0.はじめに
####この記事について

#####この記事は**「Requestインスタンス」**を用いたセッションに関する記事です。

Laravelを用いた共同開発講座に参加させていただきました。
その中で一際難しく感じたセッションを用いたカート機能の実装についてまとめたいと思います。
同じように悩む誰かの一助になれば幸いです。

転職前の若造の乱筆ですがよろしくお願いいたします。
間違い等あれば喜んで訂正させていただきます。

####簡単に自己紹介
・医療職5年目
・完全独学6ヶ月後、共同開発講座に参加
・転職まであと○○（？）

ではさっそく参ります。
#1.セッションとは

####セッションて何？
**【セッション　＝　データを保存しておく仕組み】**
です。


よくある"session"の説明　　　>>>　　「アクセスの開始から終了までの一連の通信」　　　
実際の"session"のイメージ　　>>>  　　「カギ」とか「合言葉」みたいな

という感じに、よくある説明と実際の掴みどころが少し違うような気がします。
よくある説明の通り、実現したいことは「アクセスの開始から終了までの一連の通信」を成り立たせることです。
SNSの場合毎回ログインせずとも、なぜかログイン後の画面からスタートできます。
これは一度ログインしたユーザーの情報を、サーバーが覚えてくれているから実現できます。
ショッピングサイトの場合でも、商品をカートに入れたあとにまたショッピングを楽しむにはカートに入れた商品のデータを保存しておく必要があります。
そのデータを保存するためにセッション機能があります。

*「セッション機能をなぜ使うのか？」*
この問に一言で答えるなら
*「ステートレスなプロトコルをステートフルなプロトコルにするために使う」*
となります。
これを超具体的に解説すると下記のようになります。

####具体例で解説
①あるuserがSNSに**初回ログイン**した時にクライアントは、サーバー（Laravel）に合言葉（セッション情報）を渡します。
②サーバーは合言葉を覚えます。
③userはSNSを閉じます（別のサイトに飛ぶとします）
④（サーバーはまだ覚えてます。）
⑤userはSNSのログイン後画面に戻ります。（合言葉を言う）
⑥サーバーはuserを識別し、歓迎してログイン後の状態にする。（合言葉を確認）
⑦ログインされた状態が保たれています（ステートフル！！）

※セッション機能がないともう一度ログインしないといけません（ステートレス。。）


つまり

ステート(状態)レス　＝＝＝　（ログイン）状態が保てない
を
ステート（状態）フル　＝＝＝　（ログイン）状態を保てる

ようにするのがセッション機能です。


おそらくこれだけでは不足だと思いますので
参考になり、わかりやすかった記事を掲載させていただきます。
https://qiita.com/Yametaro/items/9b65a21940e001554719
https://umaroidblog.com/webtechnology1

#2.セッションを使うと何ができる？

**①ログイン状態のキープ**

**②ショッピングカート機能として商品をカート内に入れたままの状態をキープ**

だいたいこの2つです（他にもあると思います）
今回は②のショッピングカート機能を実装したので、そちらを深堀していきます。

#3.セッションの使い方その①〜言葉で説明〜
この項でまず押さえていただきたいのが
**「セッション操作は、サーバー（Laravel）内での動作である」**
ということを忘れずにお進みください。（この前提が割と大事）
####セッション操作の種類
Laravelでセッションを操作するには主に３つの方法があるようです。

**①Requestインスタンス**
②Sessionファサード
③Sessionヘルパ


今回は**①Requestインスタンス**を用いて実装しました。
→今回はショッピングカート機能なので、ユーザーが購入したい商品をカートにいれる際にFormタグで送信（HTTPリクエスト）するため、この方法を用います。
つまり
商品情報を含んだHTTPリクエストを受け取って、その情報をそのままサーバー(Laravel)にセッション情報として保存したい時に有用です。

####大まかな流れ
大きな流れとしては以下のようになります。
やりたいことは非常に簡単です。

**①飛んできたRequestをLaravelのセッションへ保存する**
**②保存したセッション情報を取得し、表示させる**

この2つの動作をLaravel上で実装できればOKです。
やることはこの2つだけなのですが、ここにMVCや変数の受け渡しが絡んでくるためややこしいのだと思います...
次に実際に使用するコードについて見ていきます。

#4.セッションの使い方その②〜コードで説明〜

####使用するコード

```php:addCartアクション
//セッションに「キー」という名前をつけて、セッション情報を保存します
$request->session()->put('ここにキーが入る', $ここに値が入る);
```
こちらのコードはaddCartアクション(名前は適当)に記載します。
つまりカートに商品をいれると言う動作を担うアクションに記載します。
このコードにより、サーバー（Laravel）にセッション情報が「キー」と言う名前で保存されます。


```php:indexアクション
//セッションから「キー」という名前を探して、セッション情報を取得します
$request->session()->get('ここにキーが入る', $ここに値が入る);
```

こちらのコードはindexアクション（名前は適当）に記載します。
つまりカート内商品一覧を表示させる動作を担うアクションに記載します。
このコードにより、サーバ（Laravel）に保存していた「キー」と言う名前のセッション情報を引っ張り出します。

上記2つのコードを使用します

####セッション情報の保存場所について

ちなみに
「セッション情報を保存する」とありますが、
**いったいどこにどんな形で保存されるのか？**

ここに保存されます↓↓↓
__strage > framework > sessions__
![スクリーンショット 2020-12-29 0.01.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650911/9a75dad7-a3fe-2802-79d4-54394e89569b.png)

その中身であるsession情報は以下の通り暗号化？されています↓

```
a:4:{s:6:"_token";s:40:"uK79rCecgKQ1OItE1PtKdwj5OgPQUX1DbS5tY9FS";s:9:"_previous";a:1:{s:3:"url";s:39:"http://192.168.33.11/●●●●●●●●●●●●●●●";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:3:"url";a:1:{s:8:"intended";s:25:"http://192.168.33.11/●●●●●●";}}
```

このように、セッション情報は一種の個人情報であるため、暗号化されてサーバー側に保存されています。


#5.カート機能を実装してみる

では、いよいよ実装に入ります。

###ルーティング

```php:web.php
Route::resource('cartitem', 'CartController', ['only' => ['index']]);
Route::group(["prefix" => 'iteminfo'], function() {
    Route::get('/{id}', 'CartController@show');
    Route::post('/add', 'CartController@addCart')->name('addcart');
});
```
参考までに載せておきます。

###コントローラ

```php:CartController.php　(showアクション)
public function show($id)
    {
        //変数の初期化
        $ProductInfo = array();
        $ProductCategory = array();
        $UserId = '';
        //urlパラメータから飛んできたユーザidを元にモデルからそれぞれ商品、カテゴリーを特定
        $ProductInfo = Product::findOrFail($id);
        $ProductCategory = Category::findOrFail($ProductInfo -> category_id);
        $UserId = Auth::user()->id;
        
        return view('iteminfo', 
        [
            'ProductInfo' => $ProductInfo,
            'ProductCategory' => $ProductCategory,
            'UserId' => $UserId,
        ]);
    }
```

showアクションはiteminfo画面（商品の詳細画面）を表示させるアクションです
参考までに載せておきます。


###View(商品詳細画面)　の　Form部分


```php:iteminfo.blade.php(Form部分)
{!! Form::open(['route' => 'addcart']) !!}
    
        {{ Form::hidden('user_id', $userId) }}
    
        {{ Form::hidden('productId', $productInfo->id) }}
    
    <div class="form-group row justify-content-center">
        <label class="col-form-label">購入個数</label>
        <div class="">
            
            <input type="number"
　　　　　　　 class="inputNumber form-control" value="0" name="quantity" >
        </div>

        <lavel class="col-form-label">個</lavel>
        <div class="col-sm-auto">
            {!! Form::submit('カートへ', ['class' => 'btn btn-primary']) !!}
        </div>
    </div>
{!! Form::close() !!}
```
####解説

これはForm部分で　「カートへ入れる」のボタンの部分になります。
このボタンを押すと下記の動作がに入ります。

**①{{ Form::hidden('user_id', $UserId) }}**

**②{{ Form::hidden('ProductId', $ProductInfo->id) }}**

__③＜input 略 name="Quantity" ＞__

この３つのname属性で送信する（Formで指定したaddCartという名前のルーティングへ）
↓
web.phpを介して、CartController（addCartアクション）へいきます。


###コントローラ（addCartアクション）

```php:CartController（addCartアクション）

public function addCart(Request $request)
    {
        //セッションに保存したい変数を定義する（ここでは商品idと注文個数）
        //飛んできた$requestの中のname属性をそれぞれ指定
        $SessionProductId = $request->ProductId;
        $SessionProductQuantity = $request->Quantity;
        //配列の入れ物を作る（初期化）
        $SessionData = array();
        
        //作った配列に、compact関数を用いてidと個数の変数をまとめる（”” を使っているが変数の意味）
        $SessionData = compact("SessionProductId", "SessionProductQuantity");
        
        //session_dataというキーで、$SessionDataをセッションに登録
        $request->session()->push('session_data', $SessionData);
        
        return redirect('cartitem');
    }
```

####解説

今回セッションに保存したい内容は
①注文された商品のid
②注文された個数
の2つになります。

なので
このアクションでセッションにIDと個数を保存します。

変数を定義して、 ProductId というname属性で飛んできた内容を＄requestから抽出します。
＄SessionProductId = ＄request->ProductId;

同じく Quantity というname属性で飛んできた内容を＄requestから抽出します。
＄SessionProductQuantity = ＄request->Quantity;


ここで新たな変数を定義し、空の配列を用意してあげます。
※ショッピングカート機能であれば、商品idと注文個数が1セットであって欲しいために新たな変数を定義し、ショッピングはたくさんの商品を買うので、データを管理しやすくするために配列を作成しました。
＄SessionData = array();

作成した配列に2つの変数を統合します。
＄SessionData = compact("SessionProductId", "SessionProductQuantity");
※この時ダブルクォーテーションがついているが"SessionProductId"は変数を意味しています。

セッションにpushして配列に次々と値を加えていく形をとる
＄request->session()->push('session_data', ＄SessionData);
※大項目4にて、セッションに保存は”put”を用いていましたが、今回は配列に入れていくので”push”となります。


web.phpのcartitemにリダイレクトする
↓
CartControllerのindexアクションに繋がる
（このindexアクションでカート内商品一覧を表示させる動作を担います）


###コントローラ（indexアクション）

```php:cartController（indexアクション）

public function index(Request ＄request)
    {
        //セッションに保存していた値を取得し、変数として定義
        ＄SessionData = ＄request->session()->get('session_data');
        //セッションデータのなかのそれぞれのデータを抽出
        ＄SessionProductId = array_column(＄SessionData, 'SessionProductId');
        ＄SessionProductQuantity = array_column(＄SessionData, 'SessionProductQuantity');
        dd($SessionData);
    }
```

####解説

セッションに保存していた値を取得し、変数で再定義します。

＄SessionData = ＄request->session()->get('session_data');

array_column関数を用いてIDのデータをそれぞれ抽出し、これもまた変数で再定義します。
＄SessionProductId = array_column(＄SessionData, 'SessionProductId');

注文個数（Quantity）のデータに関しても同様に。
＄SessionProductQuantity = array_column(＄SessionData, 'SessionProductQuantity');

さて、ここで
dd($SessionData);
を記述すれば、ここにどのようなデータが飛んできているのかがわかります。
せっかくなので見ておきましょう。

#6.セッションから取り出したデータ内容を確認

以下は商品詳細画面です。
ここに個数「5個」を入力して「カートへ」ボタンを押すとどうなるでしょう。
ちなみにこのサンプルデータのidは「2」です。
![スクリーンショット 2020-12-29 23.43.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650911/a146b1f6-c61d-9e15-a3cd-d35bf6164cd5.png)
ボタンを押すと
![スクリーンショット 2020-12-29 23.44.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650911/06973264-567a-ce26-bede-c8bc8e0fbc09.png)
配列でデータが飛んできているのがわかります。
その配列の中にidと個数の情報がはいっているのがわかります。

ではこのあと、他の商品画面に遷移してもう一つ商品をカートに入れてみるとどうでしょう。
商品idは「4」、個数は「8」個でカートに入れてみます。
![スクリーンショット 2020-12-29 23.46.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650911/4334a136-b304-4820-5b13-b616525ca619.png)
ボタンを押すと
![スクリーンショット 2020-12-29 23.47.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650911/0bf95ba7-c0c3-98ab-0e60-3b46868b8159.png)
配列の中に配列でデータが増えて飛んできているのがわかります。
つまり、商品1つにつき、1つの配列でまとめてデータを送信しているということです。
さらに、先ほどカートへ入れた商品データの配列もそのまま残っていることがわかります。
つまり、セッションに保存された内容は、一度画面遷移を行っても情報は保持されたままということです。

そして
この取り出したデータ（変数）を元に、View（カート内商品一覧）でforeach文などを用いて商品データを表示させることができれば、カート機能の完成です!!

#7.まとめ
今回はカート機能についてまとめてみました。
コントローラの使い方や変数の渡し方がすごく重要だと思います。
そして、これがセッションの全てではありませんが、非常に便利な機能だと感じました。
まだまだこれからも精進いたします！

少し長くなってしまいましたが、最後まで読んでいただいた方々、本当にありがとうございますm(__)m