見出し画像

Laravel10(PHP8.2)+LaravelSail+MySQL8+phpMyAminで簡易的なECサイトを作るチュートリアル④

お疲れ様です。向江です。
すでに4記事目ですね!
ここまで来た方はそれだけでも持続力があるので、十分に自分を褒めてあげていいと思います。

結局なんだかんだプログラミングって
才能やコツとかの前にどれだけコードを書いたかってのが初学者にとっては絶対的な尺度になると思ってます。
(もちろんその後は色々考えて行かないといけないですが。)

ということでたくさん書いていきましょう!

カートを表示させる課題がありましたね。


******前回の記事から抜粋*******

■routes/web.phpで/myCartにアクセスされた場合でStockController@myCartメソッドが発動するようにルーティング

■myCart.blade.phpを作成(stocks.blade.phpと同じ構成でbody内は下記参考)

■UserStockモデルを作成

■cartsテーブルにphpMyadminからでいいので、
id=1,
stockId=1,
userId=1

id=2,
stockId=2,
userId=2


の2つのダミー情報を入れておく。

■StockController@myCartに
UserStockモデルを使ってusers_stocksテーブルからの全データを取得
その値を引き渡してmyCart.blade.php表示させる処理を記述。

■myCart.blade.phpのbody内に<h1>カートの中身</h1>とControllerから渡されたuserIdとstockIdをforeachを使って表示
(1122と味気なく表示されたらOK)

**********************

できましたでしょうか?
まだ出来ていない方はチャレンジしてみてくださいね。

答えを見ながら模写するのと、
考えながら0から自分でコードを書くのでは
習得率が違いますよ!
(二つの学校で講師+メンティー2名に教えてる立場なのでその差はよく認識しています。)

それでは詳細は3記事目に書いていますので
パパッと答えあわせ感覚で進めていきます。

???ってなる方は時々前の記事に戻ってくださいね。
詳しく解説しています。


1、カート画面を表示させる

課題①:routes/web.phpで/mycartにアクセスされた場合でStockController@myCartメソッドが発動するようにルーティング

Route::get('/myCart', [StockController::class, 'myCart'])->name('stock.myCart');

これはもう説明不要かな。

課題②:myCart.blade.phpを作成(stocks.blade.phpと同じ構成でbody内は下記参考)

下記参考なのでとりあえずstocks.blade.phpを複製してmyCart.blade.phpに名前を変えておきます。

課題③:UserStockモデルとcreate_users_stocks_tableを作成
users_stocksテーブルの構成は
・id(bigIncrements)
・stockId(int)
・userId(int)
・timestamps()

記事①の課題ですでにusers_stocksテーブルは作ってると思うので
まずはモデルの作成です。

php artisanの出番ですね。
app(Laravel自体フォルダ)でターミナルを開き

php artisan make:model UserStock

作成したUserStock.phpを開き
変更を可能とするカラムを指定しておきます。(最初に教えてるguardの方でもOK)
※勉強のためにやってるの実際のプロジェクトでguardでやったりfillableでやったり統一感ない感じでやるのはNG

また今回の命名でLaravelが自動で判別するテーブル名がuser_stocksテーブルになってしまうのでprotected $table =を使うことでちゃんとusers_stocksと結びつくように指定しています。

    protected $table = 'users_stocks';

    protected $fillable = [
        'stockId', 'userId',
    ];

もし今回users_stocksテーブルを初めて作ってたらmigrateを忘れずに!

// demoフォルダ上で
// sailがつくのはDBを扱うから。つかないのは扱わないので付けても付けなくてもどっちでも良いよ
sail php artisan migrate

これでOKです!

課題④:users_stocksテーブルにphpMyadminからでいいので、
id=1,
stockId=1,
userId=1

id=2,
stockId=2,
userId=2

の2つのダミー情報を入れておく。


http://localhost:8080

phpmyadminを開いて

ログイン後に
laravelデータベース(環境構築方法によって名前違います)を開くと
今まで作ったテーブルたちがいるのでusers_stocksテーブルを開きます。

あとは指示通りダミーデータを挿入します。
(phpMyAdmin自体の操作方法がわからない方はググりましょう!)

課題⑤:StocksController@myCartに
UserStockモデルを使ってEloquantで全データを取得
その値を引き渡してmycart.blade.php表示させる処理を記述。

ちょっと難しく感じるかもしれませんが、
やっていることは3記事目の内容と一緒です!

UserStockモデルを使うので
 Eloquantを使うならそのコントローラーでモデルをuseで結びつけてあげる必要があります。

なのでStockController.phpの頭の部分に

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Models\UserStock; //追加

を追記します。(もともとあるのもあります)

