Open15

【挑戦】Laravel 9 でポートフォリオを作ってみた

DaiNakaDaiNaka

はじめに

前回の作ってみたシリーズから随分と時間が経ってしまいましたがやっていきたいと思います。
今回は、Laravelの基本の部分を復習を兼ねてやってみたいと思います。
ポートフォリオの作成なので、コントローラーやモデルはあまり深くはやらず、主にビューの部分をやっていきたいと思っています。

第一弾のチャットアプリ

https://zenn.dev/dainaka/scraps/6eafb7c86dad16

第二弾の掲示板アプリ

https://zenn.dev/dainaka/scraps/14be8378905118

第三弾のメモアプリ ※完成に至らず

https://zenn.dev/dainaka/scraps/3c61ef4057452c

開発環境

  • XAMPP v3.3.0
  • composer 2.1.3
  • VS Code

利用言語

  • PHP 8.1.1
  • Laravel 9
DaiNakaDaiNaka

企画

今回は、Laravelを使ってポートフォリオを作っていきたいと思います。
コントローラーやモデルを使った、Webシステムというよりは、主にビュー周りの勉強を兼ねた感じで進めていきたいと思っているので、流れ的には下記の様な感じです。

  • トップページの作成
  • アバウトページの作成
  • 作品ページの作成
  • お問い合わせフォームの作成

各ページを作った後、共通部分をコンポーネント化する等、苦手なブレード部分を学習していきたいと考えています。

DaiNakaDaiNaka

プロジェクトの作成

早速、プロジェクトを作成していきます。
laravel new Portfolio --git --branch="main"

次に、GitHub上に作成しておいたリポジトリと連携させていきます。
git remote add origin https://github.com/DaiNaka1207/Portfolio_laravel.git

最後に、GitHub上に初期状態をプッシュしていきます。
git push -u origin main

DaiNakaDaiNaka

環境設定

.env
- APP_NAME=laravel
+ APP_NAME="DaiNaka's Portfolio"
config/app.php
- 'timezone' => 'UTC',
+ 'timezone' => 'Asia/Tokyo',

- 'locale' => 'en',
+ 'locale' => 'ja',

- 'faker_locale' => 'en_US',
+ 'faker_locale' => 'ja_JP',
DaiNakaDaiNaka

Laravel起動

コマンドプロンプトにて下記コマンドを実行して、Laravelのサーバーを起動します。
php artisan serve

そして、ブラウザにて下記へアクセスして、起動している事を確認します。
http://localhost:8000/

無事にWelcome画面が表示された事を確認

DaiNakaDaiNaka

トップページの新規作成

resources\views内にトップページの新規にブレードビューを作成します。
中身はシンプルに下記の様な感じにしました。

top.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <h1>DaiNaka's Portfolio</h1>
    <p>ポートフォリオへようこそ。</p>
</body>
</html>

ルート設定

routes\web.phpを下記の通り変更します。

web.php
Route::get('/', function () {
-    return view('welcome');
+    return view('top');
});

サイトタイトルの変更

ここでは、サイトタイトルをLaravelのコンフィグから拾ってくる様にします。
サイトタイトルの変更はほとんどないかもしれませんが、もし変更になった時に、全ての記述を変更する必要がなくなる為、コンフィグから拾ってくる様にします。

top.blade.php
 <body>
-      <h1>DaiNaka's Portfolio</h1>
+     <h1>{{config('app.name')}}</h1>
       <p>ポートフォリオへようこそ。</p>
 </body>

先程変更した.envファイルのAPP_NAME部分を拾ってきます。

DaiNakaDaiNaka

CSSの適用

ポートフォリオなので、今回はCSSはtailwindcssを使わず、自身で書いていきたいと思います。
私も含め、Laravel初学者はCSSや画像等のファイルをどこに保存すれば良いかが分からないかと思います。
調べてみると出てきますが、publicフォルダの配下に置く事になっていますので、ご参考ください。

publicフォルダへSCSSの設置

public内にcss作成して、その中にstyle.scssを作成して保管しています。

style.scss
@charset "UTF-8";

.blue {
    color: blue;
}

.plum {
    color: plum;
}
top.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
+   <link rel="stylesheet" href="css\style.css">
</head>
<body>
-   <h1>{{config('app.name')}}</h1>
+   <h1 class="blue">{{config('app.name')}}</h1>
-   <p>ポートフォリオへようこそ。</p>
+   <p class="plum">ポートフォリオへようこそ。</p>
</body>
</html>

