👋

Webエンジニアへの道157日経過 - ポートフォリオアプリ ユーザー画面作成①

2024/09/29に公開

現在の学習状況

こんにちは、Someです!
まずは現在の学習状況をシェアしていきます。
※右にある数字は終了予定月と()内が完了実績月です。

  1. html・css 4月(4)
  2. Javascript 4月(4)
  3. Python 4月(4)
  4. Django 5月(5)
  5. git・github 5月(5)
  6. Linux 5月(5)
  7. DB・SQL 6月 (6)
  8. モダンJavaScript 6月(6)
  9. 業界研究、企業研究 7月(7)
  10. ポートフォリオアイデア 7月(7)
  11. PHP・Laravel基礎学習 7月(8)
  12. 安全なアプリケーション 8月(8)
  13. API 8月(8)
  14. ポートフォリオ作成開始 8月(8)←イマココ
  15. ポートフォリオ完成 10月
  16. 転職活動開始 10月
  17. 転職成功 12月

より詳細のロードマップ↓
(https://inquisitive-toy-f87.notion.site/519f079b4a34486781073b6f6b862572?v=13677f66b244447ea2ac02e6c6e455ca)

今週の学び・気付き

先週ポートフォリオアプリの設計が終わったので、今週はアプリの作成を行なっていきました!
まずは管理画面の作成です。

ユーザー画面 献立一覧登録画面

ユーザーが自分の普段の献立に取り入れたいものがあれば追加していくページになります。
とりあえず思いつく形で見た目を作ったので、今後UI/UX面は改善していく予定です。

機能としては下記画面を開いたユーザーが自分好みの献立を追加・削除できる感じです。

具体的な作成手順は以下に記載します。

ユーザー画面 献立一覧登録画面コード

ユーザーごとの献立候補を作成する

1.モデルとその関連を作成する

sail artisan make:model UserDishes --all

2.データベーステーブルに登録する項目を設定する

    public function up(): void
    {
        Schema::create('user_dishes', function (Blueprint $table) {
            $table->bigIncrements('user_menu_id');
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->foreignId('menu_option_id')->constrained('menu__options')->onDelete('cascade'); // menu__optionsのidを外部キーとして設定
            $table->timestamps();
        });
    }

3.マイグレーション

sail artisan migrate

ユーザー毎の献立候補選択画面表示作成

1.ビュー作成

@php
use App\Models\UserDishes;
@endphp

<x-user-layout>
    <div class="container mx-auto px-4 py-8">
        <div class="flex justify-between items-center mb-8">
            <h1 class="text-3xl font-bold text-gray-800">献立候補</h1>
            <a href="{{ route('user.index') }}" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
                献立選択画面に戻る
            </a>
        </div>
        @if (session('success'))
            <div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4 mb-6" role="alert">
                <p class="font-bold">成功</p>
                <p>{{ session('success') }}</p>
            </div>
        @endif
        @if (session('error'))
            <div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6" role="alert">
                <p class="font-bold">エラー</p>
                <p>{{ session('error') }}</p>
            </div>
        @endif
        <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
            @foreach ($menu_options as $menu_option)
                @php
                // ここでユーザーの献立を取得
                $userDishes = UserDishes::where('menu_option_id', $menu_option->id)
                    ->where('user_id', auth()->id())
                    ->first();
                @endphp
                <div class="bg-white rounded-lg shadow-md overflow-hidden transition duration-300 ease-in-out transform hover:-translate-y-1 hover:shadow-lg relative">
                    @if (UserDishes::where('user_id', auth()->id())->where('menu_option_id', $menu_option->id)->exists())
                        <div class="absolute top-0 right-0 bg-green-500 text-white text-xs font-bold px-2 py-1 rounded-bl-lg">追加済</div>
                    @endif
                    <div class="p-6">
                        <h5 class="text-xl font-semibold text-gray-800 mb-3">{{ $menu_option->dish_name }}</h5>
                        @if (!UserDishes::where('user_id', auth()->id())->where('menu_option_id', $menu_option->id)->exists())
                            <form action="{{ route('user.dishes.store') }}" method="POST">
                                @csrf
                                <input type="hidden" name="menu_option_id" value="{{ $menu_option->id }}">
                                <button type="submit" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
                                    追加
                                </button>
                            </form>
                        @endif
                        @if ($userDishes)
                        <form action="{{ route('user.dishes.destroy', $userDishes->user_menu_id) }}" method="POST" class="mt-2">
                            @csrf
                            @method('DELETE')
                            <button type="submit" class="w-full bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded-full transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50">
                                削除
                            </button>
                        </form>
                        @endif
                    </div>
                </div>
            @endforeach
        </div>
    </div>
</x-user-layout>

2.index、store、destroyメソッドのルーティング作成

    // 献立候補登録ページ表示
    Route::get('dishes', [UserDishesController::class, 'index'])->name('dishes');
    // ユーザー毎献立候補新規登録
    Route::post('dishes', [UserDishesController::class, 'store'])->name('dishes.store');
    //ユーザー毎献立候補削除
    Route::delete('dishes/{userMenuId}', [UserDishesController::class, 'destroy'])->name('dishes.destroy');
});

