データベースの用意
MySQLクライアントを起動します。
> mysql -u root -p
MySQLクライアントで以下のコマンドを入力してデータベースを作成し、ユーザーを割り当てます。
mysql> create database laravel; Query OK, 1 row affected (0.00 sec) mysql> create user laravel@localhost identified by 'laraveldb'; Query OK, 0 rows affected (0.01 sec) mysql> grant all privileges on laravel.* to laravel@localhost; Query OK, 0 rows affected (0.00 sec)
データベースにアクセスするための設定
データベースにアクセスするための設定は、.env ファイルに記述します。
VSCodeで .env ファイルを開き、以下の部分を修正します。
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel DB_USERNAME=laravel DB_PASSWORD=laraveldb
これで、LaravelからMySQLにアクセスできるようになりました。
Laravelには、デフォルトでユーザーやキャッシュ関連のマイグレーションが用意されているので、これを実行しておきます。
> php artisan migrate INFO Preparing database. Creating migration table ............................................................. 27.16ms DONE INFO Running migrations. 0001_01_01_000000_create_users_table ................................................. 79.36ms DONE 0001_01_01_000001_create_cache_table ................................................. 29.63ms DONE 0001_01_01_000002_create_jobs_table .................................................. 69.06ms DONE
ユーザー認証機能を有効にする
お問い合わせには関係ないけど、ユーザー認証機能を有効にしておきます。
> composer require laravel/breeze > php artisan breeze:install
お問い合わせに対応するモデルを作成する
お問い合わせは、多くのサイトでは Contact のようなページが用意されていますので、お問い合わせに対応するモデルの名前は Contact とします。
Laravelでモデルを作成する場合は、コマンドラインから以下のコマンドを実行します。
> php artisan make:model Contact -m
最後の「-m」はマイグレーションファイルも同時に生成するためのオプションです。
これを実行すると、以下のファイルができます。
- app\Models\Contact.php
- database\migrations\yyyy_mm_dd_hhmmss_create_contacts_table.php
Contact.php
以下の内容を追加します。これは、Webアプリからこれらのフィールドにデータの保存を許可することを意味します。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
use HasFactory;
protected $fillable = [
'name',
'email',
'body',
];
}
yyyy_mm_dd_hhmmss_create_contacts_table.php
データベースの contacts テーブルに作成するカラムを指定します。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contacts');
}
};
ビューを作成する
LaravelのViewに対応するファイルは、resources\views フォルダに作成されます。
以下のコマンドでお問い合わせページに対応するビューを作ります。
> php artisan make:view contact
resources\views に contact.blade.php ファイルが作られます。
dashboard.blade.php をコピーして、以下の内容にしておきます。
おおもとのタグ x-app-layout は、resources\views\layouts\app.blade.php をレイアウトファイルに指定するという意味です。
このファイルは、ユーザーがログインしている状態を前提として書かれているので、app の部分を guest に変更しておきます。
また、@vite の行があると npm というツールをインストールする必要があるので、この行は削除しておきます。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
{{ __("お問い合わせ内容を入力してください。") }}
</div>
</div>
</div>
</div>
</x-guest-layout>
コントローラーを作成する
Laravelではコントローラーに対応するクラスは App\Http\Controllers フォルダに作成されます。
以下のコマンドで Contact に関連する制御をおこなう、ContactController を作成します。
> php artisan make:controller ContactController
ContactController.php の内容は以下のようにします。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ContactController extends Controller
{
public function index()
{
return view('contact');
}
}
resources\views\layouts\guest.blade.php は15行目の @vite の行を削除し、さらに、ロゴが記載されている div をまるごと削除しておきます。
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<!-- Scripts -->
</head>
<body class="font-sans text-gray-900 antialiased">
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
{{ $slot }}
</div>
</div>
</body>
</html>
ルーティングを定義する
どのURLにアクセスが来たときに、どのコントローラーに制御を渡すのかをルーティングといいます。
ルーティングを定義しているファイルは、routes/web.php です。
こちらに、以下の内容を追加します。
<?php
use App\Http\Controllers\ContactController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('/contact', [ContactController::class, 'index']);
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
require __DIR__.'/auth.php';
これで、http://localhost:8000/contactにアクセスすると、お問い合わせページが表示されます。
お問い合わせページに、入力フォームを追加します。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
{{ __("お問い合わせ内容を入力してください。") }}
</div>
<div>
<form action="" method="post">
@csrf
お名前: <input type="text" name="name" /></br>
Email: <input type="text" name="email" /></br>
お問い合わせ内容:
<textarea name="body"></textarea></br>
<button>送信</button>
</form>
</div>
</div>
</div>
</div>
</x-guest-layout>
ルーティングにPOST先を追加する
お問い合わせ内容のデータの送信先となるルーティングを追加します。
<?php
use App\Http\Controllers\ContactController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('/contact', [ContactController::class, 'index']);
Route::post('/post', [ContactController::class, 'post'])->name('contact.post');
formのaction属性を変更する
ルーティングでPOST先を追加したので、そのURLにデータを送信するように、formタグのaction属性を変更します。
ルーティングに対してname(‘contact.post)として名前を付けておいたので、以下のように記述すれば、Laravelが自動的に’/post’に変換してくれます。
@csrf の内容もinputタグに変換されているので、devtoolで確認しましょう。
<div>
<form action="{{ route('contact.post') }}" method="post">
@csrf
お名前: <input type="text" name="name" /></br>
Email: <input type="text" name="email" /></br>
お問い合わせ内容:
<textarea name="body"></textarea></br>
<button>送信</button>
</form>
</div>
お問い合わせ内容のデータをコントローラーで受け取る
お問い合わせ内容のデータをコントローラーで受け取って、データベースに保存するようにしましょう。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Contact;
class ContactController extends Controller
{
public function index()
{
return view('contact');
}
public function post(Request $request)
{
Contact::create([
'name' => $request->input('name'),
'email' => $request->input('email'),
'body' => $request->input('body'),
]);
return view('posted');
}
}
postメソッドの最後で posted ビューを表示しようとしていますので、作りましょう。
> php artisan make:view posted
以下の内容にします。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
{{ __("お問い合わせ内容を受け付けました。") }}
</div>
</div>
</div>
</div>
</x-guest-layout>
お問い合わせ内容を送信してみる
お問い合わせページで名前とメールアドレスとお問い合わせ内容を入力して送信してみましょう。
データベースに、入力した内容が登録されていればOKです。
以下のコマンドでも確認が可能です。
> php artisan tinker
Psy Shell v0.12.4 (PHP 8.2.12 — cli) by Justin Hileman
> App\Models\Contact::all()
= Illuminate\Database\Eloquent\Collection {#5980
all: [
App\Models\Contact {#5571
id: 1,
name: "asdf",
email: "asdf@example.com",
body: """
お問い合わせ内容\r\n
複数行\r\n
ああああ
""",
created_at: "2024-10-18 06:19:42",
updated_at: "2024-10-18 06:19:42",
},
],
}
>
お問い合わせ内容を確認するページを間に挟む
お問い合わせ内容を入力したら、確認ボタンで確認画面に遷移して、確認後にデータを送信するように変更してみましょう。
まず、contact.blade.php を少し変更します。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
{{ __("お問い合わせ内容を入力してください。") }}
</div>
<div>
<form action="{{ route('contact.confirm') }}" method="post">
@csrf
お名前: <input type="text" name="name" /></br>
Email: <input type="text" name="email" /></br>
お問い合わせ内容:
<textarea name="body"></textarea></br>
<button>確認</button>
</form>
</div>
</div>
</div>
</div>
</x-guest-layout>
15行目を route(‘contact.confirm’) に変更したので、これをルーティングに追加します。
Route::get('/contact', [ContactController::class, 'index']);
Route::post('/confirm', [ContactController::class, 'confirm'])->name('contact.confirm');
Route::post('/post', [ContactController::class, 'post'])->name('contact.post');
ContactControllerにconfirmメソッドを追加します。
public function confirm(Request $request)
{
$contact = new Contact;
$contact->name = $request->input('name');
$contact->email = $request->input('email');
$contact->body = $request->input('body');
return view('confirm', compact('contact'));
}
確認画面を作成する
確認画面では、コントローラーから受け取ったデータを表示するとともに、
フォーム内では input タグの type 属性を hidden にして送信用のデータを保持します。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
{{ __("お問い合わせ内容を確認してください。") }}
</div>
<div>
<div>お名前: {{ $contact->name }}</div>
<div>Email: {{ $contact->email }}</div>
<div>お問い合わせ内容:</div>
<div>{{ $contact->body }}</div>
<form action="{{ route('contact.post') }}" method="post">
@csrf
<input type="hidden" name="name" value="{{ $contact->name }}" /></br>
<input type="hidden" name="email" value="{{ $contact->email }}" /></br>
<input type="hidden" name="body" value="{{ $contact->body }}" />
<button>送信</button>
</form>
</div>
</div>
</div>
</div>
</x-guest-layout>
これだと、確認画面ではお問い合わせ内容の改行がうまく表示されません。
複数行を表示するために、nl2br() 関数を使って改行コードを br タグに変更するように修正します。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
{{ __("お問い合わせ内容を確認してください。") }}
</div>
<div>
<div>お名前: {{ $contact->name }}</div>
<div>Email: {{ $contact->email }}</div>
<div>お問い合わせ内容:</div>
<div>{!! nl2br($contact->body) !!}</div>
<form action="{{ route('contact.post') }}" method="post">
@csrf
<input type="hidden" name="name" value="{{ $contact->name }}" /></br>
<input type="hidden" name="email" value="{{ $contact->email }}" /></br>
<input type="hidden" name="body" value="{{ $contact->body }}" />
<button>送信</button>
</form>
</div>
</div>
</div>
</div>
</x-guest-layout>
お問い合わせの一覧を表示するページを作成する
投稿されたお問い合わせの一覧を表示するページを作成します。
まずはお問い合わせ一覧を表示するためのURLを用意します。
Route::get('/contact', [ContactController::class, 'index']);
Route::post('/confirm', [ContactController::class, 'confirm'])->name('contact.confirm');
Route::post('/post', [ContactController::class, 'post'])->name('contact.post');
Route::get('/contact-list', [ContactController::class, 'list'])->name('contact.list');
コントローラーにlist()メソッドを追加します。
public function list()
{
$contacts = Contact::orderBy('created_at', 'desc')->get();
return view('contacts', compact('contacts'));
}
ビューテンプレートを作成します。
以下のコマンドで作成します。
> php artisan make:view contacts
ビューテンプレートの内容は以下のようにします。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ一覧') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<table>
<tr>
<th>日時</th>
<th>名前</th>
</tr>
@foreach($contacts as $contact)
<tr>
<td>{{ $contact->created_at }}</td>
<td>{{ $contact->name }}</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</x-guest-layout>
日時の時間帯が変なので、タイムゾーンの設定を変更します。
.env ファイルの設定を変更します。
APP_TIMEZONE=Asia/Tokyo
お問い合わせ内容の詳細ページを作成する
お問い合わせ一覧ページで、名前のところをクリックすると、お問い合わせの詳細を表示するページに遷移できるようにします。
まずは詳細ページに対応するURLを作成します。
Route::get('/contact', [ContactController::class, 'index']);
Route::post('/confirm', [ContactController::class, 'confirm'])->name('contact.confirm');
Route::post('/post', [ContactController::class, 'post'])->name('contact.post');
Route::get('/contact-list', [ContactController::class, 'list'])->name('contact.list');
Route::get('/contact-detail/{id}', [ContactController::class, 'contact'])->name('contact.detail');
コントローラーにcontact()メソッドを追加します。
public function contact($id)
{
$contact = Contact::find($id);
return view('contact-detail', compact('contact'));
}
お問い合わせ詳細ページを作成します。
> php artisan make:view contact-detail
contact-detail.blade.php は、以下の内容にします。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div>
<div>日時: {{ $contact->created_at }}</div>
<div>お名前: {{ $contact->name }}</div>
<div>Email: {{ $contact->email }}</div>
<div>お問い合わせ内容:</div>
<div>{!! nl2br($contact->body) !!}</div>
</div>
<a href="{{ route('contact.list') }}">一覧に戻る</a>
</div>
</div>
</div>
</x-guest-layout>
一覧ページの名前の部分をリンクに修正します。
<x-guest-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('お問い合わせ一覧') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<table>
<tr>
<th>日時</th>
<th>名前</th>
</tr>
@foreach($contacts as $contact)
<tr>
<td>{{ $contact->created_at }}</td>
<td><a href="{{ route('contact.detail', $contact->id) }}">{{ $contact->name }}</a></td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</x-guest-layout>