CSSが反映されている事を確認できました。

下準備

トップページを作成していく前に、リセットCSSやフォント変更等を先に行っておきます。

top.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <!-- Site Info -->
    <title>{{config('app.name')}}</title>
    
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/destyle.css@3.0.2/destyle.min.css">
    <link rel="stylesheet" href="/css/style.css">
    
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Klee+One:wght@400;600&display=swap" rel="stylesheet">
    
</head>
<body>
    <h1 class="blue">{{config('app.name')}}</h1>
    <p class="plum">ポートフォリオへようこそ。</p>
</body>
</html>
style.scss
@charset "UTF-8";

/* Common
-------------------------------------------------- */
body {
    font-family: 'Klee One', cursive;
}

現時点の画面イメージ

DaiNakaDaiNaka

トップページの完成

とてもシンプルですが、トップページは下記の様な仕上がりにしてみました。

top.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <!-- Site Info -->
    <title>{{config('app.name')}}</title>
    
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/destyle.css@3.0.2/destyle.min.css">
    <link rel="stylesheet" href="/css/style.css">
    
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Klee+One:wght@400;600&display=swap" rel="stylesheet">

</head>
<body class="container">
    <header id="header">
        <h1>{{config('app.name')}}</h1>
        <ul id="global-menu">
            <li><a href="#">about</a></li>
            <li><a href="#">works</a></li>
            <li><a href="#">contact</a></li>
        </ul>
    </header>

    <img src="./img/top-image.jpg" alt="top-image.jpg">

    <main id="top">
        <h2>ようこそ、ポートフォリオへ</h2>
        <p>
            当サイトでは私の作品を掲載しています。<br>
            仕事のご依頼や当サイトに関するお問い合わせは<a href="#" class="contact-btn">contact</a>よりご連絡ください。
        </p>
    </main>

    <footer id="footer">
        <small>&copy; 2022 DaiNaka</small>
    </footer>
</body>
</html>
style.scss
@charset "UTF-8";

/* Common
-------------------------------------------------- */
body {font-family: 'Klee One', cursive;}

img {width: 100%;}

h1 {font-size: 1.50rem;}
h2 {font-size: 1.25rem;}

.container {
    margin: 0 10%;
}

main {
    margin: 20px 0;
    
    h2, &>p {
        margin-bottom: 20px;
    }
} // main

#footer {
    border-top: solid 1px #000;
    display: flex;
    justify-content: center;
    padding: 20px 0;
}

/* GlobalMenu
-------------------------------------------------- */
#header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin: 10px 0;
    
    /* [Mobile]
    ------------------------------------------ */
    @media (max-width: 600px) {
        flex-direction: column;
        align-items: center;
        gap: 15px;
        margin: 20px 0 30px;
    }

    #global-menu {
        display: flex;
        gap: 30px;
        text-transform: capitalize;
    }
} // #header

/* TopPage
-------------------------------------------------- */
#top {
    .contact-btn {
        text-decoration: underline;
        text-transform: capitalize;
    }
}

画面イメージ(デスクトップ)

画面イメージ(モバイル)

DaiNakaDaiNaka

アバウトページの作成

基本的には、ヘッダーやフッターは同じなのでtop.blade.phpをコピーして使いまわします。

アバウトページを簡単に記述

変更点としては下記の通りです。

  • main自体のIDをtopからaboutへ変更
  • mainの中身を適当に変更
  • タイトルを変更
about.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <!-- Site Info -->
    <title>about | {{config('app.name')}}</title>
    
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/destyle.css@3.0.2/destyle.min.css">
    <link rel="stylesheet" href="/css/style.css">
    
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Klee+One:wght@400;600&display=swap" rel="stylesheet">

</head>
<body class="container">
    <header id="header">
        <h1>{{config('app.name')}}</h1>
        <ul id="global-menu">
            <li><a href="#">about</a></li>
            <li><a href="#">works</a></li>
            <li><a href="#">contact</a></li>
        </ul>
    </header>

    <main id="about">
        <h2>アバウトページ</h2>
        <p>当サイトは、DaiNakaがLaravelを勉強していく中で簡単なポートフォリオを作成したいと思い作成しました。</p>

        <article id="profile">
            <img src="/img/profile.svg" alt="profile.svg">

            <h2 class="name">@DaiNaka</h2>
            <ul>
                <li>好き:ハヤシライス、甘い物、スマホゲーム</li>
                <li>趣味:フットサル、プログラミング</li>
                <li>仕事:社内SE・ヘルプデスク・サポートデスク</li>
            </ul>
        </article>
    </main>

    <footer id="footer">
        <small>&copy; 2022 DaiNaka</small>
    </footer>
