😄

Webエンジニアへの道150日経過 - ポートフォリオアプリ管理画面作成

2024/09/22に公開

現在の学習状況

こんにちは、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)

今週の学び・気付き

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

管理画面TOP

ログイン後こちらの画面に推移し、献立候補の新規登録、編集、削除が行えます。

献立候補新規登録画面

献立候補となる品名を4つまでまとめて登録できます。
献立タイプとしてmain、subを切り替えることができ、主食・サブメニューをそれぞれ登録可能。

献立候補編集画面

すでにデータベースに設定した献立候補を変更することができます。

今週作成したのは上記3画面です。
この中でも献立候補を4つ表示させる部分にfor文を使ったりして、基礎の大事さを感じました!

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

管理画面View作成

1.web.phpにルーティング設定

//管理画面トップページ
Route::get('/admin/top', function () {
    return view('admin.top');
});

2.reeources/views/adminにtop.blade.phpを作成

この時点で上記ファイルに打ったコードは適用される

3.resources/views/layoutsにadmin.blade.phpを作成

dashboard.blade.phpを見ると、app.blade.phpを参照するように記述あり、app/View/Components/AppLayout.phpにも以下記述があることから、dashboard.phpにはapp.blade.phpが適用されるようになっている。

{
    /**
     * Get the view / contents that represents the component.
     */
    public function render(): View
    {
        return view('layouts.app');
    }
}

4.そのためapp/View/Components/にAdminLayout.phpを作成し、以下コードを入力することで、

<x-admin-layout>を記載したファイルには、layouts/admin.blade.phpが適用されるようになる。

class AdminLayout extends Component
{
    /**
     * Get the view / contents that represents the component.
     */
    public function render(): View
    {
        return view('layouts.admin');
    }
}

5.views/componentsにadmin/header.blade.phpファイルを作成し、tailwindブロックのコードをそのまま差し込む(インデントが4つになっている場合は、このファイルのみ2つに変更してインデントを揃える)

6.views/layouts/admin.blade.phpのbodyタグ内にinclude入力

            @include('components.admin.header')

これでadmin/topを開くとtailwindcssが適用されている。

ログインで管理画面を表示させる

1.DatabaseSeeder.phpからにパスワード項目を追加する

        User::factory()->create([
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password123',
        ]);

2.シーダーをターミナルから読み込ませる

sail artisan make:seeder UserSeeder

3.auth.phpを確認すると、ログイン処理は/app/Http/Controllers/Auth/AuthenticatedSessionController.phpというファイルで行われていることが分かる


    //ログイン処理
    Route::post('login', [AuthenticatedSessionController::class, 'store']);

4.上記ファイルを確認すると、下記コードがあるため、routeの後をadmin.topに変更する。

    public function store(LoginRequest $request): RedirectResponse
    {
        $request->authenticate();

        $request->session()->regenerate();
        //管理画面トップページにリダイレクト
        return redirect()->intended(route('admin.top', absolute: false));
    }

5.ルーティングファイルから、nameを設定する

//管理画面トップページ
Route::get('/admin/top', function () {
    return view('admin.top');
})->name('admin.top');

これによりログイン後管理画面に遷移するようになる

管理画面からログアウトする

1.navigation画面右上にログアウトがあるため、navigation.blade.phpを確認

                        <!-- Authentication -->
                        <form method="POST" action="{{ route('logout') }}">
                            @csrf

                            <x-dropdown-link :href="route('logout')"
                                    onclick="event.preventDefault();
                                                this.closest('form').submit();">
                                {{ __('Log Out') }}
                            </x-dropdown-link>
                        </form>

2.上記ファイルがログアウトの記述になるので、これをheader.blade.phpに挿入する

    <form method="POST" action="{{ route('logout') }}" class='md:ml-auto'>
        @csrf
            <button
                :href="route('logout')"
                onclick="event.preventDefault();
                            this.closest('form').submit();"
                class="inline-flex items-center text-white bg-gray-700 border-0 py-1 px-3 focus:outline-none hover:bg-gray-800 rounded text-base mt-4 md:mt-0">
                Logout
            </button>
        </form>

