Webエンジニアへの道157日経過 - ポートフォリオアプリ ユーザー画面作成①
現在の学習状況
こんにちは、Someです!
まずは現在の学習状況をシェアしていきます。
※右にある数字は終了予定月と()内が完了実績月です。
- html・css 4月(4)
- Javascript 4月(4)
- Python 4月(4)
- Django 5月(5)
- git・github 5月(5)
- Linux 5月(5)
- DB・SQL 6月 (6)
- モダンJavaScript 6月(6)
- 業界研究、企業研究 7月(7)
- ポートフォリオアイデア 7月(7)
- PHP・Laravel基礎学習 7月(8)
- 安全なアプリケーション 8月(8)
- API 8月(8)
- ポートフォリオ作成開始 8月(8)←イマココ
- ポートフォリオ完成 10月
- 転職活動開始 10月
- 転職成功 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