ルーティングを
Route::get('/myCart', [StockController::class, 'myCart'])->name('stock.myCart');
としているので、

mycartメソッドを作ってあげます。
そしてEloquantで全件取得なのでall()で良さそうですね。
情報を入れる変数は$myCartsにしてみます。
後はreturnで返すだけですね!
(???って方は3記事目を復習してください!詳しく書いてます!)

    public function myCart()
    {
        $myCartStocks = UserStock::all();
        return view('myCart',compact('myCartStocks'));
        
    }

課題⑥:myCart.blade.phpに<h1>カートの中身</h1>とControllerから渡されたuserIdとstockIdをforeachを使って表示
(1122と味気なく表示されたらOK)

<x-app-layout>
    <div class="container-fluid">
        <div class="mx-auto" style="max-width:1200px">
            <h1 style="color:#555555; text-align:center; font-size:1.2em; padding:24px 0px; font-weight:bold;">商品一覧</h1>
            <div class="">
                <div class="">
               {{-- 追加 --}}               
               @foreach($myCartStocks as $stock)
                    {{$stock->stockId}}<br>
                    {{$stock->userId}}<br>
               @endforeach
              {{-- ここまで --}}
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

これで一通りは完了ですね!

open http://localhost/myCart

まだデザイン周りはやらないのでこんな感じになっていれば成功です!↓


2、カートページはログイン状態のみ閲覧可能にする


実践課題①:/mycartはログインしている人だけに表示させたい!対策をしてみよう!
ヒント:middleware('auth')を使う!

ここは記事でも説明していなかったので実践課題です。

といってもかなり簡単に実装できます。
色々な方法がありますが、

