Webエンジニアへの道164日経過 - ポートフォリオアプリ ユーザー画面作成②
現在の学習状況
こんにちは、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.ビュー作成
@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;
}
これにて献立候補登録画面作成完了!
ユーザー毎各曜日での献立設定作成 メインメニューセレクト選択
- ビュー
<div>
<h3 class="font-semibold mb-2">メインメニュー</h3>
<select name="main_menu" class="w-full p-2 border rounded">
@foreach($mainMenus as $mainMenu)
<option value="{{ $mainMenu->menu_option_id }}">{{ $mainMenu->menuOption->dish_name }}</option>
@endforeach
</select>
</div>
コントローラーから$mainMenusを取得する
valueの意味は、mainMenuの中のmanu_option_idと、さらにその中のmenuOptionのdish_nameを表示するという意味
- コントローラー
public function index()
{
// 献立候補を取得
$menu_options = MenuOptions::all(); // Menu_Optionsモデルを使用
// user_dishesからメインメニューを取得
$mainMenus = UserDishes::with('menuOption') // menuOptionは関連するMenuOptionsモデル
->where('user_id', auth()->id()) // 現在のユーザーのIDでフィルタリング
->get();
return view('user.menu', [
'menu_options' => $menu_options,
'mainMenus' => $mainMenus // 取得したメインメニューをビューに渡す
]);
}
- ルーティング
//ユーザー画面
Route::prefix('user')->name('user.')->middleware('auth')->group(function() {
// 献立ページ表示
Route::get('index', [UserMenuController::class, 'index'])->name('index');
各曜日で選択した献立を表示する
1.最終登録してあるメインメニュー・サブメニューを表示させる
@foreach(['Mon', 'Tues', 'Wednes', 'Thurs', 'Fri', 'Satur', 'Sun'] as $day)
<div id="{{ $day }}" class="tab-content mt-4 hidden">
<h2 class="text-2xl font-bold mb-4">{{ $day }}曜日の献立</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<form action="{{ route('user.menu.keep') }}" method="POST">
@csrf
<input type="hidden" name="day_of_week" value="{{ $day }}"> <!-- 曜日を隠しフィールドで送信 -->
<h3 class="font-semibold mb-2">メインメニュー</h3>
<div name="main_menu" class="w-full p-2 border rounded">
@if($userMenus->isNotEmpty())
@php
$latestDishName = null; // 最後の料理名を格納する変数
@endphp
@foreach($userMenus as $userMenu)
@if($userMenu->day_of_week == $day)
@php
// 最新のuserDishを取得
$latestUserDish = UserDishes::where('menu_option_id', $userMenu->main_dish_id)
->where('user_id', auth()->id())
->latest() // 最新のレコードを取得
->first(); // 最初の1件を取得
@endphp
@if($latestUserDish)
@php
$latestDishName = $latestUserDish->menuOption ? $latestUserDish->menuOption->dish_name : null; // 料理名を格納
@endphp
@endif
@endif
@endforeach
@if($latestDishName)
<p>{{ $latestDishName }}</p> <!-- 最後の料理名を表示 -->
@else
<p>No dish available</p>
@endif
@else
<p>No data available</p>
@endif
</div>
</div>
<div>
<h3 class="font-semibold mb-2">サブメニュー1</h3>
<div name="main_menu" class="w-full p-2 border rounded">
@if($userMenus->isNotEmpty())
@php
$latestDishName = null; // 最後の料理名を格納する変数
@endphp
@foreach($userMenus as $userMenu)
@if($userMenu->day_of_week == $day)
@php
// 最新のuserDishを取得
$latestUserDish = UserDishes::where('menu_option_id', $userMenu->sub_dish1_id)
->where('user_id', auth()->id())
->latest() // 最新のレコードを取得
->first(); // 最初の1件を取得
@endphp
@if($latestUserDish)
@php
$latestDishName = $latestUserDish->menuOption ? $latestUserDish->menuOption->dish_name : null; // 料理名を格納
@endphp
@endif
@endif
@endforeach
@if($latestDishName)
<p>{{ $latestDishName }}</p> <!-- 最後の料理名を表示 -->
@else
<p>No dish available</p>
@endif
@else
<p>No data available</p>
@endif
</div>
</div>
<div>
<h3 class="font-semibold mb-2">サブメニュー2</h3>
<div name="main_menu" class="w-full p-2 border rounded">
@if($userMenus->isNotEmpty())
@php
$latestDishName = null; // 最後の料理名を格納する変数
@endphp
@foreach($userMenus as $userMenu)
@if($userMenu->day_of_week == $day)
@php
// 最新のuserDishを取得
$latestUserDish = UserDishes::where('menu_option_id', $userMenu->sub_dish2_id)
->where('user_id', auth()->id())
->latest() // 最新のレコードを取得
->first(); // 最初の1件を取得
@endphp
@if($latestUserDish)
@php
$latestDishName = $latestUserDish->menuOption ? $latestUserDish->menuOption->dish_name : null; // 料理名を格納
@endphp
@endif
@endif
@endforeach
@if($latestDishName)
<p>{{ $latestDishName }}</p> <!-- 最後の料理名を表示 -->
@else
<p>No dish available</p>
@endif
@else
<p>No data available</p>
@endif
</div>
</div>
</div>
来週以降の進め方
今週はユーザー画面に色んな要素を盛り込んでいきましたが、ビューとコントローラーの絡みがややこしくなってきてかなり苦戦しました。。。
一旦休憩したりすると頭がスッキリするので、ずっと齧り付いているのも危険なのかなと感じましたね。
来週からはランダムに献立を表示する機能と、UI/UXを高めるビューに変更していきます。
基本的なアプリ機能としては終盤になってきたので、完成が見えてきて嬉しいです!
それでは今回はこの辺で!
ありがとうございました!
Discussion