※不要な部分は削除済み

これによりLogoutボタンを押すと、TOP画面に戻る

Logoutを押すと、Loginページに遷移するように変更

kondate_portfolio/app/Http/Controllers/Auth/AuthenticatedSessionController.php

return redirect を以下に変更する

    {
        Auth::guard('web')->logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();

        //ログイン画面にリダイレクト
        return redirect()->intended(route('login', absolute: false));
    }

これによりログアウト時ログイン画面に遷移する

ログインしてないと、admin/topに入れないようにする

Route::middleware('auth')->group(function () {

上記コードによって、ログインしていないと実行できないようにする

web.phpに先ほどのコードを追加することで、admin/topにはログインしていないと入れなくなる

Route::middleware('auth')->group(function () {
    //管理画面トップページ
    Route::get('/admin/top', function () {
        return view('admin.top');
    })->name('admin.top');
});

献立候補を新規登録

1.献立候補のデータベース関係をまとめて作成する

sail artisan make:model Menu_Options --all

allによって8種類のファイルがまとめて作成される

2.migrationファイル編集

    public function up(): void
    {
        Schema::create('menu__options', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->string('dish_name')->max(20)->comment('献立名');
            $table->enum('dish_type', ['main', 'sub1', 'sub2'])->comment('献立タイプ');
            $table->timestamps();
        });
    }

enumによって選択式に

foreignIdによって、外部キー設定

献立候補新規登録ページ作成

1.管理画面トップページに、献立候補登録ボタン作成

tailwindblocksにて

app/Http/Controllers/MenuOptionsController.php

<x-admin-layout>
    <section class="text-gray-600 body-font relative">
        <div class="container px-5 py-24 mx-auto">
            <div class="flex flex-col text-center w-full mb-12">
                <h1 class="sm:text-3xl text-2xl font-medium title-font mb-4 text-gray-900">献立候補を登録して下さい</h1>
                           </div>
                    <div class="p-2 w-full">
                        <button
                            onclick="location.href='{{ route('admin.menu_options.create') }}'"
                            class="flex mx-auto text-white bg-yellow-500 border-0 py-2 px-8 focus:outline-none hover:bg-yellow-600 rounded text-lg">
                            献立候補_新規登録
                        </button>
                    </div>

                </div>
            </div>
        </div>
    </section>
</x-admin-layout>

2.献立候補新規登録画面遷移用のルーティング作成

routes/web.php

use App\Http\Controllers\MenuOptionsController;

Route::prefix('admin')->name('admin.')->middleware('auth')->group(function () {
    //管理画面トップページ
    Route::get('top', function () {
        return view('admin.top');
    })->name('top');

    //献立管理
    Route::prefix('menu_options')->name('menu_options.')->group(function(){
        //献立新規登録画面
        Route::get('create' , [MenuOptionsController::class, 'create'])->name('create');
    });
});

3.コントローラーの処理を編集する
app/Http/Controllers/MenuOptionsController.php

     * 献立新規登録画面表示
     */
    public function create()
    {
        return view('admin.menu_options.create');
    }

4.献立候補新規登録ページを編集する

resources/views/admin/menu_options/create.blade.php

<x-admin-layout>
    <section class="text-gray-600 body-font relative">
        <div class="container px-5 py-24 mx-auto">
            <div class="flex flex-col text-center w-full mb-12">
                <h1 class="sm:text-3xl text-2xl font-medium title-font mb-4 text-gray-900">献立新規登録</h1>
            </div>
            <form method="POST" action="{{ route('admin.menu_options.store')}}">
                @csrf
                <div class="lg:w-1/2 md:w-2/3 mx-auto">
                    <div class="flex flex-wrap -m-2">
                        <div class="p-2 w-1/2">
                            <div class="relative">
                                <label for="name" class="leading-7 text-sm text-gray-600">献立名</label>
                                <input type="text" id="name" name="name" class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-yellow-500 focus:bg-white focus:ring-2 focus:ring-yellow-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out">
                            </div>
                        </div>
                        <div class="p-2 w-1/2">
                            <div class="relative">
                                <label for="type" class="leading-7 text-sm text-gray-600">献立タイプ</label>
                                <select id="type" name="type" class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-yellow-500 focus:bg-white focus:ring-2 focus:ring-yellow-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out">
                                    <option value="main">main</option>
                                    <option value="sub">sub</option>
                                    <!-- 必要に応じて他のオプションを追加 -->
                                </select>
                            </div>
                        </div>
                        <div class="p-2 w-full">
                            <button type="submit" class="flex mx-auto text-white bg-yellow-500 border-0 py-2 px-8 focus:outline-none hover:bg-yellow-600 rounded text-lg">
                                Button
                            </button>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </section>
</x-admin-layout>

献立名はnameで、献立タイプはENUM指定なのでtypeで記入する

5.保存先のルーティング設定を行う

routes/web.php

//献立新規登録処理
        Route::post('store' , [MenuOptionsController::class, 'store'])->name('store');

6.献立候補新規登録のコントローラ設定を行う

app/Http/Controllers/MenuOptionsController.php

     * 献立新規登録処理
     
        $menu_options = new Menu_Options();
        $menu_options->dish_name = $request->name;
        $menu_options->dish_type = $request->type;
        $menu_options->user_id = auth()->id();
        $menu_options->save();

        return redirect()->route('admin.top');
    }