</body>
</html>
style.scss
/* AboutPage
-------------------------------------------------- */
#profile {
    display: flex;
    flex-direction: column;
    align-items: center;

    img {
        width: 150px;
        border-radius: 50%;
    }
}

ルート設定

web.phpへ下記を記述してページへのリンクを設定します。

web.php
Route::get('/about', function () {
    return view('about');
});

画面イメージ(デスクトップ)

アバウトページが読み込めるかテストする為に、ブラウザで下記のリンクへアクセスします。
http://localhost:8000/about

画面イメージ(モバイル)

DaiNakaDaiNaka

ブレードテンプレートの機能を活用

ここまでくると、トップページとアバウトページで共通部分が散見されます。
また、共通部分の修正が発生した際に、全ページの修正しなければならず、非効率です。
そこで、レイアウト機能を活用して、ヘッダーやフッターの共通部分をコンポーネント化していきたいと思います。

レイアウト用ファイルを作成

resouses\viewsの中にlayoutsフォルダを作成して、その中にapp.blade.phpとして保存します。
top.blade.phpabout.blade.phpからコピーしてくると楽に作業できます。

ここには共通部分を書いておき、異なる部分を@yield('***')とします。
ついでにタイトル部分も変更ができる様にしておきます。

app.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <!-- Site Info -->
    @yield('title')
    
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/destyle.css@3.0.2/destyle.min.css">
    <link rel="stylesheet" href="/css/style.css">
    
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Klee+One:wght@400;600&display=swap" rel="stylesheet">

</head>
<body class="container">
    <header id="header">
        <h1>{{config('app.name')}}</h1>
        <ul id="global-menu">
            <li><a href="#">about</a></li>
            <li><a href="#">works</a></li>
            <li><a href="#">contact</a></li>
        </ul>
    </header>

    @yield('content')

    <footer id="footer">
        <small>&copy; 2022 DaiNaka</small>
    </footer>
</body>
</html>

トップページの編集

top.blade.php
@extends('layouts.app')

@section('title')
    <title>{{config('app.name')}}</title>
@endsection

@section('content')

    <img src="./img/top-image.jpg" alt="top-image.jpg">

    <main id="top">
        <h2>ようこそ、ポートフォリオへ</h2>
        <p>
            当サイトでは私の作品を掲載しています。<br>
            仕事のご依頼や当サイトに関するお問い合わせは<a href="#" class="contact-btn">contact</a>よりご連絡ください。
        </p>
    </main>

@endsection

アバウトページの編集

about.blade.php
@extends('layouts.app')

@section('title')
    <title>about | {{config('app.name')}}</title>
@endsection

@section('content')
    <main id="about">
        <h2>アバウトページ</h2>
        <p class="about-note">当サイトは、DaiNakaがLaravelを勉強していく中で簡単なポートフォリオを作成したいと思い作成しました。</p>

        <article id="profile">
            <img src="/img/profile.svg" alt="profile.svg">

            <h2 class="name">@DaiNaka</h2>
            <ul>
                <li>好き:ハヤシライス、甘い物、スマホゲーム</li>
                <li>趣味:フットサル、プログラミング</li>
                <li>仕事:社内SE・ヘルプデスク・サポートデスク</li>
            </ul>
        </article>
    </main>
@endsection

ここまでくると、大分すっきりしました。
ついでに、グローバルメニューからアバウトページに行ける様に変更します。

app.blade.php
    <header id="header">
-       <h1>{{config('app.name')}}</h1>
+       <h1><a href="/">{{config('app.name')}}</a></h1>
        <ul id="global-menu">
-           <li><a href="#">about</a></li>
+           <li><a href="/about">about</a></li>
            <li><a href="#">works</a></li>
            <li><a href="#">contact</a></li>
        </ul>
    </header>
DaiNakaDaiNaka

一部修正

