環境
- Laravel 8.6
- PHP 8.0
- Bootstrap 5.1
8.1 セッション
8.1.1 Sessionsコントローラ
ログイン処理とログアウト処理
/app/Http/Controllers/SessionsController.php
class SessionsController extends Controller
{
public function create()
{
return view("sessions.create");
}
}
/routes/web.php
Route::get('login', [SessionsController::class, 'create'])->name('login');
Route::post('login', [SessionsController::class, 'store']);
Route::delete('logout', [SessionsController::class, 'destroy'])->name('logout');
/tests/Unit/SessionsControllerTest.php
class SessionsControllerTest extends TestCase
{
public function testGetCreate()
{
$response = $this->get(route("login"));
$response->assertStatus(200);
}
}
8.1.2 ログインフォーム
/resources/views/sessions/create.blade.php
@extends('layouts.application')
@section('title', 'Log in')
@section('content')
<h1>Log in</h1>
<div class="row">
<div class="col-md-6 offset-md-3">
{{ Form::open(['route' => 'login']) }}
<div class="mb-3">
{{ Form::label('email', 'Email', ['class' => 'form-label fw-bold']) }}
{{ Form::text('email', "", ["class" => "form-control"]) }}
</div>
<div class="mb-3">
{{ Form::label('password', 'Password', ['class' => 'form-label fw-bold']) }}
{{ Form::password('password', ["class" => "form-control"]) }}
</div>
{{ Form::submit("Log in", ["class" => "btn btn-primary w-100"]) }}
{{ Form::close() }}
<p>New user? {{ Html::link(route("signup"), "Sign up now!") }}</p>
</div>
</div>
@endsection
8.1.3 ユーザーの検索と認証
/app/Http/SessionsController.php
public function store(Request $request)
{
$user = User::where("email", Str::lower($request->email))->first();
if ($user && Hash::check($request->password, $user->password)) {
// ログイン後にユーザー情報のページにリダイレクトする
} else {
// エラーメッセージを作成する
return back()->withInput();
}
}
8.1.4 フラッシュメッセージを表示する
/app/Http/Controllers/SessionsController.php
session()->flash('message', ['danger' => 'Invalid email/password combination']);
8.1.5 フラッシュのテスト
/tests/Feature/UsersLoginTest.php
public function testInvalidLogin()
{
$response = $this->get(route('login'));
$response->assertViewIs('sessions.create');
$response = $this->followingRedirects()
->post('login', [
'email' => "",
'password' => "",
]);
$response->assertViewIs('sessions.create');
$response->assertSeeText('Invalid email/password combination');
$response = $this->get('/');
$response->assertDontSeeText('Invalid email/password combination');
}
8.2 ログイン
8.2.1 log_inメソッド
/app/Http/Controllers/SessionsController.php
public function store(Request $request)
{
$user = User::where("email", Str::lower($request->email))->first();
if ($user && Hash::check($request->password, $user->password)) {
Auth::login($user);
return redirect()->route("users.show", $user);
} else {
session()->flash('message', ['danger' => 'Invalid email/password combination']);
return back()->withInput();
}
}
8.2.2 現在のユーザー
Auth::user();
8.2.3 レイアウトリンクを変更する
/resources/views/layouts/header.blade.php
<header>
<nav class="navbar navbar-expand fixed-top navbar-dark bg-dark p-0">
<div class="container">
{{ link_to_route('home', "sample app", [], ["id" => "logo", "class" => "navbar-brand"]) }}
<ul class="navbar-nav">
<li class="nav-item">{{ link_to_route("home", "Home", [], ["class" => "nav-link"]) }}</li>
<li class="nav-item">{{ link_to_route("help", "Help", [], ["class" => "nav-link"]) }}</li>
@if (auth()->check())
<li class="nav-item">{{ link_to_route("users.index", "Users", [], ["class" => "nav-link"]) }}</li>
<li class="dropdown">
<a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown">Account</a>
<ul class="dropdown-menu">
<li>{{ link_to_route("users.show", "Profile", [auth()->id()], ["class" => "dropdown-item"]) }}</li>
<li>{{ link_to_route("home", "Settings", [], ["class" => "dropdown-item"]) }}</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a href="javascript:document.logoutform.submit()" class="dropdown-item">Log out</a>
{{ Form::open(["route" => "logout", "method" => "delete", "name" => "logoutform"]) }}
{{ Form::close() }}
</li>
</ul>
</li>
@else
<li>{{ link_to_route("login", "Log in", [], ["class" => "nav-link"]) }}</li>
@endif
</ul>
</div>
</nav>
</header>
8.2.4 レイアウトの変更をテストする
/database/seedes/TestSeeder.php
class TestSeeder extends Seeder
{
public function run()
{
DB::table('users')->insert([
'name' => "Michael Example",
'email' => strtolower("michael@example.com"),
'password' => bcrypt('password'),
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
]);
}
}
/tests/Feature/UsersLoginTest.php
class UsersLoginTest extends TestCase
{
private $user;
private $user_pass;
protected function setUp()
{
parent::setUp();
Artisan::call('migrate:fresh');
$this->seed('TestSeeder');
$this->user = User::find(1);
$this->user_pass = "password";
}
...
public function testValidLogin()
{
$this->get(route("login"));
$response = $this->followingRedirects()
->post(route("login"), [
'email' => $this->user->email,
'password' => $this->user_pass
]);
$response->assertViewIs('users.show');
$dom = $this->dom($response->content());
$this->assertSame(0, $dom->filter('a:contains("Log in")')->count());
$this->assertSame(route("logout"), $dom->filter('form[name="logoutform"]')->attr("action"));
$this->assertSame(route("users.show", $this->user), $dom->filter('a:contains("Profile")')->attr("href"));
}
}
/tests/Feature/UsersSignupTest.php
class UsersSignupTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
Artisan::call('migrate:fresh');
}
テストで使用するDBを指定する
/phpunit.xml
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
8.2.5 ユーザー登録時にログイン
/app/Http/Controllers/UsersController.php
public function store(Request $request)
{
$request->email = Str::lower($request->email);
$request->validate([
"name" => "required|max:50",
"email" => "required|max:255|email|unique:users|email",
"password" => "required|confirmed|min:6",
"password_confirmation" => "required|min:6",
]);
$user = new User;
$user->name = $request->name;
$user->email = $request->email;
$user->password = bcrypt($request->password);
$user->save();
Auth::login($user);
session()->flash("message", ['success' => 'Welcome to the Sample App!']);
return redirect()->route("users.show", $user);
}
テスト追加
/tests/Feature/UsersSignupTest.php
$this->assertTrue(Auth::check());
8.3 ログアウト
/app/Http/Controllers/SessionsController.php
public function destroy()
{
Auth::logout();
return redirect("/");
}
/tests/Feature/UsersLoginTest.php
public function testValidLoginToLogout()
{
$this->get(route("login"));
$response = $this->followingRedirects()
->post(route("login"), [
'email' => $this->user->email,
'password' => $this->user_pass
]);
$this->assertTrue(Auth::check());
$response->assertViewIs('users.show');
$dom = $this->dom($response->content());
$this->assertSame(0, $dom->filter('a:contains("Log in")')->count());
$this->assertSame(route("logout"), $dom->filter('form[name="logoutform"]')->attr("action"));
$this->assertSame(route("users.show", $this->user), $dom->filter('a:contains("Profile")')->attr("href"));
$response = $this->followingRedirects()->delete(route("logout"));
$this->assertFalse(Auth::check());
$response->assertViewIs("static_pages.home");
$dom = $this->dom($response->content());
$this->assertSame(route("login"), $dom->filter('a:contains("Log in")')->attr("href"));
$this->assertSame(0, $dom->filter('form[name="logoutform"]')->count());
$this->assertSame(0, $dom->filter('a:contains("Profile")')->count());
}