Route::middleware('auth')->group(function () {
    Route::get('/myCart', [StockController::class, 'myCart'])->name('stock.myCart'); // ここに移動させた
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

これだけです。

これでこのルートはログイン状態じゃないとログイン画面へリダイレクトされます。

一度ログアウトしてから
/myCartを
リロードしたらログイン画面に飛ばされました。
せっかくなので右上のRegisterから会員登録をしてみてください!
ログイン状態だとカートページが見れるはずです!

そして今だと/にアクセスしたらエラーが出ますね。
ログイン状態じゃないと見れないページなはずなのに、見れてしまっているから起こるエラーです。

なのでこちらもmiddleware('auth')のグループに入れてあげちゃいましょう。

最終的にこうなります。

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    Route::get('/', [StockController::class, 'index'])->name('stock.index');
    Route::get('/myCart', [StockController::class, 'myCart'])->name('stock.myCart');
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

require __DIR__.'/auth.php';

さてここで、
なぜリダイレクト、、、?
どこに???

軽くツアーをしてみましょう。
middleware('auth')
となっていますので、authという名前のミドルウェアが存在していることは分かります。

ミドルウェアはリクエストが来てすぐと、レスポンスを返す時に動作する機能です。
厳密には違いますが簡単に言うとコントローラーの処理前と処理後といったところでしょうか。

ミドルウェアを使えば、
コントローラーが無駄にどんどん太っていくのを防げますし、
「なんにせよログインしてるかどうかを先に判断したい」
などメイン処理をする前に「なんにせよ」的な機能をつけたい時によく使う気がする。

あとミドルウェア化すると多面で使えて便利ですね。
当然今回のログイン認証にも大活躍します。


まずはミドルウェアの利用を司るapp/http/kernel.phpを見てみます。

すると少しスクロールすると、
$routeMiddlewareというのを発見しました!
そこの先頭に'auth'がいますね!

ここで
->middleware('auth')を使えば
\App\Http\Middleware\Authenticate::classと繋がることが分かります。

なのでmiddleware('auth')の中身はAuthenticate.phpです。

app/http/Middleware/Authenticate.phpを覗いてみましょう。

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     */
    protected function redirectTo(Request $request): ?string
    {
        return $request->expectsJson() ? null : route('login');
    }
}

まだまだ紐解いていく必要がありそうですね。
とりあえず何かあったらloginページに飛ばしてるような空気感があります。

次見るべきは
Illuminate\Auth\Middleware\Authenticateでしょうか。
venderフォルダの中に隠れています。

ただこれ以上行くと初学者は心が折れてしまいますので、
今はこうやってファイル群が裏に隠れているから
Laravelが準備しているmiddleware('auth')だけでログイン状態のチェックとリダイレクトが出来ると言う認識で十分です。

以上で前回の課題は完了です!

3、「カートに入れる」機能を作る

今の状態で
■商品一覧ページ(stocks.blade.php)
■カートページ(myCart.blade.php)
が出来上がりました。

次はカートに追加する機能を実装して行きます。
大きな流れは
①商品一覧ページの各商品ごとに「カートに追加」ボタンを作る
②カートに追加ボタンからPOST送信で/mycartにアクセスされた場合のルートを作成する。
③UsersStocksテーブルにログイン状態のユーザーのIDと商品のIDを格納する。
④UsersStocksテーブルから情報を取得して表示する。

です。
これだけでプログラミングのイメージがつく方はまずは自分だけで実装してみてください!
力になると思います!


では進んでいきます!
①商品一覧ページの各商品ごとに「カートに追加」ボタンを作る

stocks.blade.phpですね。
以下のように追記をします。

<x-app-layout>
    <div class="container-fluid">
        <div class="mx-auto" style="max-width:1200px">
            <h1 style="color:#555555; text-align:center; font-size:1.2em; padding:24px 0px; font-weight:bold;">商品一覧</h1>
            <div class="">
                <div class="grid grid-cols-4 gap-4 flex-wrap">
                    @foreach($stocks as $stock)
                        <div class="mycart_box text-center rounded shadow-lg bg-white p-6">
                            {{$stock->name}} <br>
                            {{$stock->fee}}円<br>
                            <img src="/image/{{$stock->imagePath}}" alt="" class="incart" >
                            <br>
                            {{$stock->explain}} <br>

                             {{-- 追加 --}}
                            <form action="addMyCart" method="post">
                                @csrf
                                <input type="hidden" name="stockId" value="{{ $stock->id }}">
                                <input type="submit" value="カートに入れる">
                            </form>
                             {{-- ここまで --}}
                        </div>
    
                    @endforeach
                    {{-- 追加 --}}
                </div>
                <div class="text-center" style="width: 200px;margin: 20px auto;">
                    {{  $stocks->links()}} 
                </div>
                {{-- ここまで --}}  
            </div>
        </div>
    </div>
</x-app-layout>

まだ画像などは表示されていませんがなんとか追加できましたね
デザイン部分は後ほどやりたいと思っているのでグッとこらえてください。

今回追加しているのは以下です。

<form action="addMyCart" method="post">
 @csrf
 <input type="hidden" name="stockId" value="{{ $stock->id }}">
 <input type="submit" value="カートに入れる">
</form>

form/inputについてはhtmlの基本事項なので割愛します。
{{ $stock->id }}もすでに解説済みですね。

ここで理解しておきたいのは
@csrfです。
これはセキュリティ対策ですね。
これを入れないとLaravelではformでの遷移を許可してくれません。
絶対に必要なおまじないのようなものなのでformが出たらまずは@csrfと覚えてください。
(無効にすることもできます)

意外と簡単に①は実装できちゃいました。

次は②なのでroutes/web.phpに追記して行きます。

Route::middleware('auth')->group(function () {
    Route::get('/', [StockController::class, 'index'])->name('stock.index');
    Route::get('/myCart', [StockController::class, 'myCart'])->name('stock.myCart');
    Route::post('/addMyCart', [StockController::class, 'addMyCart'])->name('stock.addMyCart'); //追記

これで/mycartにpost送信でアクセスされた時にはaddMycartメソッドが実行される事になります。

ちなみにGET送信とPOST送信の違いがわからない方は、
・GET送信→普通にアクセス
・POST送信→formを経由してこちらから情報を送りつつのアクセス
くらいの認識で今は十分かなと思います。(割と適当な解説すいません)

次はStockControllerでaddMycartメソッドの中身を作り込んで行きます。

③UsersStocksテーブルにログイン状態のユーザーのIDと商品のIDを格納する。
④UsersStocksテーブルから情報を取得して表示する。

一気に作って行きましょう。

StockControllerは以下のようになります。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; //追加
use App\Models\Models\Stock; 
use App\Models\UserStock;    


    public function addMyCart(Request $request)
    {
        $userId = Auth::id(); 
        $stockId = $request->input('stockId');
 
        $cartAddInfo = UserStock::firstOrCreate(['stockId' => $stockId,'userId' => $userId]);
 
        if($cartAddInfo->wasRecentlyCreated){
            $message = 'カートに追加しました';
        }
        else{
            $message = 'カートに登録済みです';
        }
 
        $myCartStocks = UserStock::where('userId',$userId)->get();
 
        return view('myCart',compact('myCartStocks' , 'message'));
 
    }

$userId = Auth::id()
これはログイン状態のユーザーのidを取得します。
Authファサードを使っています!

説明が長くなるので割愛しますが、ログイン状態のチェックなど様々な機能が実装できるので一度ドキュメントをみておくといいかもしれません!

https://readouble.com/laravel/10.x/ja/authentication.html
(後半)

そのあとは
$stockId=$request->input('stockId')
でPOST送信で送られてきたstockIdを取得しています。
さっきstocks.blade.phpで
<input type="hidden" name="stockId" value="{{ $stock->id }}">
って作りましたよね!
そのname="stockId"が$stockId=$request->input('stockId')で取得しています。

またStockControllerの頭にも

use Illuminate\Http\Request

と言うのがあってそれをメソッドで利用するために
public function addMycart(Request $request){
と最初のメソッドの引数にRequest $requestが入っています。

こちらも深入りは避けますが、
これらがあるからPOST送信のリクエストの中身が参照できるようになり、
$request->input('名前')という書き方でformで送られてきた値を取得することができます。
フォームを使うのであれば必ず使うやつですので覚えておきましょう!

次は
$cartAddInfo=UserStock::firstOrCreate(['stockId' => $stockId,'userId' => $userId]);
です。
firstOrCreateはstockIdとuserIdが全く同じレコードが存在しないか確認してなければ保存してくれます。

これだけでもいいですが、
■カートに追加した場合(同じレコードがなかった場合)
■カートに追加しない場合(同じレコードがあった場合)
で条件分岐させて表示させる文字を変えてみましょう。
※今回は個数という概念がないことにしています。

UserStock::firstOrCreateを格納した変数に対して、
->wasRecentlyCreatedを使います。
直近で保存された場合はtrueを
保存されていない場合はfalseを返してくれます。


if($cart_add_info->wasRecentlyCreated){
   $message = 'カートに追加しました';
}else{
   $message = 'カートに登録済みです';
}

これで状況にあったメッセージを表示させる事に成功しました。

今回は在庫が1つしかない設定ですのでこのようにしていますが、
カートに同じ種類のものを複数入れて行きたいのであれば、
else内でカート内数を+1させる処理をしてその情報をデータベースに持たせるなどしても良さそうですね。
同時にstocksテーブルにも在庫状態を…ってなってくるのでこの記事では触れないおきます。実践でやってみてください!

さあ、あとはカート情報を表示させるだけです!

4、カートの中身を表示する

StockContollerの中の

$myCartStocks = UserStock::where('userId',$userId)->get();


がカート内情報を取得するところですね。
仕組みはかなり単純です。

where句でuserIdが現在のログインしているユーザーのidと同じかどうかで情報を取得してきています。
あとは取得した情報をカートページで表示させるだけです。


return view('myCart',compact('myCartStocks' , 'message'));

しっかりmessageも連れて行ってあげましょう。

連れて行く先の
 myCart.blade.phpも編集を加えて行きます!

<x-app-layout>
    <div class="container-fluid">
        <div class="mx-auto" style="max-width:1200px">
            <h1 style="color:#555555; text-align:center; font-size:1.2em; padding:24px 0px; font-weight:bold;">
            {{ Auth::user()->name }}さんのカートの中身</h1>
                <p class="text-center">{{ $message ?? '' }}</p><br>
                <div class="">
               {{-- 追加 --}}               
               @foreach($myCartStocks as $stock)
                    {{$stock->stockId}}<br>
                    {{$stock->userId}}<br>
               @endforeach
              {{-- ここまで --}}
                </div>
            </div>
    </div>
</x-app-layout>

追加された{{ Auth::user()->name }}はまたしてもAuthファサードです。ログイン者の情報を取得し今回はnameカラムを表示させています。


<p class="text-center">{{ $message ?? '' }}</p><br>も追加されました。
これはコントローラーから送られてきたmessageを表示させています。
もし何もなければ表示させないことになります。

試しに色々カートにいれてみてください!
こんな感じになって行くと思います↓


なんかカートができそうな雰囲気になってきましたね!!

4、今までのページのレイアウトのこと


今ままで特にCSSは書いてないのになんか勝手にいい感じのデザインになってくれていますが、これはLaravelのデフォルトCSSフレームワークである

tailwind css

の力です。
昔はbootstrapですがいつの間にか変わっています。

このフレームワークは適応するクラス名を指定してあげるだけで勝手に装飾してくる便利なものです。

例えば class = "grid grid-cols-4 gap-4 flex-wrap"
とあったとすると、
「グリッド表示で、横並び4つで、それぞれの要素を4だけ間を開ける」
って勝手にやってくれてるわけです。

だから商品一覧が4つ並びになってるんですね。

他にもたくさんのクラスがあるので試してみるのもいいかもしれません。

https://tailwindcss.com/docs/aspect-ratio

さて今回はここまでにしたいと思います!
あとは画像を表示させたり、カート機能を完成させたりです。

多分折り返しはしているはず!
また次回の記事でお待ちしてます〜!

次回の記事

https://note.com/mukae9/n/nde603b89db93




***大阪の方でPHP学びたい方はここで先生してるんぜひ!***

http://code-create.tech/

この記事が気に入ったらサポートをしてみませんか?