大画面にすると画像が大きくなり過ぎて見栄えが悪くなる為、一部CSSを修正

style.scss
.container {
    margin: 0 auto;
+   max-width: 800px;
+
+   /* [Mobile]
+   ------------------------------------------ */
+   @media (max-width: 1000px) {
+       margin: 0 10%;
+   }
}
DaiNakaDaiNaka

ワークスページの作成

アバウトページと同様に、他のビューをコピーして使いまわします。
今回は、アバウトページをコピーしてリネームしました。

内容を編集

変更点としては下記の通りです。

  • @section('title')の中身を変更
  • @section('content')の中身を変更
works.blade.php
@extends('layouts.app')

@section('title')
    <title>Works | {{config('app.name')}}</title>
@endsection

@section('content')
    <main id="works">
        <h2>ワークスページ</h2>
        <p>このページでは、私がこれまでに作成してきたものを掲載しています。</p>

        <article>
            <section>
                <h3>Grim Soul/グリムソウル | 攻略プレイ日記</h3>
                <ul>
                    <li>
                        <a href="https://grimsoul-clear.com" target="_blank">
                            <img src="/img/GrimSoul.png" alt="GrimSoul.png">
                            https://grimsoul-clear.com
                        </a>
                    </li>
                    <li><p>私がはまっていて、ずっとプレイしているスマホゲームです。<br>界隈では、めちゃくちゃマゾいと有名なサバイバルゲームです。</p></li>
                </ul>
            </section>

            <section>
                <h3>DailyReport</h3>
                <ul>
                    <li>
                        <a href="https://daily-report.dainaka.live" target="_blank">
                            <img src="/img/DailyReport.png" alt="DailyReport.png">
                            https://daily-report.dainaka.live
                        </a>
                    </li>
                    <li><p>日々の作業内容や振り返り、体温を記録する事ができるサービスです。</p></li>
                </ul>
            </section>

            <section>
                <h3>Chat-App</h3>
                <ul>
                    <li>
                        <a href="https://chat-app.dainaka.live" target="_blank">
                            <img src="/img/ChatApp.png" alt="ChatApp.png">
                            https://chat-app.dainaka.live
                        </a>
                    </li>
                    <li><p>PHP/Laravelの勉強に作成したチャットサービスです。</p></li>
                </ul>
            </section>

            <section>
                <h3>BBS-App</h3>
                <ul>
                    <li>
                        <a href="https://bbs-app.dainaka.live" target="_blank">
                            <img src="/img/BBS-App.png" alt="BBS-App.png">
                            https://bbs-app.dainaka.live
                        </a>
                    </li>
                    <li><p>PHP/Laravelの勉強に作成した昔ながらの掲示板サービスです。</p></li>
                </ul>
            </section>
        </article>
    </main>
@endsection
style.scss
/* Common
-------------------------------------------------- */
h3 {font-weight: 600;}

/* WorksPage
-------------------------------------------------- */
#works {
    article {
        section {
            display: flex;
            flex-direction: column;
            gap: 10px;
            margin-bottom: 25px;

            p {
                font-size: 0.90rem;
            }

            a {
                display: flex;
                flex-direction: column;
                width: max-content;
            }

            img {
                width: max-content;
            }
        } //section
    } //article
} //#works

ルート設定

web.phpへ下記を記述してページへのリンクを設定します。

web.php
Route::get('/works', function () {
    return view('works');
});

画面イメージ

ワークスページが読み込めるかテストする為に、ブラウザで下記のリンクへアクセスします。
http://localhost:8000/works

  • デスクトップ

  • モバイル

グローバルナビのリンクを修正

app.blade.php
    <header id="header">
        <h1><a href="/">{{config('app.name')}}</a></h1>
        <ul id="global-menu">
            <li><a href="/about">about</a></li>
-           <li><a href="#">works</a></li>
+           <li><a href="/works">works</a></li>
            <li><a href="#">contact</a></li>
        </ul>
    </header>
DaiNakaDaiNaka

お問い合わせページの作成

ここでも、アバウトページやワークスページ同様に、他のビューをコピーして使いまわします。
今回は、アバウトページをコピーしてリネームしました。
レイアウト機能を活用すると編集がとても楽になります。

内容を編集

変更点としては下記の通りです。

  • @section('title')の中身を変更
  • @section('content')の中身を変更
contact.blade.php
@extends('layouts.app')

