作成するアプリ
Laravelで基本のCRUD操作ができるようになったので、いよいよ本格的なアプリを作成していきましょう。
今回作成するアプリは、「タスク管理アプリ」です。
アプリ作成準備
さっそくLaravel教材で作成したfirst-app
と同じLaravel
ディレクトリにsailコマンド
でプロジェクトを作成していきましょう。
ターミナルで下記コマンドをLaravel
ディレクトリ上で実行してください。
Laravelプロジェクト作成
$ curl -s "https://laravel.build/task-app" | bash
実行が終わったらsail upコマンド
で起動させましょう。
ターミナルで下記コマンドをLaravel
ディレクトリ上で実行してください。
sail up
$ cd task-app && ./vendor/bin/sail up -d
これでタスク管理アプリのプロジェクト作成が完了しました。
phpMyAdminインストール
次にphpMyAdminのインストールをしていきましょう。
task-app/docker-compose.yml
を下記の通り編集してください。
# For more information: https://laravel.com/docs/sail
version: '3'
services:
# --- ここから追加 ---
phpmyadmin:
image: phpmyadmin/phpmyadmin
links:
- mysql:mysql
ports:
- 8888:80
environment:
PMA_HOST: mysql
networks:
- sail
# --- ここまで追加 ---
laravel.test:
# --- 以下省略 ---
編集が完了したら、一旦開発環境をオフにします。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail stop
もう一度起動させるので、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail up -d
これでphpMyAdminがインストールできたので、さっそくhttp://localhost:8888へアクセスして下記画像のような画面が表示されればOKです。
データベースにアクセスするためのユーザー名
とパスワードはtask-app/.env
に記述されているので確認してみましょう。
# --- 以上省略 ---
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=task_app
DB_USERNAME=sail
DB_PASSWORD=password
# --- 以下省略 ---
DB_〇〇
となっているところがデータベースに関する情報です。
データベース名がtask_app
でユーザー名がsail
、パスワードがpassword
となります。
phpMyAdminで上記のユーザー名とパスワードを入力してログインしてください。
task_app
というデータベースが作成されていればOKです。
初期設定
Laravelをインストールした直後にしておく初期設定をしておきましょう。
初期設定で行うことは下記の4つです。
- アプリケーション名変更
- タイムゾーン変更
- 言語設定
- 日本語ファイルダウンロード
アプリケーション名変更
それでは、アプリケーション名を変更するためにtask-app/.env
を下記の通り編集してください。
# APP_NAME=Laravelから下記に変更
APP_NAME="タスク管理アプリ"
APP_ENV=local
# --- 以下省略 ---
タイムゾーン・言語設定・日本語ファイルダウンロード
次はタイムゾーン・言語設定・日本語ファイルダウンロードを一気に行っていきます。
task-app/config/app.php
を下記の通り編集してください。
// 'timezone' => 'UTC'から下記に変更(72行目付近)
'timezone' => 'Asia/Tokyo',
// 'locale' => 'en'から下記に変更(85行目付近)
'locale' => 'ja',
// 'faker_locale' => 'en_US'から下記に変更(111行目付近)
'faker_locale' => 'ja_JP',
編集箇所はLaravel教材の時と全く同じです。
最後にアプリケーション名と、タイムゾーン、言語設定の変更をアプリケーションに反映させるためにターミナルにて、下記コマンドをtask-app
ディレクトリ上で実行してください
$ ./vendor/bin/sail php artisan cache:clear && ./vendor/bin/sail php artisan config:clear
これでアプリケーション名とタイムゾーン、言語設定を変更することができました。
最後に日本語ファイルのダウンロードを行っておきましょう。
こちらはドキュメントにも載っていますが、下記3コマンドで完了します。
さっそくターミナルにて、下記コマンドをtask-app
ディレクトリ上で実行してください
$ ./vendor/bin/sail php -r "copy('https://readouble.com/laravel/8.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
$ ./vendor/bin/sail php -f install-ja-lang.php
$ ./vendor/bin/sail php -r "unlink('install-ja-lang.php');"
コマンド実行後、task-app/resources/lang/ja
ディレクトリが作成されていればOKです。
これで開発の準備は整ったので、さっそく開発していきましょう。
ログイン機能実装
まずはログイン機能を実装していきます。
Laravelにはログイン機能を実装するためにLaravel Breeze
が提供されています。
このLaravel Breezeを使用すると、たった数コマンドで認証機能が実装できてしまいます。
新規登録機能やログイン機能、ログアウト機能は認証機能と言われるので、覚えておきましょう。
それではさっそくLaravel Breezeを導入していきましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail composer require laravel/breeze
このコマンドでLaravel Breezeに関するデータがインストールされたので、次はそれらのデータをLaravelに反映させていきましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan breeze:install
コマンドの実行が終わると様々なファイルが追加されています。
追加されているファイルを覚える必要はありませんが、下記のようなファイルが追加されています。
app/Http/Controllers/Auth/AuthenticatedSessionController.php
app/Http/Controllers/Auth/ConfirmablePasswordController.php
app/Http/Controllers/Auth/EmailVerificationNotificationController.php
app/Http/Controllers/Auth/EmailVerificationPromptController.php
app/Http/Controllers/Auth/NewPasswordController.php
app/Http/Controllers/Auth/PasswordResetLinkController.php
app/Http/Controllers/Auth/RegisteredUserController.php
app/Http/Controllers/Auth/VerifyEmailController.php
app/Http/Requests/Auth/LoginRequest.php
app/View/Components/AppLayout.php
app/View/Components/GuestLayout.php
resources/views/auth/confirm-password.blade.php
resources/views/auth/forgot-password.blade.php
resources/views/auth/login.blade.php
resources/views/auth/register.blade.php
resources/views/auth/reset-password.blade.php
resources/views/auth/verify-email.blade.php
resources/views/components/application-logo.blade.php
resources/views/components/auth-card.blade.php
resources/views/components/auth-session-status.blade.php
resources/views/components/auth-validation-errors.blade.php
resources/views/components/button.blade.php
resources/views/components/dropdown-link.blade.php
resources/views/components/dropdown.blade.php
resources/views/components/input.blade.php
resources/views/components/label.blade.php
resources/views/components/nav-link.blade.php
resources/views/components/responsive-nav-link.blade.php
resources/views/dashboard.blade.php
resources/views/layouts/app.blade.php
resources/views/layouts/guest.blade.php
resources/views/layouts/navigation.blade.php
routes/auth.php
tailwind.config.js
tests/Feature/AuthenticationTest.php
tests/Feature/EmailVerificationTest.php
tests/Feature/PasswordConfirmationTest.php
tests/Feature/PasswordResetTest.php
tests/Feature/RegistrationTest.php
ファイル名や拡張子を確認すると、コントローラーやビューなどのファイルがインストールされていることがわかるかと思います。
次にターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail npm install && ./vendor/bin/sail npm run dev
実行に時間がかかることもありますが、このコマンドを実行することで認証に関する画面が無事に表示できるようになります。
試しにhttp://localhostへアクセスしてみると、画面右上にLoginボタン
とRegisterボタン
が表示されます。
Loginボタンをクリックすると、下記のようにログイン画面が表示されます。
Registerボタンをクリックすると、下記のように新規登録画面が表示されます。
今のままだと画面は表示できるのですが、ユーザー情報を登録するテーブルが作成されていないので、新規登録やログインをすることはできません。
そのため、最後に認証に関するusersテーブル
などを作成するためにマイグレーションを行いましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan migrate
Laravel教材では削除したデフォルトのマイグレーションファイルが実行され、認証用のテーブルが作成されます。
実際に作成されるテーブルはfailed_jobsテーブル
、 migrationsテーブル
、password_resetsテーブル
、personal_access_tokensテーブル
、usersテーブル
の5つが作成されます。
今回使用するのはusersテーブル
です。
ユーザー作成
以上でLaravel Breezeの導入は終了ですが、新規登録やログインができることを確認しておきましょう。
http://localhost/registerへアクセスして、新規登録を行いましょう。
登録するデータは下記の通りです。
ラベル | データ |
---|---|
Name | fdsa |
fdsa@fdsa.fdsa | |
Password | fdsafdsa |
Confirm Password | fdsafdsa |
無事に登録が完了すれば、下記画面へリダイレクトされます。
右上のユーザー名fdsa
をクリックするとLog Outボタン
が出てくるのでログアウトしましょう。
http://localhost/loginへアクセスして、ログインを行いましょう。
ログイン情報は下記の通りです。
ラベル | データ |
---|---|
fdsa@fdsa.fdsa | |
Password | fdsafdsa |
ログインが無事に完了すれば、下記画面へリダイレクトされます。
これで新規登録とログイン、ログアウトの動作確認が完了しました。
テストデータ作成
次にユーザーのテストデータを作成しておきましょう。
テストデータの作成はLaravel教材の時と全く同じです。
まずはシーダーファイルを作成しましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:seeder UsersTableSeeder
作成したtask-app/database/seeders/UsersTableSeeder.php
を下記の通り編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; // ここを追加
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('users')->insert([
'name' => 'test',
'email' => 'test@test.test',
'password' => bcrypt('testtest'),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
Laravelでは、ユーザーのパスワード登録時にセキュリティの観点から必ず暗号化して登録します。
暗号化のためのヘルパーメソッドであるbcryptメソッド
も用意されており、簡単に暗号化することができます。
Laravelでユーザーのテストデータを作る際には、パスワードを暗号化することを覚えておきましょう。
次にtask-app/database/seeders/DatabaseSeeder.php
を下記の通り編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call([
UsersTableSeeder::class,
]);
}
}
これでusersテーブル
のテストデータを作成する準備が整いました。
それでは、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan db:seed
コマンドの実行が完了すると、ユーザー名test
というテストデータが作成されます。
念の為、皆さんはphpMyAdminなどで確認しておきましょう。
現在usersテーブル
には新規登録画面から登録したユーザー名fdsa
のデータとシーダーで登録したユーザー名test
の2つのユーザーデータが存在してることになります。
どちらのユーザーでもログインができるかなどは確かめておきましょう。
ID | ユーザー名 | メールアドレス | パスワード |
---|---|---|---|
1 | fdsa | fdsa@fdsa.fdsa | fdsafdsa |
2 | test | test@test.test | testtest |
・新規登録
http://localhost/registerアクセス
・ログイン
ログアウト後、http://localhost/loginへアクセス
各種テンプレート作成
次はビューの元になる見た目のテンプレートを作成してきましょう。
テンプレート作成
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ touch resources/views/layouts/layout.blade.php
task-app/resources/views/layouts
ディレクトリにlayout.blade.php
が作成されるので、下記の通り編集してください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>@yield('title', 'タスク管理')</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
<!-- CSS -->
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
</head>
<body>
<!-- ヘッダー -->
@include('layouts.header')
<!-- コンテンツ -->
<main>
@yield('content')
</main>
<!-- フッター -->
@include('layouts.footer')
<!-- Bootstrap -->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
Laravel教材の時と同じように、簡単あに見た目を整えるためにBootstrapをCDNで使用しています。
また、後ほど作成するCSSを読み込むための<link rel="stylesheet" href="{{ asset('css/style.css') }}">
という記述もしています。
こちらについては、CSS導入時に説明します。
タイトルやヘッダー、コンテンツ、フッターなどは@yield
や@include
を使用しているので、この後作成していきます。
ヘッダー作成
次に共通ヘッダーを作成します。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ touch resources/views/layouts/header.blade.php
task-app/resources/views/layouts
ディレクトリにheader.blade.php
が作成されるので、下記の通り編集してください。
<header>
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #1f1f1f;">
<div class="container-fluid">
<a class="navbar-brand" href="#">タスク管理</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup">
<div class="navbar-nav">
@auth
<a class="mr-lg-3 my-lg-0 my-3 btn btn-sm btn-dark text-light nav-item nav-link" href="#">ユーザーA</a>
<form action="{{ route('logout') }}" method="POST">
@csrf
<button class="btn btn-sm btn-danger text-light nav-item nav-link w-100">ログアウト</button>
</form>
@else
<a class="mr-lg-3 my-lg-0 my-3 btn btn-sm btn-dark text-light nav-item nav-link" href="{{ route('login') }}">ログイン</a>
<a class="btn btn-sm btn-primary text-light nav-item nav-link" href="{{ route('register') }}">新規登録</a>
@endauth
</div>
</div>
</div>
</nav>
</header>
ヘッダーには必ず左上にアプリ名であるタスク管理
というリンクを設定しておきます。
また、右上には未ログインユーザーかログインユーザーかで出し分けるボタンを設置します。
未ログインの場合はログインボタン
と新規登録ボタン
を右上に表示します。
一方でログイン済みの場合はユーザー名
とログアウトボタン
を表示します。
Bladeテンプレートでは@auth
と記述することでログイン済みの時の処理を記述することができます。
@authで囲まれている部分は下記のようにユーザー名
とログアウトボタン
を記述しています。
@auth
<a class="mr-lg-3 my-lg-0 my-3 btn btn-sm btn-dark text-light nav-item nav-link" href="#">ユーザーA</a>
<form action="{{ route('logout') }}" method="POST">
@csrf
<button class="btn btn-sm btn-danger text-light nav-item nav-link w-100">ログアウト</button>
</form>
@else
ログアウトボタン
はPOST送信のためform要素
として処理しています。
formを使用する際は@csrf
を忘れないようにしましょう。
また、action属性
の属性値に{{ route('logout') }}
と記述されていますが、これはLaravel Breezeで導入した場合のログアウト用のルート名がlogout
だからです。
ちなみにLaravelでは現在設定されているルートをコマンドで確認することができます。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan route:list
コマンドを実行すると下記のような実行結果が表示されます。
GET|HEAD / ............................................................................................................
POST _ignition/execute-solution ..... ignition.executeSolution › Spatie\LaravelIgnition › ExecuteSolutionController
GET|HEAD _ignition/health-check ................. ignition.healthCheck › Spatie\LaravelIgnition › HealthCheckController
POST _ignition/update-config .............. ignition.updateConfig › Spatie\LaravelIgnition › UpdateConfigController
GET|HEAD api/user .....................................................................................................
GET|HEAD confirm-password .................................. password.confirm › Auth\ConfirmablePasswordController@show
POST confirm-password .................................................... Auth\ConfirmablePasswordController@store
GET|HEAD dashboard .......................................................................................... dashboard
POST email/verification-notification ....... verification.send › Auth\EmailVerificationNotificationController@store
GET|HEAD forgot-password ................................... password.request › Auth\PasswordResetLinkController@create
POST forgot-password ...................................... password.email › Auth\PasswordResetLinkController@store
GET|HEAD login ..................................................... login › Auth\AuthenticatedSessionController@create
POST login .............................................................. Auth\AuthenticatedSessionController@store
POST logout .................................................. logout › Auth\AuthenticatedSessionController@destroy
GET|HEAD register ..................................................... register › Auth\RegisteredUserController@create
POST register ................................................................. Auth\RegisteredUserController@store
POST reset-password ............................................ password.update › Auth\NewPasswordController@store
GET|HEAD reset-password/{token} .................................... password.reset › Auth\NewPasswordController@create
GET|HEAD sanctum/csrf-cookie .............................................. Laravel\Sanctum › CsrfCookieController@show
GET|HEAD verify-email ........................... verification.notice › Auth\EmailVerificationPromptController@__invoke
GET|HEAD verify-email/{id}/{hash} ........................... verification.verify › Auth\VerifyEmailController@__invoke
Showing [21] routes
これが現在ルーティングされているルート一覧です。
ログアウトに関するルーティングは下記の通りです。
POST logout .................................................. logout › Auth\AuthenticatedSessionController@destroy
この一文を読み解くと、POST通信でhttp://localhost//logout
というURLにアクセスでき、ルート名はlogout
で、アクセスがあるとAuth\AuthenticatedSessionController
というコントローラーのdestroyメソッド
が実行されるということです。
また、実行結果をよく見るとregister
やlogin
といった新規登録とログインのルーティングもされていることがわかります。
それらのルート名を使用して、未ログイン時の2つのボタンも作成することができます。
未ログイン時の記述は@else
以下の処理です。
@else
<a class="mr-lg-3 my-lg-0 my-3 btn btn-sm btn-dark text-light nav-item nav-link" href="{{ route('login') }}">ログイン</a>
<a class="btn btn-sm btn-primary text-light nav-item nav-link" href="{{ route('register') }}">新規登録</a>
@endauth
ログイン用のルート名はlogin
なので、{{ route('login') }}
となります。
また、新規登録用のルート名はregister
なので{{ route('register') }}
となります。
フッター作成
次に共通フッターを作成します。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ touch resources/views/layouts/footer.blade.php
task-app/resources/views/layouts
ディレクトリにfooter.blade.php
が作成されるので、下記の通り編集してください。
<footer class="footer fixed-bottom" style="background-color: #1f1f1f;">
<div class="container text-center">
<span class="text-light">©︎Laravel応用教材</span>
</div>
</footer>
フッターは特に難しいことはしていません。
トップページ作成
次にテンプレートが完成したのでトップページを作成していきましょう。
http://localhost/へアクセスされた場合に表示されるwelcome.blade.php
を変更していきましょう。
task-app/resources/views/welcome.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
タスク管理
@endsection
@section('content')
<div class="top-page d-flex flex-column justify-content-center align-items-center ">
<div class="h1 text-center">
<h1 class="text-light">タスク管理</h1>
<h2 class="text-light">いつでもどこでも簡単に♪</h2>
</div>
<div class="d-flex">
@auth
<a href="#" class="btn btn-success">プロジェクト一覧</a>
@else
<a href="{{ route('register') }}" class="btn btn-success">まずは無料で登録する</a>
@endauth
</div>
</div>
@endsection
ヘッダーと同じように@auth
を使用して、ログイン時と未ログインの時のボタン出し分けを行っています。
ログイン時はプロジェクト一覧へ遷移できるボタンを表示し、未ログインの時は新規登録画面へ遷移するためのボタンを表示するようにしています。
次に、独自のCSSで背景などを指定したいので、CSSファイルを作成します。
LaravelでCSSを使用するにはいくつか方法がありますが、まずは一番簡単に実装できる方法で実装します。
task-app/public
ディレクトリにcss
フォルダを作成し、その中にstyle.css
を作成してください。
コマンドでもVSCode上で作成してもどちらでもOKです。
コマンドで作成する場合は、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ mkdir public/css && touch public/css/style.css
作成したtask-app/public/css/style.css
を下記の通り編集してください。
.top-page {
background-image: url('../images/test.jpg');
background-color:rgba(0, 0, 0, .6);
background-blend-mode:darken;
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
min-height: calc(100vh - 80px);
}
CSSで背景に画像を指定していますが、画像がまだないので写真を用意して設定していきます。
皆さんが持っている画像でも大丈夫ですが、https://unsplash.com/photos/cckf4TsHAuwからダウンロードしていただいてもOKです。
※画像URLが開けない場合はこちらから好きな写真を選んでダウンロードしてください。
Laravelで画像を表示させるためにtask-app/public
ディレクトリにimages
フォルダを作成します。
さっそくpublic
ディレクトリにimages
フォルダを作成しましょう。
コマンドでもVSCode上で作成してもどちらでもOKです。
コマンドで作成する場合は、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ public/images
作成したtask-app/public/images
ディレクトリに画像を入れてください。
画像の名前はCSSで指定した通り、test.jpg
としています。
これでトップページを表示することができるようになりました。
http://localhost/にアクセスして、下記画像のようになっていればOKです!
認証画面編集
次に、新規登録画面やログイン画面の見た目を変更していきましょう。
Laravel Breezeで認証機能を実装した場合、自動的にtask-app/resources/views
ディレクトリにauth
ディレクトリが作成され、その中に認証用のBladeファイル
がいくつか作成されています。
新規登録画面
まず、新規登録画面はtask-app/resources/views/auth/register.blade.php
を変更することで、オリジナルの新規登録画面を作成することができます。
register.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
新規登録
@endsection
@section('content')
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header text-center">新規登録</div>
<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="form-group d-flex flex-column flex-md-row">
<label for="name" class="col-md-4 col-form-label text-md-right">ユーザー名:</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex flex-column flex-md-row mt-3">
<label for="email" class="col-md-4 col-form-label text-md-right">メールアドレス:</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex flex-column flex-md-row mt-3">
<label for="password" class="col-md-4 col-form-label text-md-right">パスワード:</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex flex-column flex-md-row mt-3">
<label for="password_confirmation" class="col-md-4 col-form-label text-md-right">パスワード確認:</label>
<div class="col-md-6">
<input id="password_confirmation" type="password" class="form-control @error('password_confirmation') is-invalid @enderror" name="password_confirmation" required autocomplete="new-password">
@error('password_confirmation')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex mt-3 mb-0">
<div class="col-md-10 col-12 d-flex justify-content-end">
<button type="submit" class="btn btn-primary">登録</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
編集が完了したら、http://localhost/registerへアクセスし、下記の通り画面が表示されればOKです。
それでは実装内容について2つのポイントを説明します。
まず1つ目は、formでPOST送信をしているということです。
action属性
にはroute('register')
を記述していますが、これは以前ルーティング一覧をコマンドで確認した時に新規登録用のルート名がregister
であったためです。
念の為、もう一度ルーティング一覧を確認できるコマンドを復習しておきましょう。
$ ./vendor/bin/sail php artisan route:list
心配な方はもう一度新規登録のルート名がregister
なのか確認しておくと良いでしょう。
2つ目にパスワード一致のバリデーション機能についてです。
Laravelでは、confirmedルール
というバリデーションルールが実装されています。
このルールはname属性がある属性値(仮にpassword
とします)の入力値とある属性値+confirmation
という属性値(今回の場合はpassword_confirmation
)の入力値が一致することを検証します。
よく使う場面として、パスワードを入力させた後に確認としてもう一度パスワードを入力させる時のバリデーションとして使用します。
今回もパスワードの確認用inputにはpassword_confirmation
というname属性
にしています。
ログイン画面
次にログイン画面の編集を行っていきます。
ログイン画面はtask-app/resources/views/auth/login.blade.php
を変更することで、オリジナルのログイン画面を作成することができます。
login.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
ログイン
@endsection
@section('content')
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header text-center">ログイン</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="form-group d-flex flex-column flex-md-row">
<label for="email" class="col-md-4 col-form-label text-md-right">メールアドレス:</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex flex-column flex-md-row mt-3">
<label for="password" class="col-md-4 col-form-label text-md-right">パスワード:</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex mt-3 mb-0">
<div class="col-md-10 col-12 d-flex justify-content-end">
<button type="submit" class="btn btn-primary">ログイン</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
編集が完了したら、http://localhost/loginへアクセスし、下記の通り画面が表示されればOKです。
ログイン画面は特に難しいことはありません。
新規登録画面と同じようにformでPOST送信をしています。
action属性
にはroute('login')
を記述していますが、Laravel Breezeで認証機能を実装した場合のログインのルート名がlogin
だからです。
もちろん$ ./vendor/bin/sail php artisan route:list
で確認できます。
バリデーションメッセージの日本語化
これで新規登録とログイン機能は完成しましたが、新規登録時のバリデーションメッセージの一部が下記画像の通り、日本語化されていないので設定していきます。
ちなみに、新規登録機能のコントローラーはtask-app/app/Http/Controllers/Auth/RegisterdUserController.php
です。
ユーザー情報登録用のメソッドstore
は下記のようになっています。
/**
* Handle an incoming registration request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
}
バリデーションに関するコードは$request->validate()
で囲まれた部分です。
記述方法はフォームリクエストで指定した方法と同じです。
このようにバリデーションはコントローラーで指定することもできます。
では、さっそく日本語化していきましょう。
task-app/resources/lang/ja/validation.php
を下記の通り編集してください。
// --- 以上省略 ---
/*
|--------------------------------------------------------------------------
| カスタムバリデーション属性名
|--------------------------------------------------------------------------
|
| 以下の言語行は、例えば"email"の代わりに「メールアドレス」のように、
| 読み手にフレンドリーな表現でプレースホルダーを置き換えるために指定する
| 言語行です。これはメッセージをよりきれいに表示するために役に立ちます。
|
*/
'attributes' => [
'name' => 'ユーザー名',
'email' => 'メールアドレス',
'password' => 'パスワード',
],
validation.php
は、以前日本語ファイルをダウンロードした際にダウンロードされたファイルです。
そのファイルの一番下にattributes => [];
となっているので、その中に翻訳した値を入力すればOKです。
もう一度新規登録画面でバリデーションに引っかかるように入力してみると、下記画像のようになります。
これで日本語化はOKです。
プロジェクト一覧
次はプロジェクト一覧画面を作成していきましょう。
ルーティング
まずはルーティングから行っていきます。
task-app/routes/web.php
を下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProjectController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth'])->name('dashboard');
// ログイン必須ルート
Route::middleware('auth')->group(function () {
// プロジェクト一覧画面
Route::get('projects', [ProjectController::class, 'index'])->name('projects.index');
});
require __DIR__.'/auth.php';
まず、タスク管理アプリには、未ログインユーザーがアクセスできないページがいくつか存在しています。
その内の一つが今回実装するプロジェクト一覧画面です。
web.php
では、アプリケーションの利用に認証を求める機能を実装することができます。
ページに認証を求める処理はミドルウェアを用いて実装します。
ミドルウェアとは、ルート毎の処理に移る前に実行される処理のことです。
つまり、画面を表示する前にログインかどうかをミドルウェアを使用して確かめるということです。
認証を求めるミドルウェアは最初から用意されているので、ルートにミドルウェアを適用するだけで実装することができます。
具体的な記述方法は下記の通りです。
Route::middleware('auth')->group(function () {
// ここにログイン後しか表示できないルートを記述する
});
とても簡単ですね。
以降ログインが必要な画面・処理のルーティングをする場合は、この中に記述していきます。
今はこれから実装するプロジェクト一覧画面のルーティングを記述しています。
コントローラー作成&編集
次に、ルーティングで指定したProjectコントローラー
を作成していきます。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:controller ProjectController
作成が完了したら、task-app/app/Http/Controllers/ProjectController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProjectController extends Controller
{
/**
* プロジェクト一覧画面表示
*/
public function index()
{
return view('projects.index');
}
}
これでProjectコントローラー
にindexメソッド
を作成することができました。
http://localhost/projects
にアクセスがあった場合、projects/index.blade.php
を表示させるように処理を記述したので、index.blade.php
を作成していきましょう。
まずはtask-app/resources/views
ディレクトリ配下にprojects
フォルダを作成してください。
作成が完了したらprojects
フォルダ内にindex.blade.php
を作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ mkdir resources/views/projects && touch resources/views/projects/index.blade.php
作成したtask-app/resources/views/projects/index.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
プロジェクト一覧
@endsection
@section('content')
<div class="container mt-4">
<div class="row">
<div class="col col-md-6 offset-md-3">
<div class="card">
<div class="card-header bg-dark text-light d-flex justify-content-between align-items-center">
<p class="mb-0 h5">プロジェクト</p>
<a href="#" class="btn btn-primary">追加</a>
</div>
<table class="table table-hover mb-0">
<tbody class="text-center">
<tr>
<td class="table-active"><a href="#">プロジェクト1</a></td>
</tr>
<tr>
<td><a href="#">プロジェクト2</a></td>
</tr>
<tr>
<td><a href="#">プロジェクト3</a></td>
</tr>
<tr>
<td><a href="#">プロジェクト4</a></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
今回は特に難しい記述はありません。
まだプロジェクトのデータが存在していないので、HTMLでデータ部分を記述しています。
テーブル作成
プロジェクトのデータを保存するためのprojectsテーブルを作成していきます。
projectsテーブルの内容は下記の通りです。
カラム名 | 日本語 |
---|---|
id | ID |
user_id | ユーザーID |
project_name | プロジェクト名 |
created_at | 作成日 |
updated_at | 更新日 |
user_id
というカラムが存在していますが、このカラムの値とusersテーブル
のid
を紐付けることで、どのユーザーが作成したプロジェクトなのかをデータとして紐付けることができます。
それでは、さっそくマイグレーションでprojectsテーブル
を作成していきましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:migration create_projects_table
作成したtask-app/database/migrations/2022_06_23_223330_create_projects_table.php
を下記の通り編集してください。
※2022_06_23_223330は作成した日時なので、皆さんのファイルとは異なっています。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('projects', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->unsigned(); // ここを追加
$table->string('project_name', 30); // ここを追加
$table->timestamps();
// 外部キーの設定
$table->foreign('user_id')->references('id')->on('users'); // ここを追加
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('projects');
}
};
追加した内容を確認すると、外部キーの設定という処理が追加されています。
これは2つのテーブル間でデータの整合性を保つために設定される制約です。
必須の設定ではありませんが、usersテーブル
とprojectsテーブル
のデータの整合性を保つために記述しておきます。
※データの整合性を保つ例としては、ユーザー情報が削除されると、削除されたユーザーに紐付いたプロジェクト情報も削除されます。
では、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan migrate
これでprojectsテーブル
が作成されました。
モデル作成
次に、projectsテーブル
のデータを操作するためのモデルを作成しましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:model Project
作成したtask-app/app/Models/Project.php
を下記の通り編集してください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'project_name',
];
}
データ内容を変えたいカラムを$fillable
で指定しておきましょう。
今回はユーザーID
とプロジェクト名
の2つのカラムのみでOKです。
テストデータ作成
モデルの作成も終わったので、シーダーを使用してテストデータを作成していきましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:seeder ProjectsTableSeeder
作成したtask-app/database/seeders/ProjectsTableSeeder.php
を下記の通り編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; // ここを追加
class ProjectsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// usersテーブルから1つのデータを取得
$user = DB::table('users')->first();
DB::table('projects')->insert([
'project_name' => 'プロジェクト1',
'user_id' => $user->id,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('projects')->insert([
'project_name' => 'プロジェクト2',
'user_id' => $user->id,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('projects')->insert([
'project_name' => 'プロジェクト3',
'user_id' => $user->id,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
特に難しい処理はしていません。
今回はテストデータとして3つのデータを作成しました。
また、user_id
には、存在するuser_id
が保存されていてほしいので、$user = DB::table('users')->first();
と記述して、シーダーファイル内でユーザー情報を取得するようにしています。
次にtask-app/database/seeders/DatabaseSeeder.php
を下記の通り編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call([
UsersTableSeeder::class,
ProjectsTableSeeder::class,
]);
}
}
これでprojectsテーブル
のテストデータを作成する準備が整いました。
それでは、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan migrate:refresh --seed
今回は新しくシーダーファイルを追加したので、migrate:refresh
コマンドを実行します。
このmigrate:refresh
コマンドを実行すると、全てのテーブルが一回削除され、もう一度マイグレーションが実行されます。
つまり、中のデータもリセットされるということです。
また、--seed
オプションを付けると、シーダーの実行も一緒にしてくれるので、テーブルの作成とデータの作成を同時に行ってくれます。
migrate:refresh
コマンド後にテーブルのデータを確認しておくといいですね!
リレーション
次はリレーションを定義していきます。
そもそもリレーション
とは、データベース上のテーブルを関連付けるための機能です。
例えば今回の実装の場合、usersテーブル
とprojectsテーブル
の2つが存在しています。
外部キーの設定でも少し説明しましたが、この2つのテーブルはid
とuser_id
で関連付けられています。
これをLaravelでは、どのテーブルとどのテーブルが関連しているのかをモデルの中に定義することができます。
リレーションにはいくつかの種類があり、代表的なものを2つ紹介します。
リレーション | メソッド | 例 |
---|---|---|
1 対 1 | hasOne() | 1人のユーザーに対してプロフィール画像は1枚 |
1 対 1(逆) | belongsTo() | 1枚のプロフィール画像に対してユーザーは1人 |
1 対 多 | hasMany() | 1人のユーザーに対して投稿は複数 |
1 対 多(逆) | belongsTo() | 1つの投稿に対してユーザーは1人 |
リレーションでは、このようにどのテーブルとどのテーブルがどのような関係で関連しているのかを定義することができます。
リレーションを設定していると、下記のような利点があります。
- リレーション先のテーブルにあるデータを取得できる
- テーブル情報に変更があれば、自動的に変更があったテーブルとリレーションのあるテーブルにも情報が反映される
なんとなくイメージはできたと思うので、さっそく実装していきます。
task-app/app/Models/User.php
を下記の通り編集してください。
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
// --- ここから追加 ---
/**
* Projectsテーブルとのリレーション
*/
public function projects()
{
return $this->hasMany(Project::class);
}
// --- ここまで追加 ---
}
今回は$this->hasMany(Project::class);
と定義しています。
それは、1人のユーザーに対して複数のプロジェクトを保持することができるからです。
また、メソッド名はprojects
と複数形にしています。
これも複数のプロジェクトを保持できることから複数形にしています。
projectsテーブル
から見た時のリレーションも設定していきましょう。
task-app/app/Models/Project.php
を下記の通り編集してください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
use HasFactory;
protected $fillable = [
'project_name',
'user_id',
];
// --- ここから追加 ---
/**
* Usersテーブルとのリレーション
*/
public function user()
{
return $this->belongsTo(User::class);
}
// --- ここまで追加 ---
}
今回は$this->belongsTo(User::class);
と定義しています。
それは、1つのプロジェクトに対して、1人のユーザーがそのプロジェクトの所有者だからです。
また、メソッド名はuser
と単数形にしています。
これは1つのプロジェクトにつき、プロジェクトの保有者は1人だからです。
これで定義自体は終わりです。
定義したメソッドの使用方法は実装時に確認しましょう。
コントローラー編集
プロジェクトのテストデータを用意したので、projectsテーブル
からデータを取得して表示できるようにしてみましょう。
task-app/app/Http/Controllers/ProjectController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; // ここを追加
class ProjectController extends Controller
{
/**
* プロジェクト一覧画面
*/
public function index()
{
// ログインユーザーが作成した全てのプロジェクトを取得
$projects = Auth::user()->projects->all();
return view('projects.index', compact('projects'));
}
}
Laravelでは現在のログインユーザー情報をAuth::user()
と記述することで取得することができます。
これはLaravelのAuthファサード
という機能を使用することで実装することができるので、use文
を使用して、Authファサード
を読み込んでいます。
また、ユーザー情報に->projects->all()
と記述するとリレーション先のprojectsテーブル
の情報を取得することができます。
ここで先ほどUserモデル
に定義したprojectsメソッド
を使用することができるということです。
後は取得したプロジェクト情報をcompact関数
を使用してビューに渡しています。
ビュー編集
次はビューを編集していきましょう。
task-app/resources/views/projects/index.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
プロジェクト一覧
@endsection
@section('content')
<div class="container mt-4">
<div class="row">
<div class="col col-md-6 offset-md-3">
<div class="card">
<div class="card-header bg-dark text-light d-flex justify-content-between align-items-center">
<p class="mb-0 h5">プロジェクト</p>
<a href="#" class="btn btn-primary">追加</a>
</div>
<table class="table table-hover mb-0">
<tbody class="text-center">
@foreach ($projects as $project)
<tr>
<td><a href="#">{{ $project->project_name }}</a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
http://localhost/projectsにアクセスし、下記画像のような画面が表示されればOKです。
これでプロジェクト一覧が表示されるようになりました。
新規登録・ログイン後のリダイレクト先を変更
現在、新規登録・ログイン後にリダイレクトされる先は下記の通りです。
これを先ほど作成したプロジェクト一覧に遷移するように設定しましょう。
Laravel Breezeでログイン機能を実装した場合、新規登録・ログイン後のリダイレクト先を変更するには、RouteServiceProvider.php
のpublic const HOME = '/dashboard'
を変更する必要があります。
task-app/app/Providers/RouteServiceProvider.php
を下記の通り編集してください。
<?php
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/projects';
// --- 以下省略 ---
}
編集が終了したら、http://localhost/loginからログインしてください。
ログイン後のリダイレクト先が下記画像のようにプロジェクト一覧画面になっていればOKです。
定数HOME
にプロジェクト一覧のパス
を指定することで、指定したリダイレクト先にリダイレクトされるようになります。
これは新規登録処理のコントローラーRegisteredUserController.php
とログイン処理のコントローラーAuthenticatedSessionController.php
のstoreメソッド
の中身で、RouteServiceProvider
の定数HOME
がリダイレクト先として指定してあるからです。
RegisteredUserController.php
のstoreメソッド
は下記の通りです。
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
}
return redirect(RouteServiceProvider::HOME);
と記述されているのがわかると思います。
また、AuthenticatedSessionController.php
のstoreメソッド
は下記の通りです。
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::HOME);
}
return redirect()->intended(RouteServiceProvider::HOME);
と記述されています。
中身の処理をわざわざ覚える必要は全くありません。
リダイレクト先の変更はRouteServiceProvider.php
の定数HOME
を変更することで実装できることが理解できればOKです!
念の為、新規登録後のリダイレクト先もプロジェクト一覧になっているかも各自確かめておきましょう。
タスク一覧
プロジェクト一覧が表示できるようになったので、次はタスク一覧画面を作成していきましょう。
ルーティング
まずはルーティングから行っていきます。
task-app/routes/web.php
を下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProjectController;
use App\Http\Controllers\TaskController; // ここを追加
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth'])->name('dashboard');
// ログイン必須ルート
Route::middleware('auth')->group(function () {
// プロジェクト一覧画面
Route::get('projects', [ProjectController::class, 'index'])->name('projects.index');
// タスク一覧画面
Route::get('projects/{id}/tasks', [TaskController::class, 'index'])->name('tasks.index'); // ここを追加
});
require __DIR__.'/auth.php';
もちろんタスク一覧画面もログインユーザーのみが閲覧できるようにしたいので、Route::middleware('auth')->group(function () {});
の中にルーティングしています。
テーブル作成
次は、タスク情報を保存するtasksテーブル
をマイグレーションで作成していきます。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:migration create_tasks_table
作成したtask-app/database/migrations/2022_06_23_223330_create_tasks_table.php
を下記の通り編集してください。
※2022_06_23_223330は作成した日時なので、皆さんのファイルとは異なっています。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->bigInteger('project_id')->unsigned(); // ここを追加
$table->string('task_name', 100); // ここを追加
$table->date('due_date'); // ここを追加
$table->integer('task_status')->default(0); // ここを追加
$table->timestamps();
// 外部キーの設定
$table->foreign('project_id')->references('id')->on('projects'); // ここを追加
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tasks');
}
};
今回も外部キーの設定をしています。
というのも、タスクはプロジェクト情報の下に存在するデータだからです。
そのためprojectsテーブル
とtasksテーブル
の整合性を保つために外部キー制約の設定をしています。
また、タスクにはタスク名(task_name)
や期限(due_date)
、進捗(task_status)
に関するデータを保持するので、それらの定義をマイグレーションファイル内で行っています。
進捗に関しては完了
や処理済み
、処理中
、未対応
などの文字列を入れるのではなく、数値(integer)
で管理していきます。
このようなある物事のステータスなどをデータベースで管理する場合は、数値で管理することが多いので覚えておきましょう。
それでは、定義したマイグレーションファイルを実行しましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan migrate
これでtasksテーブル
がデータベースに作成されました。
モデル作成
次に、tasksテーブル
のデータを操作するためのモデルを作成しましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:model Task
作成したtask-app/app/Models/Task.php
を下記の通り編集してください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
use HasFactory;
protected $fillable = [
'project_id',
'task_name',
'due_date',
'task_status',
];
}
データ内容を変えたいカラムを$fillable
で指定しておきましょう。
今回はプロジェクトID
とタスク名
、期限
、進捗
の4つのカラムを指定すればOKです。
リレーション
今回もprojectsテーブル
とtasksテーブル
のリレーションの設定をしておきましょう。
task-app/app/Models/Task.php
を下記の通り編集してください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
use HasFactory;
protected $fillable = [
'project_id',
'task_name',
'due_date',
'task_status',
];
/**
* Projectsテーブルとのリレーション
*/
public function project()
{
return $this->belongsTo(Project::class);
}
}
今回は$this->belongsTo(Project::class);
と定義しています。
それは、1つのタスクに対して、ある1つのプロジェクトがそのタスクを所有しているからです。
projectsテーブル
から見た時のリレーションも設定していきましょう。
task-app/app/Models/Project.php
を下記の通り編集してください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
use HasFactory;
protected $fillable = [
'project_name',
'user_id',
];
/**
* Usersテーブルとのリレーション
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Tasksテーブルとのリレーション
*/
public function tasks()
{
return $this->hasMany(Task::class);
}
}
今回は$this->hasMany(Task::class);
と定義しています。
それは、1つのプロジェクトに対して複数のタスクを保持することができるからです。
これでリレーションの設定は終わりです。
テストデータ作成
次は、シーダーを使用してタスクのテストデータを作成していきましょう。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:seeder TasksTableSeeder
作成したtask-app/database/seeders/TasksTableSeeder.php
を下記の通り編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; // ここを追加
class TasksTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$project = DB::table('projects')->first();
DB::table('tasks')->insert([
'project_id' => $project->id,
'task_name' => 'タスク名1',
'task_status' => 0,
'due_date' => date('Y-m-d H:i:s', strtotime("1 day")),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('tasks')->insert([
'project_id' => $project->id,
'task_name' => 'タスク名2',
'task_status' => 1,
'due_date' => date('Y-m-d H:i:s', strtotime("2 day")),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('tasks')->insert([
'project_id' => $project->id,
'task_name' => 'タスク名3',
'task_status' => 2,
'due_date' => date('Y-m-d H:i:s', strtotime("3 day")),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('tasks')->insert([
'project_id' => $project->id,
'task_name' => 'タスク名4',
'task_status' => 3,
'due_date' => date('Y-m-d H:i:s', strtotime("4 day")),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
特に難しい処理はしていません。
今回はテストデータとして4つのデータを作成しました。
また、project_id
には、存在するproject_id
が保存されていてほしいので、$project = DB::table('projects')->first();
と記述して、シーダーファイル内でプロジェクト情報を取得するようにしています。
次にtask-app/database/seeders/DatabaseSeeder.php
を下記の通り編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call([
UsersTableSeeder::class,
ProjectsTableSeeder::class,
TasksTableSeeder::class,
]);
}
}
これでtasksテーブル
のテストデータを作成する準備が整いました。
それでは、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan migrate:refresh --seed
今回も新しくシーダーファイルを追加したので、migrate:refresh
コマンドを実行します。
--seed
オプションも付与して、テーブルの作成とデータの作成を同時に行っています。
migrate:refresh
コマンド後にテーブルのデータを確認しておくといいですね!
コントローラー作成&編集
次に、ルーティングで指定したTaskコントローラー
を作成していきます。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:controller TaskController
作成が完了したら、task-app/app/Http/Controllers/TaskController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
class TaskController extends Controller
{
/**
* プロジェクトに紐づくタスク一覧
*/
public function index($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// プロジェクト取得
$project = Project::find($currentProjectId);
// 取得したプロジェクトに紐づくタスクを取得
$tasks = $project->tasks->all();
return view('tasks.index', compact(
'currentProjectId',
'tasks',
));
}
}
これでTaskコントローラー
にindexメソッド
を作成することができました。
ビュー作成&編集
http://localhost/projects/1/tasks
にアクセスがあった場合、Taskコントローラー
でtasks/index.blade.php
を表示させるように処理を記述したので、index.blade.php
を作成していきましょう。
まずはtask-app/resources/views
ディレクトリ配下にtasksフォルダ
を作成してください。
作成が完了したらtasksフォルダ
内にindex.blade.php
を作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ mkdir resources/views/tasks && touch resources/views/tasks/index.blade.php
作成したtask-app/resources/views/tasks/index.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
タスク一覧
@endsection
@section('content')
<div class="container mt-4">
<div class="row">
<div class="column col-md-8 offset-md-2 mt-md-0 mt-3">
<div class="card">
<div class="card-header bg-dark text-light d-flex justify-content-between align-items-center">
<p class="mb-0 h5">タスク</p>
<a href="#" class="btn btn-primary">追加</a>
</div>
<table class="table table-hover mb-0">
<thead class="text-light" style="background-color: rgb(106, 106, 106)">
<tr class="text-center">
<th scope="col"style="width: 65%">タスク名</th>
<th scope="col" style="width: 15%">進捗</th>
<th scope="col" style="width: 20%">期限</th>
</tr>
</thead>
<tbody class="text-center">
@foreach ($tasks as $task)
<tr>
<td><a href="#">{{ $task->task_name }}</a></td>
<td><span class="d-inline badge bg-secondary">{{ $task->task_status }}</span></td>
<td>{{ $task->due_date }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
http://localhost/projects/1/tasksにアクセスして、下記画像のように表示されればOKです。
また、プロジェクト一覧画面からプロジェクト名がクリックされた時に、そのクリックされたプロジェクトに紐づくタスク一覧画面が表示するようにリンクを変更しましょう。
task-app/resources/views/projects/index.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
プロジェクト一覧
@endsection
@section('content')
<div class="container mt-4">
<div class="row">
<div class="col col-md-6 offset-md-3">
<div class="card">
<div class="card-header bg-dark text-light d-flex justify-content-between align-items-center">
<p class="mb-0 h5">プロジェクト</p>
<a href="#" class="btn btn-primary">追加</a>
</div>
<table class="table table-hover mb-0">
<tbody class="text-center">
@foreach ($projects as $project)
<tr>
<td><a href="{{ route('tasks.index', $project->id) }}">{{ $project->project_name }}</a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
http://localhost/projectsへアクセスして、プロジェクト1
をクリックしてみましょう。
下記画像のようにプロジェクト1に紐付いたタスクが表示されればOKです。
さらにヘッダーでいくつか変更するべき箇所があるので、そちらを変更していきましょう。
task-app/resources/views/layouts/header.blade.php
を下記の通り編集してください。
<header>
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #1f1f1f;">
<div class="container-fluid">
@auth
<a class="navbar-brand" href="{{ route('projects.index') }}">タスク管理</a>
@else
<a class="navbar-brand" href="/">タスク管理</a>
@endauth
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup">
<div class="navbar-nav">
@auth
<a class="mr-lg-3 my-lg-0 my-3 btn btn-sm btn-dark text-light nav-item nav-link disabled" href="#">{{ Auth::user()->name }}</a>
<form action="{{ route('logout') }}" method="POST">
@csrf
<button class="btn btn-sm btn-danger text-light nav-item nav-link w-100">ログアウト</button>
</form>
@else
<a class="mr-lg-3 my-lg-0 my-3 btn btn-sm btn-dark text-light nav-item nav-link" href="{{ route('login') }}">ログイン</a>
<a class="btn btn-sm btn-primary text-light nav-item nav-link" href="{{ route('register') }}">新規登録</a>
@endauth
</div>
</div>
</div>
</nav>
</header>
変更点は下記の2点です。
1つ目は左上のタスク管理ボタンを未ログイン・ログイン時に遷移先を変更しています。
未ログイン時はトップページへ遷移させるようにし、ログイン時はプロジェクト一覧画面を表示させるようにしています。
2つ目は右上のユーザー名をログインユーザーの名前を表示させるようにしました。
アクセサ
アクセサとは、テーブルが本来持つデータを加工した状態で参照することができるようにしたLaravel
にデフォルトで備わっている機能です。
今回は進捗を数値でテーブルに保存しているので、表示する際に数字を完了
や処理済み
、処理中
、未対応
などの文字列に変換する必要があります。
この変換をモデルで自動的に行うことができる機能です。
使い方はモデルでget〇〇Attribute
というメソッドを用意するだけです。
以下は例として性別(カラム名:gender)を取得するためのgetGenderTextAttributeメソッド
です。
public function getGenderTextAttribute()
{
// 性別を取得(数値)
$gender = $this->attributes['gender'];
if($gender === 1) {
return '男性';
} else if ($gender === 2) {
return '女性';
} else {
return 'その他';
}
}
まず、$this->attributes['gender']
と記述することで、genderカラム
の値を取得することができます。
そして、取得した値を条件分岐させることで取得した値によって男性
や女性
、その他
としてreturnしています。
このgetGenderTextAttributeメソッド
はコントローラーやテンプレートで下記のように使用することができます。
$user->gender_text
get○○○Attribute
の○○○
の部分を->
の後に記述するだけです。
ただしメソッドの定義ではキャメルケース(GenderText)
ですが、使用するときはスネークケース(gender_text)
になります。
アクセサの実装
それでは実際に実装してみましょう。
task-app/app/Models/Task.php
を下記の通り編集してください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
use HasFactory;
const TASK_STATUS_STRING = [
'未対応',
'処理中',
'処理済み',
'完了',
];
const TASK_STATUS_CLASS = [
'bg-danger',
'bg-primary',
'bg-success',
'bg-secondary',
];
protected $fillable = [
'project_id',
'task_name',
'due_date',
'task_status',
];
/**
* Projectsテーブルとのリレーション
*/
public function project()
{
return $this->belongsTo(Project::class);
}
/**
* 進捗のテキスト用アクセサ
*/
public function getTaskStatusStringAttribute()
{
$taskStatus = $this->attributes['task_status'];
if (!isset(self::TASK_STATUS_STRING[$taskStatus])) {
return '';
}
return self::TASK_STATUS_STRING[$taskStatus];
}
/**
* 進捗のBootstrapクラス用アクセサ
*/
public function getTaskStatusClassAttribute()
{
$taskStatus = $this->attributes['task_status'];
if (!isset(self::TASK_STATUS_CLASS[$taskStatus])) {
return '';
}
return self::TASK_STATUS_CLASS[$taskStatus];
}
}
今回は進捗のテキスト完了
、処理済み
、処理中
、未対応
を出力してくれるgetTaskStatusStringAttributeメソッド
と、進捗によって色を変えたいのでBootstrapのクラスを出力してくれるgetTaskStatusClassAttributeメソッド
を作成しました。
どちらのメソッドでも$taskStatus = $this->attributes['task_status'];
で進捗を数値で取得した後、テキストとクラス名を返す処理にしてあります。
これでモデル側の処理は終了です。
ビュー編集
次に、ビュー側でアクセサのメソッドを使用してみましょう。
task-app/resources/views/tasks/index.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
タスク一覧
@endsection
@section('content')
<div class="container mt-4">
<div class="row">
<div class="column col-md-8 offset-md-2 mt-md-0 mt-3">
<div class="card">
<div class="card-header bg-dark text-light d-flex justify-content-between align-items-center">
<p class="mb-0 h5">タスク</p>
<a href="#" class="btn btn-primary">追加</a>
</div>
<table class="table table-hover mb-0">
<thead class="text-light" style="background-color: rgb(106, 106, 106)">
<tr class="text-center">
<th scope="col"style="width: 65%">タスク名</th>
<th scope="col" style="width: 15%">進捗</th>
<th scope="col" style="width: 20%">期限</th>
</tr>
</thead>
<tbody class="text-center">
@foreach ($tasks as $task)
<tr>
<td><a href="#">{{ $task->task_name }}</a></td>
<td><span class="d-inline badge {{ $task->task_status_class }}">{{ $task->task_status_string }}</span></td>
<td>{{ $task->due_date }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
http://localhost/projects/1/tasksにアクセスして、下記画像のように進捗が表示されていればOKです。
まず、Bootstrapのクラス名を<span class="d-inline badge {{ $task->task_status_class }}">
で設定しています。
メソッド名はgetTaskStatusClassAttribute
だったので、取得時はtask_status_class
になるだけですね。
また、進捗のテキストを{{ $task->task_status_string }}
で設定しています。
こちらもメソッド名がgetTaskStatusStringAttribute
だったものを、取得時にtask_status_string
にするだけです。