laravelのTailwindCSS
Tailwind CSS のインストール
npm install tailwindcss postcss autoprefixer
Tailwind CSS の初期設定
npx tailwindcss init
SCSS のパッケージインストール
npm install sass
resources/scss/app.scss の作成
# resources/scss/app.scss
/* Tailwind のインポート */
@use "tailwindcss/base";
@use "tailwindcss/components";
@use "tailwindcss/utilities";
laravelのユーザー認証
共通レイアウトの作成
# resources/views/layouts/app.blade.php
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', 'Laravel App')</title>
@vite(['resources/css/app.scss', 'resources/js/app.js'])
</head>
<body id="app">
<header class="relative flex justify-between bg-white shadow-lg p-6">
<h2 class="text-center">ページタイトル</h2>
@auth
<p class="text-center">ログイン中: {{ Auth::user()->name }}</p>
@endauth
</header>
<main class="flex flex-col items-center justify-center min-h-screen bg-slate-200 py-3">
<div class="container shadow-md bg-white py-3 px-3 md:px-10">
@yield('content')
</div>
</main>
</body>
</html>
コントローラーの作成
php artisan make:controller HomeController
# app/Http/Controllers/HomeController.php
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
class HomeController extends Controller
{
public function index()
{
return view('index');
}
public function dashboard()
{
return view('dashboard');
}
}
トップページの作成
# resources/views/index.blade.php
@extends('layouts.app')
@section('title', 'ページタイトル')
@section('content')
<section id="top-page">
<h2 class="text-center mb-4">Welcome</h2>
<div class="mb-8">
<ul>
<li>アプリの説明</li>
</ul>
</div>
@auth
<p class="text-center mb-8">ログイン中: {{ Auth::user()->name }}</p>
@endauth
<div class="flex justify-center">
@auth
<a href="{{ route('dashboard') }}"
class="block w-auto text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">ダッシュボード</a>
@else
<a href="{{ route('register') }}"
class="block w-auto text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">ユーザー登録</a>
<a href="{{ route('login') }}"
class="block w-auto text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">ログイン</a>
@endauth
</div>
</section><!-- /#top-page -->
@endsection
ダッシュボードページの作成
# resources/views/dashboard.blade.php
@extends('layouts.app')
@section('title', 'ダッシュボード')
@section('content')
<h2 class="text-center mb-4">ダッシュボード</h2>
<p class="text-center">ログイン中: {{ Auth::user()->name }}</p>
<div class="d-flex justify-content-center">
<a href="{{ route('home') }}" class="btn btn-outline-secondary mx-2">トップページに戻る</a>
</div>
<form class="text-center mt-4" action="{{ route('logout') }}" method="POST" class="d-inline">
@csrf
<button type="submit" class="btn btn-danger mx-2">ログアウト</button>
</form>
@endsection
Controller.phpの変更
# Controller.php
<?php
namespace AppHttpControllers;
abstract class Controller
{
//
}
# Controller.php(変更後)
<?php
namespace AppHttpControllers;
use IlluminateFoundationAuthAccessAuthorizesRequests;
use IlluminateFoundationValidationValidatesRequests;
use IlluminateRoutingController as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
}
ルーティングの設定
# routes/web.php
<?php
use IlluminateSupportFacadesRoute;
use AppHttpControllersTaskController;
use AppHttpControllersHomeController;
use AppHttpControllersAuthRegisterController;
use AppHttpControllersAuthLoginController;
use AppHttpControllersAuthLogoutController;
// トップページのルート
Route::get('/', [HomeController::class, 'index'])->name('home');
// ダッシュボードのルート(ログインが必要)
Route::get('/dashboard', [HomeController::class, 'dashboard'])->middleware('auth')->name('dashboard');
// ユーザー登録のルート
Route::get('register', [RegisterController::class, 'showRegistrationForm'])->name('register')->middleware('guest');
Route::post('register', [RegisterController::class, 'register']);
// ログインのルート
Route::get('login', [LoginController::class, 'showLoginForm'])->name('login');
Route::post('login', [LoginController::class, 'login']);
// ログアウトのルート
Route::post('logout', [LogoutController::class, 'logout'])->name('logout');
Route::resource('tasks', TaskController::class);
ユーザー登録コントローラーの作成
php artisan make:controller Auth/RegisterController
# app/Http/Controllers/Auth/RegisterController.php
<?php
namespace AppHttpControllersAuth;
use AppHttpControllersController;
use AppModelsUser;
use IlluminateAuthEventsRegistered;
use IlluminateHttpRequest;
use IlluminateSupportFacadesAuth;
use IlluminateSupportFacadesHash;
use IlluminateValidationRules;
class RegisterController extends Controller
{
/**
* 登録フォームを表示する
*
* @return IlluminateViewView
*/
public function showRegistrationForm()
{
return view('auth.register');
}
/**
* 新しいユーザーインスタンスを作成し、保存する
*
* @param IlluminateHttpRequest $request
* @return IlluminateHttpRedirectResponse
*/
public function register(Request $request)
{
// 1. リクエストのデータを検証する
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', RulesPassword::defaults()],
]);
// 2. 新しいユーザーを作成する
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
// 3. ユーザー登録イベントを発行する
event(new Registered($user));
// 4. ユーザーをログインさせる
Auth::login($user);
// 5. ダッシュボードページにリダイレクトする
return redirect()->route('dashboard');
}
}
ユーザー登録フォームのテンプレート作成
# resources/views/auth/register.blade.php
@extends('layouts.app')
@section('title', 'ユーザー登録')
@section('content')
<h2 class="text-center mb-4">ユーザー登録</h2>
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="mb-3">
<label for="name" class="form-label">ユーザー名</label>
@error('name')
<div class="text-danger">{{ $message }}</div>
@enderror
<input id="name" type="text" name="name" class="border border-slate-400 p-2 w-full rounded-md"
value="{{ old('name') }}" autofocus>
</div>
<div class="mb-3">
<label for="email" class="form-label">メールアドレス</label>
@error('email')
<div class="text-danger">{{ $message }}</div>
@enderror
<input id="email" type="email" name="email" class="border border-slate-400 p-2 w-full rounded-md"
value="{{ old('email') }}">
</div>
<div class="mb-3">
<label for="password" class="form-label">パスワード</label>
@error('password')
<div class="text-danger">{{ $message }}</div>
@enderror
<input id="password" type="password" name="password" class="border border-slate-400 p-2 w-full rounded-md">
</div>
<div class="mb-3">
<label for="password-confirm" class="form-label">パスワード確認</label>
@error('password_confirmation')
<div class="text-danger">{{ $message }}</div>
@enderror
<input id="password-confirm" type="password" name="password_confirmation"
class="border border-slate-400 p-2 w-full rounded-md">
</div>
<div class="flex justify-center flex-row-reverse">
<button type="submit"
class="block w-auto text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">登録</button>
<a href="{{ route('home') }}"
class="w-auto text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">戻る</a>
</div>
</form>
@endsection
ログイン用コントローラークラスの作成
php artisan make:controller Auth/LoginController
# app/Http/Controllers/Auth/LoginController.php
<?php
namespace AppHttpControllersAuth;
use AppHttpControllersController;
use IlluminateHttpRequest;
use IlluminateSupportFacadesAuth;
class LoginController extends Controller
{
public function __construct()
{
$this->middleware('guest'); // ログインしていないユーザーのみがアクセス可能
}
/**
* ログインフォームを表示する
*
* @return IlluminateViewView
*/
public function showLoginForm()
{
return view('auth.login');
}
/**
* ユーザーのログイン処理を実行する
*
* @param IlluminateHttpRequest $request
* @return IlluminateHttpRedirectResponse
*/
public function login(Request $request)
{
// 1. リクエストのデータを検証する
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
// 2. 認証を試みる
if (Auth::attempt($credentials, $request->filled('remember'))) {
// 認証に成功したら、セッションを再生成する
$request->session()->regenerate();
// ダッシュボードにリダイレクトする
return redirect()->intended('dashboard');
}
// 認証に失敗した場合は、ログインページにリダイレクトする
return back()->withErrors([
'email' => 'ログイン情報が正しくありません。',
])->onlyInput('email');
}
}
ログアウト用コントローラーの作成
php artisan make:controller Auth/LogoutController
# app/Http/Controllers/Auth/LogoutController.php
<?php
namespace AppHttpControllersAuth;
use AppHttpControllersController;
use IlluminateSupportFacadesAuth; // Authファサードのインポート
use IlluminateHttpRequest;
class LogoutController extends Controller
{
/**
* ログアウト処理を実行する
*
* @param IlluminateHttpRequest $request
* @return IlluminateHttpRedirectResponse
*/
public function logout(Request $request)
{
// 1. ユーザーをログアウトさせる
Auth::logout();
// 2. セッションを無効にする
$request->session()->invalidate();
// 3. 新しいCSRFトークンを再生成する
$request->session()->regenerateToken();
// 4. トップページにリダイレクトする
return redirect('/');
}
}
ログインフォームのテンプレート作成
# resources/views/auth/login.blade.php
@extends('layouts.app')
@section('title', 'ログイン')
@section('content')
<section id="login">
<h2 class="text-center mb-4">ログイン</h2>
<form action="{{ route('login') }}" method="POST">
@csrf
<div class="mb-3">
<label class="block" for="email">メールアドレス</label>
<input id="email" class="border border-slate-400 p-2 w-full rounded-md" name="email" type="email"
value="{{ old('email') }}" autofocus autocomplete="email" required />
@error('email')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label class="block" for="password">パスワード</label>
<input id="password" class="border border-slate-400 p-2 w-full rounded-md" name="password" type="password"
autocomplete="current-password" required />
@error('password')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<div class="flex justify-center items-center mb-3 form-check">
<input id="remember"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
name="remember" type="checkbox" />
<label class="ml-3" for="remember">ログイン情報を記憶する</label>
</div>
<div class="flex justify-center flex-row-reverse">
<button
class="block w-auto text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
type="submit">ログイン</button>
<a href="{{ route('home') }}"
class="w-auto text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">戻る</a>
</div>
</form>
</section><!-- /#login -->
@endsection
Laravel の validation 日本語化
- locale の変更
# .env
APP_LOCALE=ja
日本語化
# resources/lang/ja/validation.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'attributes' => [
'name' => '名前',
],
'accepted' => ':attributeを承認してください。',
'active_url' => ':attributeは、有効なURLではありません。',
'after' => ':attributeには、:dateより後の日付を指定してください。',
'after_or_equal' => ':attributeには、:date以降の日付を指定してください。',
'alpha' => ':attributeには、アルファベッドのみ使用できます。',
'alpha_dash' => ":attributeには、英数字('A-Z','a-z','0-9')とハイフンと下線('-','_')が使用できます。",
'alpha_num' => ":attributeには、英数字('A-Z','a-z','0-9')が使用できます。",
'array' => ':attributeには、配列を指定してください。',
'before' => ':attributeには、:dateより前の日付を指定してください。',
'before_or_equal' => ':attributeには、:date以前の日付を指定してください。',
'between' => [
'numeric' => ':attributeには、:minから、:maxまでの数字を指定してください。',
'file' => ':attributeには、:min KBから:max KBまでのサイズのファイルを指定してください。',
'string' => ':attributeは、:min文字から:max文字にしてください。',
'array' => ':attributeの項目は、:min個から:max個にしてください。',
],
'boolean' => ":attributeには、'true'か'false'を指定してください。",
'confirmed' => ':attributeと:attribute確認が一致しません。',
'date' => ':attributeは、正しい日付ではありません。',
'date_equals' => ':attributeは:dateに等しい日付でなければなりません。',
'date_format' => ":attributeの形式は、':format'と合いません。",
'different' => ':attributeと:otherには、異なるものを指定してください。',
'digits' => ':attributeは、:digits桁にしてください。',
'digits_between' => ':attributeは、:min桁から:max桁にしてください。',
'dimensions' => ':attributeの画像サイズが無効です',
'distinct' => ':attributeの値が重複しています。',
'email' => ':attributeは、有効なメールアドレス形式で指定してください。',
'ends_with' => 'The :attribute must end with one of the following: :values',
'exists' => '選択された:attributeは、有効ではありません。',
'file' => ':attributeはファイルでなければいけません。',
'filled' => ':attributeは必須です。',
'gt' => [
'numeric' => ':attributeは、:valueより大きくなければなりません。',
'file' => ':attributeは、:value KBより大きくなければなりません。',
'string' => ':attributeは、:value文字より大きくなければなりません。',
'array' => ':attributeの項目数は、:value個より大きくなければなりません。',
],
'gte' => [
'numeric' => ':attributeは、:value以上でなければなりません。',
'file' => ':attributeは、:value KB以上でなければなりません。',
'string' => ':attributeは、:value文字以上でなければなりません。',
'array' => ':attributeの項目数は、:value個以上でなければなりません。',
],
'image' => ':attributeには、画像を指定してください。',
'in' => '選択された:attributeは、有効ではありません。',
'in_array' => ':attributeが:otherに存在しません。',
'integer' => ':attributeには、整数を指定してください。',
'ip' => ':attributeには、有効なIPアドレスを指定してください。',
'ipv4' => ':attributeはIPv4アドレスを指定してください。',
'ipv6' => ':attributeはIPv6アドレスを指定してください。',
'json' => ':attributeには、有効なJSON文字列を指定してください。',
'lt' => [
'numeric' => ':attributeは、:valueより小さくなければなりません。',
'file' => ':attributeは、:value KBより小さくなければなりません。',
'string' => ':attributeは、:value文字より小さくなければなりません。',
'array' => ':attributeの項目数は、:value個より小さくなければなりません。',
],
'lte' => [
'numeric' => ':attributeは、:value以下でなければなりません。',
'file' => ':attributeは、:value KB以下でなければなりません。',
'string' => ':attributeは、:value文字以下でなければなりません。',
'array' => ':attributeの項目数は、:value個以下でなければなりません。',
],
'max' => [
'numeric' => ':attributeには、:max以下の数字を指定してください。',
'file' => ':attributeには、:max KB以下のファイルを指定してください。',
'string' => ':attributeは、:max文字以下にしてください。',
'array' => ':attributeの項目は、:max個以下にしてください。',
],
'mimes' => ':attributeには、:valuesタイプのファイルを指定してください。',
'mimetypes' => ':attributeには、:valuesタイプのファイルを指定してください。',
'min' => [
'numeric' => ':attributeには、:min以上の数字を指定してください。',
'file' => ':attributeには、:min KB以上のファイルを指定してください。',
'string' => ':attributeは、:min文字以上にしてください。',
'array' => ':attributeの項目は、:min個以上にしてください。',
],
'not_in' => '選択された:attributeは、有効ではありません。',
'not_regex' => ':attributeの形式が無効です。',
'numeric' => ':attributeには、数字を指定してください。',
'password' => ':attributeが間違っています',
'present' => ':attributeが存在している必要があります。',
'regex' => ':attributeには、有効な正規表現を指定してください。',
'required' => ':attributeは、必ず指定してください。',
'required_if' => ':otherが:valueの場合、:attributeを指定してください。',
'required_unless' => ':otherが:values以外の場合、:attributeを指定してください。',
'required_with' => ':valuesが指定されている場合、:attributeも指定してください。',
'required_with_all' => ':valuesが全て指定されている場合、:attributeも指定してください。',
'required_without' => ':valuesが指定されていない場合、:attributeを指定してください。',
'required_without_all' => ':valuesが全て指定されていない場合、:attributeを指定してください。',
'same' => ':attributeと:otherが一致しません。',
'size' => [
'numeric' => ':attributeには、:sizeを指定してください。',
'file' => ':attributeには、:size KBのファイルを指定してください。',
'string' => ':attributeは、:size文字にしてください。',
'array' => ':attributeの項目は、:size個にしてください。',
],
'starts_with' => ':attributeは、次のいずれかで始まる必要があります。:values',
'string' => ':attributeには、文字を指定してください。',
'timezone' => ':attributeには、有効なタイムゾーンを指定してください。',
'unique' => '指定の:attributeは既に使用されています。',
'uploaded' => ':attributeのアップロードに失敗しました。',
'url' => ':attributeは、有効なURL形式で指定してください。',
'uuid' => ':attributeは、有効なUUIDでなければなりません。',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/
];
laravel のテンプレート的使用方法
- 既存のプロジェクトをコピーし、コピーしたプロジェクトの.env を編集します
# .env
DB_DATABASE=kadaikanriserviceapp_db
- キャッシュを削除します
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear
- composer.lock を削除します
rm -rf vendor composer.lock
- composer install を実行します
composer install
- db を作成します
mysql -u root -p -e "CREATE DATABASE kadaikanriserviceapp_db;"
- migrate を実行します
php artisan migrate
- key を生成します
php artisan key:generate
- git の設定を削除します
rm -rf .git
- commit します
git init
git add .
git commit -m "Initial commit for new project"
- リモートリポジトリを作成して、push します
git branch -M main
git remote add origin https://github.com/****.git
git push -u origin main
laravel の composer は、ruby の gem
- composer.jsonにGemfileと同様に、バージョンなどが記載されています。