@section('title')
    <title>Contact | {{config('app.name')}}</title>
@endsection

@section('content')
    <main id="contact">
        <h2>お問い合わせフォーム</h2>
        <p>当サイトに関する事や、お仕事のご依頼はこちらからご連絡ください。</p>

        <form action="#" method="post">
            @csrf
            <ul>
                <li>
                    <label for="name">お名前</label>
                    <input type="text" name="name" id="name" placeholder="DaiNaka" required>
                </li>
                <li>
                    <label for="email">メール</label>
                    <input type="email" name="email" id="email" placeholder="example@mail.com" required>
                </li>
                <li>
                    <label for="text">内容</label>
                    <textarea name="text" id="text" rows="6" placeholder="こちらにお問い合わせ内容をご入力ください" required></textarea>
                </li>
            </ul>
            <button type="submit">送信</button>
        </form>
    </main>
@endsection
style.scss
/* ContactPage
-------------------------------------------------- */
#contact {
    li {
        display: flex;
        flex-direction: column;
        margin-bottom: 10px;

        label {
            margin-bottom: 3px;
        }
    } // li

    input, textarea {
        background-color: rgba(200, 200, 200, 0.3);
        padding: 5px;
        resize: vertical;

        &:focus {
            outline: solid 2px rgba(200, 200, 200, 1.0);
        }
    }

    button {
        display: block;
        margin-left: auto;
        background-color: #777;
        color: #fff;
        padding: 8px 15px;
    }
} // #contact

ルート設定

web.phpへ下記を記述してページへのリンクを設定します。

web.php
Route::get('/contact', function () {
    return view('contact');
});

画面イメージ

お問い合わせページが読み込めるかテストする為に、ブラウザで下記のリンクへアクセスします。
http://localhost:8000/contact

  • デスクトップ

  • モバイル

グローバルナビのリンクを修正

app.blade.php
    <header id="header">
        <h1><a href="/">{{config('app.name')}}</a></h1>
        <ul id="global-menu">
            <li><a href="/about">about</a></li>
            <li><a href="/works">works</a></li>
-           <li><a href="#">contact</a></li>
+           <li><a href="/contact">contact</a></li>
        </ul>
    </header>

トップページに記載しておいた、問い合わせへのリンクも変更しておきます。

top.blade.php
    <main id="top">
        <h2>ようこそ、ポートフォリオへ</h2>
        <p>
            当サイトでは私の作品を掲載しています。<br>
-           仕事のご依頼や当サイトに関するお問い合わせは<a href="#" class="contact-btn">contact</a>よりご連絡ください。
+           仕事のご依頼や当サイトに関するお問い合わせは<a href="/contact" class="contact-btn">contact</a>よりご連絡ください。
        </p>
    </main>
DaiNakaDaiNaka

リンク先への名前の追加

ここではweb.phpで設定ができる、リンク先へ名前を追加する方法を紹介したいと思います。

何が出来るのか

例えばお問い合わせページのアドレス/contactを変更する必要が出てくると、トップページのaタグと、グローバルナビのリンクを修正する必要が出てきます。
しかし、リンク先に名前を付けておいて、各ページのリンク先は名前で指定しておく事によって、web.phpのリンク先だけ変更する事で済む様になります。

やり方

それでは、実際にやっていきましょう。
web.phpを下記の様に変更していきます。

web.php
Route::get('/', function () {
    return view('top');
})->name('top');

Route::get('/about', function () {
    return view('about');
})->name('about');

Route::get('/works', function () {
    return view('works');
})->name('works');

Route::get('/contact', function () {
    return view('contact');
})->name('contact');

各ルートの後ろに->name('***')を追加するだけです。
次にapp.blade.phpを変更していきます。

app.blade.php
    <header id="header">
        <h1><a href="{{route('top')}}">{{config('app.name')}}</a></h1>
        <ul id="global-menu">
-           <li><a href="/about">about</a></li>
+           <li><a href="{{route('about')}}">about</a></li>
-           <li><a href="/works">works</a></li>
+           <li><a href="{{route('works')}}">works</a></li>
-           <li><a href="/contact">contact</a></li>
+           <li><a href="{{route('contact')}}">contact</a></li>
        </ul>
    </header>

ここでは、リンク先に{{route('***')}}としています。
これは、web.phpで付けた名前を***に入れています。