3.ルーティングに対応したコントローラ作成

indexメソッド

    public function index()
    {
        //献立候補を取得
        $menu_options = MenuOptions::all(); // Menu_Optionsモデルを使用
        return view('user.dishes', [
            'menu_options' => $menu_options
        ]);
    }

storeメソッド

    public function store(StoreUserDishesRequest $request)
    {
        //ユーザー毎献立候補新規登録
        //ユーザーIDを取得(認証済みユーザー)
        $userId = auth()->id();

        // menu_option_idを取得(フォームから送信された値)
        $menuOptionId = $request->input('menu_option_id'); // フォームから送信されたmenu_option_idを取得

        // すでに同じ献立候補が存在するか確認
        $existingDish = UserDishes::where('user_id', $userId)
        ->where('menu_option_id', $menuOptionId)
        ->first();

        if ($existingDish) {
            session()->flash('error', 'この献立候補はすでに追加されています。');
            return redirect()->route('user.dishes');
        }

        // 新しいユーザー献立を作成
        $userDishes = new UserDishes();
        $userDishes->user_id = $userId; // 認証済みユーザーのIDを設定
        $userDishes->menu_option_id = $menuOptionId; // フォームから送信されたmenu_option_idを設定

        // 献立を保存
        $userDishes->save();

        // user_menu_idを取得
        $userMenuId = $userDishes->user_menu_id; // 保存した後に主キーを取得

        // 成功メッセージをセッションに追加
        session()->flash('success', '献立候補が追加されました。');

        // リダイレクト(例: 献立一覧ページ)
        return redirect()->route('user.dishes'); // 適切なルートにリダイレクト

    }

destroyメソッド

        public function destroy($userMenuId)
        {
            $userDishes = UserDishes::where('user_menu_id', $userMenuId)->firstOrFail();
            $userDishes->delete();
            session()->flash('success', '献立候補が削除されました。');
            return redirect()->route('user.dishes');
        }

⭐️つまづきポイント

destroyメソッド適用時、user_dishesテーブルにidカラムがないと表示されていた。

user_dishesテーブルの主キーには、user_menu_idを使用しているため、コントローラでもそのように指定していたが、Laravelは標準で主キーをidとして認識する。

そのため、モデルで主キーをuser_menu_idにしていることを参照しなければならない。

kondate_portfolio/app/Models/UserDishes.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class UserDishes extends Model
{
    protected $table = 'user_dishes';
    protected $primaryKey = 'user_menu_id';
    public $incrementing = true;
}

これにて献立候補登録画面作成完了!

来週以降の進め方

ユーザーが各自登録した献立を、実際の日々の献立に組み込んでいきます。
月曜日から金曜日までの献立に反映さえなければならないが、できるのか!?
常に未知の世界でワクワクします。

それでは今回はこの辺で!
ありがとうございました!

Discussion