https://mailtrap.io/を使ってローカルのメール送信をやってみましょう。19161917
会員登録をしましょう。
で、それが終わったら、
Email testingのstart tesingをクリックしてください。
そしてこのセレクトボックスでLaravelを選択すると おもいっきりSMTPの情報が書かれています。
そしてそれを.envにコピーします。
こういう感じになります。
MAIL_MAILER=smtp
MAIL_HOST=sandbox.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=2322ce6647c723
MAIL_PASSWORD=ebc27440c41091
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="info@appnamedayo.com"
MAIL_FROM_NAME="${APP_NAME}"
Laravel 11の開発でUserを作る時の
マイグレーションファイルの作成
ユーザ登録用のマイグレーションファイルを作成します。 メールが飛んで、アクティベーションを作るのと 免許証をアップロードしてあとからadminが承認して承認ユーザだけがログインできるようなものを作ろうと思います、
php artisan make:migration create_users_table
database/migrations/xxxx_xx_xx_create_users_table.phpに以下のコードを追加します。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('nickname');
$table->string('phone');
$table->string('email')->unique();
$table->tinyInteger('gender');
$table->string('license_image')->nullable();
$table->text('introduction')->nullable();
$table->string('occupation');
$table->string('password');
$table->boolean('approved')->default(false);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
マイグレーションを実行します。(php artisan migrate)
あれ。。。
エラーになりました。
みてみると「もうusersテーブルあるよ」ってことです。 どうやらデフォルトでusersテーブルがあるようです。 たしかにマイグレーションファイルを確認すると
<?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('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
もともとこれがありました。
ということで、 必要な項目だけ追加するマイグレーションファイルを作ることにします。
さきほど実行して作ったマイグレーションファイルは削除して もう一度作りましょう。
php artisan make:migration add_fields_to_users_table –table=users
で生成されたマイグレーションに、
<?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::table('users', function (Blueprint $table) {
$table->string('nickname')->after('id');
$table->string('phone')->after('nickname');
$table->tinyInteger('gender')->after('email');
$table->string('license_image')->nullable()->after('gender');
$table->text('introduction')->nullable()->after('license_image');
$table->string('occupation')->after('introduction');
$table->boolean('approved')->default(false)->after('password');
$table->string('activation_token', 60)->nullable()->after('remember_token'); // ここに追加
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['nickname', 'phone', 'gender', 'license_image', 'introduction', 'occupation', 'approved']);
});
}
};
これを記載します。
そしてもう一度、
できましたね。
元々テーブルがある場合のカラム追加の解説になったかと思います。
Userモデルの作成
Userモデルを作成し、fillable属性を設定します。
php artisan make:model User
となると本来このコマンドでUserモデルができますが、 デフォルトであるようなので、/app/Models/User.phpをみると 確かにありますね。
app/Models/User.phpに以下のコードを追加します。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name', 'nickname', 'phone', 'email', 'gender', 'license_image', 'introduction', 'occupation', 'password', 'approved', 'activation_token'
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password', 'remember_token', 'activation_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
もともとのモデルの要素をかえずに今回追加したカラムを追加しました。
ルーティングの設定
routes/web.phpにルーティングを追加します。
use App\Http\Controllers\UserController; //これは上に書いてね。
Route::get('user/signup', [UserController::class, 'showSignupForm'])->name('user.signup');
Route::post('user/signup', [UserController::class, 'registerUser'])->name('user.register');
Route::get('user/activate/{token}', [UserController::class, 'activateUser'])->name('user.activate');
Route::get('user/dashboard', [UserController::class, 'showDashboard'])->middleware('auth')->name('user.dashboard');
コントローラーの作成
ユーザ登録用のコントローラーを作成します。 モデルまで用意されているのにユーザ側はデフォルトではないのかよと思いながら 作っていきます。
php artisan make:controller UserController
app/Http/Controllers/UserController.phpに以下のコードを追加します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use App\Mail\ActivationMail;
class UserController extends Controller
{
public function showSignupForm()
{
return view('user.signup');
}
public function registerUser(Request $request)
{
$request->validate([
'nickname' => 'required|string|max:255',
'name' => 'required|string|max:255',
'phone' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'gender' => 'required|integer',
'license_image' => 'required|file|image|max:2048',
'introduction' => 'nullable|string',
'occupation' => 'required|string',
]);
$licenseImagePath = $request->file('license_image')->store('licenses', 'public');
$user = User::create([
'nickname' => $request->nickname,
'name' => $request->name,
'phone' => $request->phone,
'email' => $request->email,
'gender' => $request->gender,
'license_image' => $licenseImagePath,
'introduction' => $request->introduction,
'occupation' => $request->occupation,
'password' => Hash::make(Str::random(10)),
]);
$token = Str::random(60);
$user->update(['activation_token' => $token]);
Mail::to($user->email)->send(new ActivationMail($user, $token));
return redirect()->route('user.signup')->with('success', 'ご登録ありがとうございます。確認のためのメールをお送りしました。');
}
public function activateUser($token)
{
$user = User::where('activation_token', $token)->firstOrFail();
$user->update(['approved' => true, 'activation_token' => null]);
return redirect()->route('password.reset', $passwordResetToken)->with('success', 'アカウントが承認されました。パスワードを設定してください。');
}
public function showDashboard()
{
return view('user.dashboard');
}
}
メールのためのクラスの作成
メール送信のためのクラスを作成します。
php artisan make:mail ActivationMail
app/Mail/ActivationMail.phpに以下のコードを追加します。
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class ActivationMail extends Mailable
{
use Queueable, SerializesModels;
public $user;
public $token;
public function __construct($user, $token)
{
$this->user = $user;
$this->token = $token;
}
public function build()
{
return $this->from(env('MAIL_FROM_ADDRESS'), env('MAIL_FROM_NAME'))
->subject('推薦マッチに登録いただきましてありがとうございます。')
->view('emails.activation')
->with(['user' => $this->user, 'token' => $this->token]);
}
}
FROMのメールはenvから 件名はアクティベーションメールの時のみ、 メールテンプレートはviews/emails/activation.blade.php にあるよと。 ->with([‘user’ => $this->user, ‘token’ => $this->token]);はメールテンプレートに変数を渡すやり方です。基本的な機能なのでよく使います。
ビューの作成
resources/views/user/signup.blade.phpに以下のコードを追加します。
php artisan make:view user.signup
を実行して作りましょう。
@extends('layouts.guest')
@section('content')
<form action="{{ route('user.register') }}" method="POST" enctype="multipart/form-data">
@csrf
<label for="name">お名前:</label>
<input type="text" name="name" required><br>
<label for="nickname">ニックネーム:</label>
<input type="text" name="nickname" required><br>
<label for="phone">電話番号:</label>
<input type="text" name="phone" required><br>
<label for="email">メール:</label>
<input type="email" name="email" required><br>
<label for="gender">性別:</label>
<select name="gender" required>
<option value="1">男</option>
<option value="0">女</option>
</select><br>
<label for="license_image">免許証アップロード:</label>
<input type="file" name="license_image" required><br>
<label for="introduction">自己紹介:</label>
<textarea name="introduction"></textarea><br>
<label for="occupation">職業:</label>
<select name="occupation" required>
@foreach(config('occupations') as $occupation)
<option value="{{ $occupation }}">{{ $occupation }}</option>
@endforeach
</select><br>
<button type="submit">登録</button>
</form>
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
@endif
@endsection
ログインしていないときのlayoutsを作成しました。
views/layouts/guest.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ config('app.name', '出会い系アプリ') }}</title>
<link rel="stylesheet" href="<https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css>">
<link rel="stylesheet" href="<https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css>">
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.header,
.footer {
background-color: #f8f9fa;
padding: 10px 20px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 24px;
}
.content {
padding: 20px;
}
.footer p {
margin: 0;
font-size: 14px;
}
</style>
</head>
<body>
<div class="header">
<h1>{{ config('app.name', '出会い系アプリ') }}</h1>
</div>
<div class="content">
@yield('content')
</div>
<div class="footer">
<p>© {{ date('Y') }} 出会い系アプリ. All rights reserved.</p>
</div>
<script src="<https://code.jquery.com/jquery-3.2.1.slim.min.js>"></script>
<script src="<https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js>"></script>
<script src="<https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js>"></script>
</body>
</html>
php artisan make:view emails.activationを実行。
resources/views/emails/activation.blade.phpに以下のコードを追加します。
<div>
<p>{{ $user->nickname }}様</p>
<p>推薦マッチに登録いただきましてありがとうございます。</p>
<p>わたしたちはとても良い人なのに、忙しさや環境によって出会いがない方々のためのサービスを提供しています。</p>
<p>以下のリンクをクリックして、アカウントを有効化してください。</p>
<a href="{{ route('user.activate', $token) }}">アカウントを有効化</a>
</div>
ログイン後のダッシュボードのviewも作りましょう。
php artisan make:view dashboard
resources/views/dashboard.blade.phpに以下のコードを追加します。
@extends('layouts.user')
@section('content')
<div>
<h1>ダッシュボード</h1>
<p>ここには今後の機能が表示されます。</p>
</div>
@endsection
views/layouts/user.blade.phpにレイアウトを追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ config('app.name', '出会い系アプリ') }}</title>
<link rel="stylesheet" href="<https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css>">
<link rel="stylesheet" href="<https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css>">
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.header, .footer {
background-color: #f8f9fa;
padding: 10px 20px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 24px;
}
.nav-menu {
display: flex;
justify-content: space-around;
background-color: #343a40;
color: white;
}
.nav-menu .nav-item {
color: white;
text-decoration: none;
padding: 10px 15px;
display: block;
cursor: pointer;
}
.nav-menu .nav-item:hover {
background-color: #495057;
}
.content {
padding: 20px;
}
.footer p {
margin: 0;
font-size: 14px;
}
</style>
</head>
<body>
<div class="header">
<h1>{{ config('app.name', '出会い系アプリ') }}</h1>
</div>
<div class="nav-menu">
<span class="nav-item"><i class="fas fa-home"></i> ホーム</span>
<span class="nav-item"><i class="fas fa-user"></i> プロフィール</span>
<span class="nav-item"><i class="fas fa-envelope"></i> メッセージ</span>
<span class="nav-item"><i class="fas fa-cog"></i> 設定</span>
<span class="nav-item" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"><i class="fas fa-sign-out-alt"></i> ログアウト</span>
<form id="logout-form" action="#" method="POST" style="display: none;">
@csrf
</form>
</div>
<div class="content">
@yield('content')
</div>
<div class="footer">
<p>© {{ date('Y') }} 出会い系アプリ. All rights reserved.</p>
</div>
<script src="<https://code.jquery.com/jquery-3.2.1.slim.min.js>"></script>
<script src="<https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js>"></script>
<script src="<https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js>"></script>
</body>
</html>
職業はadminでマスタ化しようか迷ったんですが、configでいきます。
config/occupations.phpを作成し、以下の内容を追加します。 Laravel 11でもこのあたりの指定は同じです。
<?php
return [
'management' => '管理職',
'professional' => '専門職',
'technical' => '技術職',
'creative' => 'クリエイティブ職',
'educational' => '教育職',
'medical' => '医療・福祉職',
'sales_service' => '販売・サービス職',
'office' => '事務職',
'public_servant' => '公務員',
'transport_logistics' => '運輸・物流職',
'agriculture_forestry_fisheries' => '農林水産職',
'student' => '学生',
'other' => 'その他',
];
ここまでで、会員登録するとメールが飛んできて、 アクティベーションメールをクリックするとuserのapproveが1となり有効になるというところまで解説できました。
Zenさん曰く 、
Laravel10まではconfigファイルはLaravel/laravelのconfigディレクトリにありましたが、
それがlaravel/frameworkの方に移動しました。
reffectさん曰く
Laravel 11では、configディレクトリに10個のファイルがあります。 一方、Laravel 10では、configディレクトリに15個のファイルがあります。 Laravel 11では、ファイル数が減少し5つのファイルが削除されています。例えば、hashing.phpが削除されています。Laravel 11では、変更の頻度が低い設定ファイルはデフォルトでは存在しません。しかし、必要な場合はコマンドを使ってファイルを作成できます。 例えば、hashing.phpを作成する場合 php artisan config:publish hashing このコマンドを実行すると、configディレクトリにhashing.phpファイルが追加されます。どのようなコマンドを利用するとconfigファイルが作成できるかはマニュアルで確認できますが、php artisan config:publishコマンドを実行するだけで、作成可能なファイルの選択肢が表示されます。
とのことなので、おもいっきり10のやり方で書いてしまってますので、 みておいてください。
ここから、userテーブルにパスワードリセットのカラムを用意して アクティベーション後にパスワードを自分で決めさせてログインできるよう解説していきます。
php artisan make:migration add_password_reset_token_to_users_table --table=users
パスワードリセットのところだけのマイグレーションです。 userに追加する感じで。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('password_reset_token', 60)->nullable()->after('activation_token');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('password_reset_token');
});
}
};
php artisan migrateで、実際にユーザテーブルを更新しましょう
ルーティングに
// パスワード更新ページ
Route::get('user/reset-password/{token}', [UserController::class, 'showResetPasswordForm'])->name('password.reset');
// パスワード更新処理
Route::post('user/reset-password', [UserController::class, 'resetPassword'])->name('password.update');
// ログインページ
Route::get('login', [UserController::class, 'showLoginForm'])->name('login');
Route::post('login', [UserController::class, 'login']);
これを追加です。
Userコントローラーにこれを追加してください
public function showResetPasswordForm($token)
{
return view('user.reset-password', ['token' => $token]);
}
public function resetPassword(Request $request)
{
$request->validate([
'token' => 'required',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::where('password_reset_token', $request->token)->firstOrFail();
$user->update([
'password' => Hash::make($request->password),
'password_reset_token' => null,
]);
return redirect()->route('login')->with('success', 'パスワードが更新されました。ログインしてください。');
}
public function showLoginForm()
{
return view('auth.login');
}
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required|string',
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('dashboard')->with('success', 'ログインに成功しました。');
}
return back()->withErrors([
'email' => '提供された認証情報は記録と一致しません。',
])->onlyInput('email');
}
public function showDashboard()
{
return view('user.dashboard');
}
コントローラーの一覧上に use IlluminateSupportFacadesAuth; を追記してAuth(認証)のライブラリを使えるようにしておきましょう。
viewを作るために
php artisan make:view user.reset-password
php artisan make:view auth.login
を実行してviewを作ります。
パスワードはこんな感じでOKです。
@extends('layouts.guest')
@section('content')
<form action="{{ route('password.update') }}" method="POST">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group">
<label for="password">新しいパスワード:</label>
<input type="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label for="password_confirmation">パスワード確認:</label>
<input type="password" name="password_confirmation" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">更新</button>
</form>
@endsection
ログインは
@extends('layouts.guest')
@section('content')
<form action="{{ route('login') }}" method="POST">
@csrf
<div class="form-group">
<label for="email">メールアドレス:</label>
<input type="email" name="email" class="form-control" required>
</div>
<div class="form-group">
<label for="password">パスワード:</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">ログイン</button>
</form>
@endsection
です。
UserのModelの $fillableの配列と $hiddenの配列にpassword_reset_tokenを追加するのを忘れないようにしましょう。
最後にルートに
// 認証が必要なルートグループ
Route::middleware(['auth'])->group(function () {
Route::get('dashboard', [UserController::class, 'showDashboard'])->name('user.dashboard');
});
を追加して ログインしたらダッシュボードにいくためのルーティングを作っておきます。
(前のdashbordの記述は消しておきましょう。グループの中に書いていくほうが合理的です。)
これで、Laravel 11の基本的な実装で、登録からログインまで解説完了。
実際にやってみてもできるようになったと思うので、 確認してみます。
では会員登録したユーザの免許証を見てadminが承認するという機能を作ってみましょう。
このあたりはLaravel 11の解説というよりも、 もはやただのLaravelの開発の基本的解説です。 まずルーティングのadminのgroupの中に
//adminがユーザを管理する
Route::get('users', [UserController::class, 'adminIndex'])->name('admin.users.index');
Route::get('users/{user}', [UserController::class, 'adminShow'])->name('admin.users.show');
Route::post('users/{user}/approve', [UserController::class, 'approveUser'])->name('admin.users.approve');
を追加して admin/usersのようなURLに対して、 Userコントローラーでadminの閲覧や承認用のアクションのためのルーティングを作ります。
UserControllerの中に追加するアクションは
/**
* 管理者用のユーザ一覧を表示します。
*
* @return \Illuminate\View\View
*/
public function adminIndex(Request $request)
{
$query = User::query();
// 検索フィルタ
if ($request->filled('name')) {
$query->where('name', 'like', '%' . $request->name . '%');
}
if ($request->filled('approved')) {
$query->where('approved', $request->approved);
}
// 並び替え
if ($request->filled('sort_by')) {
$query->orderBy($request->sort_by, $request->get('sort_order', 'asc'));
} else {
$query->orderBy('created_at', 'desc');
}
$users = $query->paginate(10);
return view('admin.users.index', compact('users'));
}
/**
* 管理者用のユーザ詳細を表示します。
*
* @param \App\Models\User $user
* @return \Illuminate\View\View
*/
public function adminShow(User $user)
{
return view('admin.users.show', compact('user'));
}
/**
* ユーザを承認します。
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\User $user
* @return \Illuminate\Http\RedirectResponse
*/
public function approveUser(Request $request, User $user)
{
$user->update(['approved' => true]);
return redirect()->route('admin.users.index')->with('success', 'ユーザを承認しました。');
}
です。
次にviewですが。 resources/views/admin/users/index.blade.php に
@extends('admin.layout')
@section('content')
<div class="container">
<h1>ユーザ一覧</h1>
<form method="GET" action="{{ route('admin.users.index') }}" class="mb-3">
<div class="form-row">
<div class="col">
<input type="text" name="name" class="form-control" placeholder="名前" value="{{ request('name') }}">
</div>
<div class="col">
<select name="approved" class="form-control">
<option value="">承認ステータス</option>
<option value="1"{{ request('approved') == '1' ? ' selected' : '' }}>承認済み</option>
<option value="0"{{ request('approved') == '0' ? ' selected' : '' }}>未承認</option>
</select>
</div>
<div class="col">
<button type="submit" class="btn btn-primary">検索</button>
</div>
</div>
</form>
<table class="table">
<thead>
<tr>
<th><a href="{{ route('admin.users.index', ['sort_by' => 'name', 'sort_order' => request('sort_order') === 'asc' ? 'desc' : 'asc']) }}">名前</a></th>
<th>性別</th>
<th><a href="{{ route('admin.users.index', ['sort_by' => 'approved', 'sort_order' => request('sort_order') === 'asc' ? 'desc' : 'asc']) }}">承認ステータス</a></th>
<th><a href="{{ route('admin.users.index', ['sort_by' => 'created_at', 'sort_order' => request('sort_order') === 'asc' ? 'desc' : 'asc']) }}">登録日時</a></th>
<th>更新日時</th>
<th>詳細</th>
<th>承認</th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td>{{ $user->name }}</td>
<td>{{ $user->gender == 1 ? '男性' : '女性' }}</td>
<td>{{ $user->approved ? '承認済み' : '未承認' }}</td>
<td>{{ $user->created_at }}</td>
<td>{{ $user->updated_at }}</td>
<td><a href="{{ route('admin.users.show', $user) }}" class="btn btn-primary">詳細</a></td>
<td>
@if (!$user->approved)
<form method="POST" action="{{ route('admin.users.approve', $user) }}">
@csrf
<button type="submit" class="btn btn-success">承認</button>
</form>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
{{ $users->withQueryString()->links() }}
</div>
@endsection
そして、
resources/views/admin/users/show.blade.php には
@extends('layouts.app')
@section('content')
<div class="container">
<h1>{{ $referral->nickname }} の詳細</h1>
<p><strong>活動エリア:</strong> {{ $referral->areas }}</p>
<p><strong>紹介:</strong> {{ $referral->introduction }}</p>
<p><strong>仕事関係の紹介:</strong> {{ $referral->work_introduction }}</p>
<h2>マッチした紹介者</h2>
<ul>
@foreach ($matchedReferrals as $matchedReferral)
<li>
<p><strong>名前:</strong> {{ $matchedReferral->nickname }}</p>
<p><strong>活動エリア:</strong> {{ $matchedReferral->areas }}</p>
<p><strong>紹介:</strong> {{ $matchedReferral->introduction }}</p>
<p><strong>仕事関係の紹介:</strong> {{ $matchedReferral->work_introduction }}</p>
</li>
@endforeach
</ul>
</div>
@endsection
を追加します。
http://127.0.0.1/admin/users2137 にアクセスすると。。。
できました。
ではユーザが自分の友人を勝手に登録して同じエリアあたりの人がいたらそれをマッチした人として表示する機能を作ります
1. Migrationの作成
まず、新しい「紹介したい人」テーブルを作成するためのマイグレーションを作成します。
php artisan make:migration create_referrals_table --create=referrals
database/migrations/xxxx_xx_xx_xxxxxx_create_referrals_table.php ファイルを以下のように編集します。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateReferralsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('referrals', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('nickname');
$table->string('areas'); // 複数の活動エリアをカンマ区切りで保存
$table->text('introduction');
$table->text('work_introduction');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('referrals');
}
}
2. Modelの作成
新しいモデルを作成します。
php artisan make:model Referral
app/Models/Referral.php ファイルを以下のように編集します。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Referral extends Model
{
use HasFactory;
protected $fillable = [
'user_id', 'nickname', 'areas', 'introduction', 'work_introduction'
];
public function user()
{
return $this->belongsTo(User::class);
}
}
3. Controllerの作成
新しいコントローラーを作成します。
php artisan make:controller ReferralController
app/Http/Controllers/ReferralController.php ファイルを以下のように編集します。
<?php
namespace App\Http\Controllers;
use App\Models\Referral;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ReferralController extends Controller
{
/**
* 紹介したい人の登録フォームを表示します。
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('referrals.create');
}
/**
* 紹介したい人を保存します。
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
'nickname' => 'required|string|max:255',
'areas' => 'required|string|max:255',
'introduction' => 'required|string|max:1000',
'work_introduction' => 'required|string|max:300',
]);
Referral::create([
'user_id' => Auth::id(),
'nickname' => $request->nickname,
'areas' => $request->areas,
'introduction' => $request->introduction,
'work_introduction' => $request->work_introduction,
]);
return redirect()->route('referrals.my')->with('success', '紹介したい人を登録しました。');
}
/**
* 紹介した人の一覧を表示します。
*
* @return \Illuminate\View\View
*/
public function myReferrals()
{
$userId = Auth::id();
$referrals = Referral::where('user_id', $userId)->get();
$matchedCounts = [];
foreach ($referrals as $referral) {
$areas = explode(',', $referral->areas);
$matchedCounts[$referral->id] = 0;
foreach (Referral::where('user_id', '!=', $userId)->get() as $otherReferral) {
$otherAreas = explode(',', $otherReferral->areas);
$commonAreas = array_intersect($areas, $otherAreas);
if (!empty($commonAreas)) {
$matchedCounts[$referral->id]++;
}
}
}
return view('referrals.my', compact('referrals', 'matchedCounts'));
}
/**
* 紹介者の詳細を表示します。
*
* @param \App\Models\Referral $referral
* @return \Illuminate\View\View
*/
public function show(Referral $referral)
{
if ($referral->user_id != Auth::id()) {
abort(403, 'Unauthorized action.');
}
$areas = explode(',', $referral->areas);
$matchedReferrals = [];
foreach (Referral::where('user_id', '!=', Auth::id())->get() as $otherReferral) {
$otherAreas = explode(',', $otherReferral->areas);
$commonAreas = array_intersect($areas, $otherAreas);
if (!empty($commonAreas)) {
$matchedReferrals[] = $otherReferral;
}
}
return view('referrals.show', compact('referral', 'matchedReferrals'));
}
/**
* 紹介した人のマッチング一覧を表示します。
*
* @return \Illuminate\View\View
*/
public function index()
{
return $this->myReferrals();
}
}
4. Routingの追加
routes/web.php ファイルを以下のように編集します。
ここまでの全てのルーティングは
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AdminController;
use App\Http\Controllers\Auth\AdminLoginController;
use App\Http\Controllers\Auth\AdminForgotPasswordController;
use App\Http\Controllers\Auth\AdminResetPasswordController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ReferralController;
use App\Http\Controllers\FlyerController;
Route::prefix('admin')->group(function () {
// Admin ログインルート
Route::get('login', [AdminLoginController::class, 'showLoginForm'])->name('admin.login');
Route::post('login', [AdminLoginController::class, 'login'])->name('admin.login.submit');
Route::post('logout', [AdminLoginController::class, 'logout'])->name('admin.logout');
// Admin パスワードリセットルート
Route::get('password/reset', [AdminForgotPasswordController::class, 'showLinkRequestForm'])->name('admin.password.request');
Route::post('password/email', [AdminForgotPasswordController::class, 'sendResetLinkEmail'])->name('admin.password.email');
Route::get('password/reset/{token}', [AdminResetPasswordController::class, 'showResetForm'])->name('admin.password.reset');
Route::post('password/reset', [AdminResetPasswordController::class, 'reset'])->name('admin.password.update');
// Admin ダッシュボードルート
Route::get('dashboard', [AdminController::class, 'index'])->name('admin.dashboard')->middleware('auth:admin');
// Admin 管理ルート
Route::get('admins', [AdminController::class, 'index'])->name('admin.admins.index')->middleware('auth:admin');
Route::get('admins/create', [AdminController::class, 'create'])->name('admin.admins.create')->middleware('auth:admin');
Route::post('admins', [AdminController::class, 'store'])->name('admin.admins.store')->middleware('auth:admin');
Route::get('admins/{admin}', [AdminController::class, 'show'])->name('admin.admins.show')->middleware('auth:admin');
Route::get('admins/{admin}/edit', [AdminController::class, 'edit'])->name('admin.admins.edit')->middleware('auth:admin');
Route::put('admins/{admin}', [AdminController::class, 'update'])->name('admin.admins.update')->middleware('auth:admin');
Route::delete('admins/{admin}', [AdminController::class, 'destroy'])->name('admin.admins.destroy')->middleware('auth:admin');
// パーミッション管理ルート
Route::get('permissions', [AdminController::class, 'permissions'])->name('admin.permissions.index')->middleware('auth:admin');
Route::post('permissions', [AdminController::class, 'updatePermissions'])->name('admin.permissions.update')->middleware('auth:admin');
// Admin がユーザを管理するルート
Route::get('users', [UserController::class, 'adminIndex'])->name('admin.users.index')->middleware('auth:admin');
Route::get('users/{user}', [UserController::class, 'adminShow'])->name('admin.users.show')->middleware('auth:admin');
Route::post('users/{user}/approve', [UserController::class, 'approveUser'])->name('admin.users.approve')->middleware('auth:admin');
});
// ユーザの会員登録、ログイン、パスワードリセットルート
Route::get('user/signup', [UserController::class, 'showSignupForm'])->name('user.signup');
Route::post('user/signup', [UserController::class, 'registerUser'])->name('user.register');
Route::get('user/activate/{token}', [UserController::class, 'activateUser'])->name('user.activate');
Route::get('user/reset-password/{token}', [UserController::class, 'showResetPasswordForm'])->name('password.reset');
Route::post('user/reset-password', [UserController::class, 'resetPassword'])->name('password.update');
// ユーザのログインページ
Route::get('login', [UserController::class, 'showLoginForm'])->name('login');
Route::post('login', [UserController::class, 'login']);
// 認証が必要なルートグループ
Route::middleware(['auth'])->group(function () {
Route::get('dashboard', [UserController::class, 'showDashboard'])->name('user.dashboard');
// 紹介したい人のルート
Route::get('referrals', [ReferralController::class, 'index'])->name('referrals.index');
Route::get('referrals/create', [ReferralController::class, 'create'])->name('referrals.create');
Route::post('referrals', [ReferralController::class, 'store'])->name('referrals.store');
Route::get('referrals/{referral}', [ReferralController::class, 'show'])->name('referrals.show');
Route::get('my-referrals', [ReferralController::class, 'myReferrals'])->name('referrals.my');
});
これです。
5. Viewの作成
resources/views/referrals/create.blade.php
@extends('layouts.user')
@section('content')
<div class="container">
<h1>紹介したい人を登録する</h1>
<form method="POST" action="{{ route('referrals.store') }}">
@csrf
<div class="form-group">
<label for="nickname">ニックネーム</label>
<input type="text" name="nickname" id="nickname" class="form-control" required>
</div>
<div class="form-group">
<label for="areas">活動エリア(都道府県 市区町村)</label>
<input type="text" name="areas" id="areas" class="form-control" placeholder="例: 東京, 大阪" required>
</div>
<div class="form-group">
<label for="introduction">この人の紹介</label>
<textarea name="introduction" id="introduction" class="form-control" rows="4" required></textarea>
</div>
<div class="form-group">
<label for="work_introduction">この人の仕事関係の紹介</label>
<textarea name="work_introduction" id="work_introduction" class="form-control" rows="2" required></textarea>
</div>
<button type="submit" class="btn btn-primary">登録</button>
</form>
</div>
@endsection
resources/views/referrals/index.blade.php
@extends('layouts.user')
@section('content')
<div class="container">
<h1>マッチした紹介者一覧</h1>
@foreach ($matchedReferrals as $referralId => $matched)
<h2>紹介者ID: {{ $referralId }}</h2>
<ul>
@foreach ($matched as $referral)
<li>
ニックネーム: {{ $referral->nickname }},
活動エリア: {{ $referral->areas }},
紹介: {{ $referral->introduction }},
仕事紹介: {{ $referral->work_introduction }}
</li>
@endforeach
</ul>
@endforeach
</div>
@endsection
resouces/views/referrals/my.blade.php
@extends('layouts.user')
@section('content')
<div class="container">
<h1>自分が紹介した人の一覧</h1>
<table class="table">
<thead>
<tr>
<th>名前</th>
<th>マッチした数</th>
<th>詳細</th>
</tr>
</thead>
<tbody>
@foreach ($referrals as $referral)
<tr>
<td>{{ $referral->nickname }}</td>
<td>{{ $matchedCounts[$referral->id] }}</td>
<td><a href="{{ route('referrals.show', $referral) }}" class="btn btn-primary">詳細</a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
show.blade.phpも作ります、
@extends('layouts.user')
@section('content')
<div class="container">
<h1>{{ $referral->nickname }} の詳細</h1>
<p><strong>活動エリア:</strong> {{ $referral->areas }}</p>
<p><strong>紹介:</strong> {{ $referral->introduction }}</p>
<p><strong>仕事関係の紹介:</strong> {{ $referral->work_introduction }}</p>
<h2>マッチした紹介者</h2>
<ul>
@foreach ($matchedReferrals as $matchedReferral)
<li>
<p><strong>名前:</strong> {{ $matchedReferral->nickname }}</p>
<p><strong>活動エリア:</strong> {{ $matchedReferral->areas }}</p>
<p><strong>紹介:</strong> {{ $matchedReferral->introduction }}</p>
<p><strong>仕事関係の紹介:</strong> {{ $matchedReferral->work_introduction }}</p>
</li>
@endforeach
</ul>
</div>
@endsection
これで、新しい「紹介したい人」機能が追加されます。
できましたね
【最新!Laravel 11】しっかり体系的に学ぶ Laravel 11 基礎講座
受講生: 139人 | 評価: 4.2/5 | 現在の価格: ¥4,200
Laravel 11を基礎から体系的に学べるコースです。PHP 8.2やcomposerの知識が必要です。
PHPからLaravelまで サーバーサイドをとことんやってみよう
受講生: 14,259人 | 評価: 4.5/5 | 現在の価格: ¥10,000
PHP初歩からLaravelまでを網羅したコース。初心者から脱初心者を目指す方におすすめです。