user_idはいらなかったかも?DBにNOTNULL指定していないため追記

menu_optionsというのは、上部のuseから引っ張り

これにより献立候補新規登録完了!

7.献立を複数まとめて登録できるように変更

kondate_portfolio/app/Http/Controllers/MenuOptionsController.php

$names = $request->input('names');
        $types = $request->input('types');

        foreach ($names as $index => $name) {
            // 名前とタイプが空でないことを確認
            if (!empty($name) && !empty($types[$index])) {
                $menu_options = new Menu_Options();
                $menu_options->dish_name = $name;
                $menu_options->dish_type = $types[$index];
                $menu_options->user_id = auth()->id(); // ユーザーIDを設定
                $menu_options->save();

2.複数回分献立登録するゾーンを作成

kondate_portfolio/resources/views/admin/menu_options/create.blade.php

@for ($i = 0; $i < 4; $i++) <!-- 必要な数だけ繰り返し -->
                            <div class="p-2 w-1/2">
                                <div class="relative">
                                    <label for="name_{{ $i }}" class="leading-7 text-sm text-gray-600">献立名</label>
                                    <input type="text" id="name_{{ $i }}" name="names[]" class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-yellow-500 focus:bg-white focus:ring-2 focus:ring-yellow-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out">
                                </div>
                            </div>
                            <div class="p-2 w-1/2">
                                <div class="relative">
                                    <label for="type_{{ $i }}" class="leading-7 text-sm text-gray-600">献立タイプ</label>
                                    <select id="type_{{ $i }}" name="types[]" class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-yellow-500 focus:bg-white focus:ring-2 focus:ring-yellow-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out">
                                        <option value="main">main</option>
                                        <option value="sub">sub</option>
                                        <!-- 必要に応じて他のオプションを追加 -->
                                    </select>
                                </div>
                            </div>
                        @endfor

8.献立保存成功後、保存成功メッセージ表示

まずはコントローラ設定

kondate_portfolio/app/Http/Controllers/MenuOptionsController.php

        // 保存成功後のメッセージをセッションに格納
        session()->flash('success', '献立候補の新規登録が完了しました。');
        // 保存成功後のメッセージをセッションに格納
        return redirect()->route('admin.top')->with('success', '献立候補の新規登録が完了しました。');

9.次にビュー設定

kondate_portfolio/resources/views/admin/top.blade.php

    @if (session('success'))
        <div class="alert alert-success text-green-500">
            {{ session('success') }}
        </div>
    @endif

献立候補を管理画面トップに表示させる

1.管理画面トップの見た目に、献立一覧を表示させる

kondate_portfolio/resources/views/admin/top.blade.php

<section class="text-gray-600 body-font">
        <div class="container px-5 py-12 mx-auto">
            <div class="flex flex-col text-center w-full mb-20">
                <h1 class="sm:text-4xl text-3xl font-medium title-font mb-2 text-gray-900">献立候補一覧</h1>
            </div>
            <div class="lg:w-2/3 w-full mx-auto overflow-auto">
                <table class="table-auto w-full text-left whitespace-no-wrap">
                    <thead>
                        <tr>
                            <th class="px-4 py-3 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100 rounded-tl rounded-bl">ID</th>
                            <th class="px-4 py-3 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100">献立名</th>
                            <th class="px-4 py-3 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100">献立タイプ</th>
                            <th class="px-4 py-3 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100">編集</th>
                            <th class="px-4 py-3 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100">削除</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td class="px-4 py-3">1</td>
                            <td class="px-4 py-3">ラーメン</td>
                            <td class="px-4 py-3">main</td>
                            <td class="px-4 py-3 text-lg text-gray-900">
                                <button class="flex mr-auto text-white bg-yellow-500 border-0 py-2 px-6 focus:outline-none hover:bg-yellow-600 rounded">編集</button>
                            </td>
                            <td class="px-4 py-3 text-lg text-gray-900">
                                <button class="flex mr-auto text-white bg-red-500 border-0 py-2 px-6 focus:outline-none hover:bg-yellow-600 rounded">削除</button>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </section>

※テーブルの中身はとりあえず仮

2.管理画面トップページのルーティング作成

    //管理画面トップページ
    Route::get('top', [MenuOptionsController::class,'top'])->name('top');

3.コントローラー上で献立一覧を取得

    /**
     * 管理画面トップページ兼献立一覧表示
     **/
    public function top()
    {
        //献立一覧を取得
        $menuoptions = Menu_Options::get();
        // dd($menuoptions);
        return view('admin.top', [
            'menuoptions' => $menuoptions
        ]);
    }

menuoptions変数に全ての献立候補を取得させて格納

その後admin.topを表示

連想配列で、ビューに渡すデータを指定(menuoptionsをビュー内で使用)

4.コントローラで取得した変数をビューで表示させる

                    <tbody>
                        @foreach ($menu_options as $menu_option)
                        <tr>
                            <td class="px-4 py-3">{{$menu_option->id}}</td>
                            <td class="px-4 py-3">{{$menu_option->dish_name}}</td>
                            <td class="px-4 py-3">{{$menu_option->dish_type}}</td>
                            <td class="px-4 py-3 text-lg text-gray-900">
                                <button class="flex mr-auto text-white bg-yellow-500 border-0 py-2 px-6 focus:outline-none hover:bg-yellow-600 rounded">編集</button>
                            </td>
                            <td class="px-4 py-3 text-lg text-gray-900">
                                <button class="flex mr-auto text-white bg-red-500 border-0 py-2 px-6 focus:outline-none hover:bg-red-600 rounded">削除</button>
                            </td>
                        </tr>
                        @endforeach
                    </tbody>

献立候補の編集・削除ボタンから画面遷移するようにする

1.showルートを作成する

        //献立編集画面
        Route::get('{menu_optionId}',[MenuOptionsController::class, 'show'])->name('show');

2.編集ボタンを押した時の挙動をビューから編集する

                                <button
                                    {{-- 献立一覧詳細画面に遷移 --}}
                                    onclick="location.href='{{ route('admin.menu_options.show', ['menu_optionId => $menuoption->id'])}}'"
                                    class="flex mr-auto text-white bg-yellow-500 border-0 py-2 px-6 focus:outline-none hover:bg-yellow-600 rounded">
                                    編集
                                </button>

クリックすると、admin.menu_options.showが開き、menu_optionIdを受け取れる

3.コントローラーにて、menu_option変数にmenuoptionIDを格納し、
その結果をビューのshowファイルに返すように指定。

use Illuminate\Http\Request;

     * 献立詳細画面表示
     */
    public function show(Request $request, int $menu_optionId)
    {
        $menu_option = Menu_Options::findOrFail($menu_optionId);
        return view('admin.menu_options.show', [
            'menu_option' => $menu_option
        ]);

最後のmenu_optionはビュー内で使用するキー。
このキーを指定することで、$menu_optionを扱えるという意味。

4.献立編集画面に、それぞれのIDに沿った献立名と献立タイプを表示させる

<x-admin-layout>
    <section class="text-gray-600 body-font">
        <div class="container px-5 py-24 mx-auto">
            <div class="flex flex-col text-center w-full mb-20">
                <h1 class="sm:text-4xl text-3xl font-medium title-font mb-2 text-gray-900">{{$menu_option->dish_name}}</h1>
                <p class="lg:w-2/3 mx-auto leading-relaxed text-base">
                    {{ $menu_option->dish_type}}
                </p>
            </div>
        </div>
    </section>
</x-admin-layout>

献立編集画面から、入力した献立名と献立タイプに編集する

1.献立編集画面ににformメソッドを取り入れる

kondate_portfolio/resources/views/admin/menu_options/edit.blade.php

            <form method="POST" action="{{ route('admin.menu_options.update', ['menu_optionId' => $menu_option->id])}}">

2.updateのルーティング設定を入れる

        //献立更新処理
        Route::POST('{menu_optionId}/update',[MenuOptionsController::class, 'update'])->name('update');

3.コントローラのupdateメソッドを編集する

    /**
     * 献立更新処理
     */
    public function update(UpdateMenu_OptionsRequest $request, int $menu_optionId)
    {
        // dd($request);
        $menu_options = Menu_Options::findOrFail($menu_optionId);
        $menu_options->dish_name = $request->name;
        $menu_options->dish_type = $request->type;
        $menu_options->user_id   = auth()->id(); // ユーザーIDを設定
        $menu_options->save();
            return redirect()->route('admin.top', ['menu_options' => $menu_options])->with('success', '献立候補の更新が完了しました。');
    }

これにて献立編集画面から献立の編集が可能になる。

献立候補削除機能実装

1.献立候補一覧画面から、削除ボタンを押した時の挙動を設定

                                <form method="POST" action="{{ route('admin.menu_options.destroy', ['menu_optionId' => $menu_option->id])}}">
                                    @csrf
                                    <td class="px-4 py-3 text-lg text-gray-900">
                                        <button type="submit" class="flex mr-auto text-white bg-red-500 border-0 py-2 px-6 focus:outline-none hover:bg-red-600 rounded">
                                            削除
                                        </button>
                                    </td>
                                </form>

2.削除機能のルーティング設定

        //献立削除処理
        Route::post('{menu_optionId}/destroy',[MenuOptionsController::class, 'destroy'])->name('destroy');

3.削除機能のコントローラ設定

    /**
     * 献立削除処理
     */
    public function destroy(Request $request, int $menu_optionId)
    {
        // dd($menu_optionId, $request);
        $menu_options = Menu_Options::findOrFail($menu_optionId);
        $menu_options->delete();
        return redirect()->route('admin.top')->with('success', '献立候補の削除が完了しました。');
    }

これで削除ボタンを押すと、データベースから該当の献立候補が削除される

来週以降の進め方

来週からユーザー画面の作成に入っていきます。
ユーザー画面は表示内容が非常に多いので、実装が大変そうでもありワクワクもします!

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

今週使用した学習教材

【Laravel11】クイズアプリを作りながら自力でアプリ開発する力を身につけよう【要件定義・設計〜プログラミングまで】(Udemy)

Discussion