Open takanori-matsushita opened 4 years ago
リスト 10.1: ユーザーのeditアクション app/Http/Controllers/UsersController.php
public function edit($id)
{
$user = User::find($id);
return view('users.edit', ["user" => $user]);
}
リスト 10.2: ユーザーのeditビュー
viewファイルの作成 resources/views/users/edit.blade.php
@php
$title = 'Edit user';
@endphp
@extends('layouts.layout')
@section('content')
<div class="row">
<div class="col-md-6 offset-md-3">
<form action="{{route('users.update', ['user' => $user->id])}}" method="post">
@method('patch')
@csrf
@include('shared.error_messages')
<label for="name">Name</label>
<input type="text" name="name" class="form-control @error('name') is-invalid @enderror" value="{{old('name', $user->name)}}">
<label for="email">Email</label>
<input type="text" name="email" class="form-control @error('email') is-invalid @enderror" value="{{old('email', $user->email)}}">
<label for="password">Password</label>
<input type="password" name="password" class="form-control @error('password') is-invalid @enderror">
<label for="password_confirmation">Password Confirmation</label>
<input type="password" name="password_confirmation" class="form-control">
<input type="submit" value="Save changes" class="btn btn-primary">
</form>
<div class="gravatar_edit">
<img src={{gravator_for($user)}}>
<a href="http://gravatar.com/emails" target="_blank" rel="noopener">change</a>
</div>
</div>
</div>
@endsection
oldメソッドは、エラーだった際に前回の入力値を保持するものだったが、第二引数にデフォルトで表示させたい値を入力することができる。 上記のようにすることで、編集ページにアクセスした際、データベースに保存されている値が表示される。 バリデーションに引っかかった際は、フォームへ送った値が保持されて再度レンダリングされる。
リスト 10.4: レイアウトの “Settings” リンクを更新する resources/views/layouts/header.blade.php Settingsのhref部分の実装
<a class="dropdown-item" href="{{route('users.edit', ['user'=>Auth::id()])}}">Settings</a>
ここで、['user'=>Auth::id()]
に注目する。
基本的にMVCアーキテクチャーでviewの中に、ロジックを組み込むのは、メンテナンス性が良くない。
そのため、Laravelではビューコンポーザを利用する。手順としては、
となる。いかに手順を記述する。
app/Http/Composers/AuthIdComposer.php
<?php
namespace App\Http\Composers;
use Illuminate\View\View;
class AuthIdComposer
{
public function compose(View $view)
{
$view->with('auth_id', \Auth::id());
}
}
php artisan make:provider GetAuthIdServiceProvider
app/Providers/GetAuthIdServiceProvider.php
VIewファサードの利用と、bootメソッドを追加する。
use Illuminate\Support\Facades\View;
:
:
public function boot()
{
View::composer(
'layouts.header',
'App\Http\Composers\AuthIdComposer'
);
}
/*
*View Composer Service Providers...
*/
App\Providers\GetAuthIdServiceProvider::class,
これで['user'=>Auth::id()]
を$auth_id
変数に代入される。最終的には下記コードになる。
resources/views/layouts/header.blade.php
<header>
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a href={{route('root')}} id="logo">Sample app</a>
<ul class="navbar-nav">
<li class="nav-item"><a href={{route('root')}} class="nav-link">Home</a></li>
<li class="nav-item"><a href={{route('help')}} class="nav-link">Help</a></li>
@auth
<li class="nav-item dropdown" dusk="Users">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Users
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{route('users.show', $auth_id)}}">Profile</a> //ビューコンポーザを利用した値の取得
<a class="dropdown-item" href="{{route('users.edit', $auth_id)}}">Settings</a> //ビューコンポーザを利用した値の取得
<div class="dropdown-divider"></div>
<form action="{{route('logout')}}" method="post">
@csrf
<button type="submit" class="dropdown-item" dusk="Logout">Logout</button>
</form>
</div>
</li>
@else
<li class="nav-item"><a href="{{route('login')}}" class="nav-link">Login</a></li>
@endauth
</ul>
</div>
</nav>
</header>
演習
resources/views/users/edit.blade.php
<a href="http://gravatar.com/emails" target="_blank" rel="noopener">change</a>
app/Http/Controllers/UsersController.php updateアクションの追加
use App\Http\Requests\UserFormRequest;
use Illuminate\Support\Facades\Hash;
:
class UsersController extends Controller
{
public function update(UserFormRequest $request, User $user)
{
$user->name = $request->name;
$user->email = $request->email;
$user->password = Hash::make($request->password);
$user->password_confirmation = Hash::make($request->password_confirmation);
$user->save();
}
以前のユーザーのテストで作成したバリデーションを読み込むために、UserFormRequestを引数として指定。
このバリデーションのままだと、メールアドレスが既に存在するエラーがキャッチされるため、以下のように変更。 app/Http/Requests/UserFormRequest.php
use Illuminate\Validation\Rule;
class UserFormRequest extends FormRequest
{
:
public function rules()
{
return [
"name" => "required|max:50",
"email" => [
"required",
"max:255",
"email:filter",
Rule::unique('users')->ignore(\Auth::id()),
],
"password" => "required|min:6"
];
}
Ruleのignoreメソッドで、ログインしているidの重複を除外する。
リスト 10.9: 編集の失敗に対するテストgreen
テストファイルの作成
php artisan dusk:make UsersEditTest
tests/Browser/UsersEditTest.php
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class UsersEditTest extends DuskTestCase
{
use DatabaseMigrations; //テスト前のマイグレーションを実行する
/**
* A Dusk test example.
*
* @return void
*/
public function testUnsuccessfulEdit()
{
$user = $this->registerUser(); //DuskTestCaseに記述したメソッドの呼び出し
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->assertSeeLink('Sign up now!')
->type('email', $user->email)
->type('password', 'password')
->press('Log in!')
->click('@Users')
->click('@Settings')
->type('name', '')
->type('email', 'foo@invalid')
->type('password', 'foo')
->press('Save changes')
->type('password_confirmation', 'bar')
->assertPathIs('/users/1/edit');
});
}
}
リスト 10.10: green
php artisan dusk
演習
->assertSee('The form contains 3 errors');
リスト 10.11: 編集の成功に対するテストred tests/Browser/UsersEditTest.php
public function testSuccessfulEdit()
{
$user = $this->registerUser();
$this->browse(function (Browser $browser) use ($user) {
$browser
->click('@Users')
->click('@Settings')
->type('name', 'Foo Bar')
->type('email', 'foo@bar.com')
->type('password', '')
->type('password_confirmation', '')
->press('Save changes')
->assertPathIs('/users/1')
->assertSee('Profile updated')
->script('location.reload();');
$browser
->assertDontSee('Profile updated');
});
}
scriptメソッドでページをリロードしている。これはフラッシュメッセージのテストでページ更新後に指定のテキストがなくなっていることをチェックしている。 scriptメソッドを使うと、メソッドチェーンが使えなくなるため、再度$browserを使っている。
リスト 10.12: ユーザーのupdateアクション red app/Http/Controllers/UsersController.php
public function update(UserFormRequest $request, User $user)
{
$user->name = $request->name;
$user->email = $request->email;
$user->password = Hash::make($request->password);
$user->save();
session()->flash('success', 'Profile updated'); //フラッシュメッセージ
return redirect()->route('users.show', \Auth::id()); //詳細ページへリダイレクト
}
リスト 10.13: パスワードが空のままでも更新できるようにする green 複数のファイルを編集して実装する。 app/Http/Requests/UserFormRequest.php
public function rules(Request $request)
{
$password = [];
if ($request->method() === 'POST') {
$password = [
"min:6",
"required"
];
}
return [
"name" => "required|max:50",
"email" => [
"required",
"max:255",
"email:filter",
Rule::unique('users')->ignore(\Auth::id()),
],
"password" => $password
];
}
HTTPメソッドがPOST(新規登録)の場合は、passwordのバリデーションを設定し、PATCH(更新)の場合は、passwordのバリデーションを外す処理を記述している。
しかし、このままだと、空のパスワードがデータベースに登録されてしまうため、updateアクションの処理を以下のように変更する。 app/Http/Controllers/UsersController.php
public function update(UserFormRequest $request, User $user)
{
$user->name = $request->name;
$user->email = $request->email;
if (!empty($user->password)) {
$user->password = Hash::make($request->password);
}
$user->save();
session()->flash('success', 'Profile updated');
return redirect()->route('users.show', \Auth::id());
}
emptyメソッドで値が存在するかチェックする。値がある場合はfalseとなるが、!を使っているのでtrueとなり、パスワードの更新処理が走る。
これに伴い、バリデーションエラーが変わるため、テストファイルを再度編集。 tests/Browser/UsersEditTest.php
public function testUnsuccessfulEdit()
{
:
->assertSee('The form contains 2 errors'); //3を2に変更する。
}
リスト 10.13: パスワードが空のままでも更新できるようにする green 複数のファイルを編集して実装する。 app/Http/Requests/UserFormRequest.php
public function rules(Request $request)
{
$password = [];
if ($request->method() === 'POST') {
$password = [
"min:6",
"required"
];
}
return [
"name" => "required|max:50",
"email" => [
"required",
"max:255",
"email:filter",
Rule::unique('users')->ignore(\Auth::id()),
],
"password" => $password
];
}
HTTPメソッドがPOST(新規登録)の場合は、passwordのバリデーションを設定し、PATCH(更新)の場合は、passwordのバリデーションを外す処理を記述している。
しかし、このままだと、空のパスワードがデータベースに登録されてしまうため、updateアクションの処理を以下のように変更する。 app/Http/Controllers/UsersController.php
public function update(UserFormRequest $request, User $user)
{
$user->name = $request->name;
$user->email = $request->email;
if (isset($user->password)) {
$user->password = Hash::make($request->password);
}
$user->save();
session()->flash('success', 'Profile updated');
return redirect()->route('users.show', \Auth::id());
}
issetメソッドで変数の値が存在するかチェックする。値がある場合はtrueとなるので、パスワードの更新処理が走る。
リスト 10.14: green
php artisan dusk
リスト 10.15: beforeフィルターにlogged_in_userを追加する green app/Http/Controllers/UsersController.php laravel/uiのAuthのmiddlewareが用意されているためそちらを使用する。
class UsersController extends Controller
{
public function __construct()
{
$this->middleware('auth', [
'only' => ['edit', 'update']
]);
}
:
}
コンストラクタで、指定のアクションに対して、処理を実装する。
リスト 10.16: green
php artisan dusk
duskテストでは、greenとなり、アクセス制限のテストができないため、以下のテストを追加で記述する。
リスト 10.17: テストユーザーでログインする green duskテストでは、ログインしていない際に編集ページにアクセスするテストを別途記述する。これは、ログインする前に実装したほうが良いので、testUnsuccessfulEditメソッドの上に記述する。 tests/Browser/UsersEditTest.php
public function testAuthenticateCantAccessEdit()
{
$this->browse(function (Browser $browser) {
$browser->click('@Logout')
->visit('/users/1/edit')
->assertPathIs('/login');
});
}
以下省略
リスト 10.23: fixtureファイルに2人目のユーザーを追加する tests/DuskTestCase.php
protected function anyUsers()
{
$user1 = $this->registerUser();
$user2 = factory(User::class)->create([
"name" => "Sterling Archer",
"email" => "duchess@example.gov",
"password" => Hash::make('password')
]);
return [$user1, $user2];
}
複数のユーザーを登録する関数を定義する。
リスト 10.24: 間違ったユーザーが編集しようとしたときのテスト red tests/Browser/UsersControllerTest.php
class UsersControllerTest extends DuskTestCase
{
use DatabaseMigrations;
:
public function testShouldRedirectEditWhenLoggedInAsWrongUser()
{
$users = $this->anyUsers();
$this->browse(function (Browser $browser) use ($users) {
$browser->visit('login')
->type('email', $users[0]->email)
->type('password', 'password')
->press('Log in!')
->visit('/users/2/edit')
->assertPathIs('/');
});
}
}
リスト 10.25: beforeフィルターを使って編集/更新ページを保護する green
Larabelでフィルターを作るには、policyを作成する。
php artisan make:policy UserPolicy --model=User
--model=UserでUserモデルポリシーの雛形を作成する。
app/Policies/UserPolicy.php
public function update(User $auth, User $user)
{
return $auth->id == $user->id;
}
ログイン中のユーザーIDとページのIDを比較する。
ポリシーの登録をする。 app/Providers/AuthServiceProvider.php
protected $policies = [
'App\User' => 'App\Policies\UserPolicy',
];
コントローラーに認可の処理を記述する。 app/Http/Controllers/UsersController.php
public function edit(User $user)
{
$auth = auth()->user();
//ログイン中のユーザーが自身のユーザーページと一致するかチェックし、処理を切り替える。
return ($user->can('update', $auth)) ? view('users.edit', compact('user')) : redirect()->route('root');
}
public function update(UserFormRequest $request, User $user)
{
$auth = auth()->user();
if($user->can('update', $auth)) {
$user->name = $request->name;
$user->email = $request->email;
if (!empty($request->password)) {
$user->password = Hash::make($request->password);
}
$user->save();
session()->flash('success', 'Profile updated');
return redirect()->route('users.show', \Auth::id());
}
return redirect()->route('/')
}
authorizeメソッドでポリシーのメソッドを第一引数に指定する。第二引数で現在アクセスしているページのパラメータをポリシーに引数として渡す。
リスト 10.26: green
php artisan dusk
LaravelではLaravel/uiですでに実装されているため、テストコードもgreenとなる。
リスト 10.29: フレンドリーフォワーディングのテスト green tests/Browser/UsersEditTest.php ブラウザテストでの実装のため、一度ログアウト処理を記述する。
public function testLogout()
{
$user = $this->registerUser();
$this->browse(function (Browser $browser) {
$browser->click('@Users')
->press('Logout')
->assertPathIs('/');
});
}
public function testSuccessfulEditWithFriendlyForwarding()
{
$user = $this->registerUser();
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/users/1/edit')
->assertPathIs('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Log in!')
->assertPathIs('/users/1/edit');
});
}
以下省略
リスト 10.34: indexアクションのリダイレクトをテストする red
public function testShouldRedirectIndexWhenNotLoggedIn()
{
$users = $this->anyUsers();
$this->browse(function (Browser $browser) use ($users) {
$browser->visit(route('users.index'))
->assertPathIs('/login');
});
}
リスト 10.35: indexアクションにはログインを要求する green app/Http/Controllers/UsersController.php
public function __construct()
{
$this->middleware('auth', [
'only' => ['index', 'edit', 'update'] //indexを追加
]);
}
リスト 10.36: ユーザーのindexアクション app/Http/Controllers/UsersController.php
class UsersController extends Controller
{
:
public function index()
{
$users = User::all();
return view('users.index', compact('users'));
}
:
}
リスト 10.37: ユーザーのindexビュー resources/views/users/index.blade.php
@php
$title = 'All users';
@endphp
@extends('layouts.layout')
@section('content')
<h1>All users</h1>
<ul class="users">
@foreach($users as $user)
<li>
<img src={{gravator_for($user, ['size'=>50])}}>
<a href="{{route('users.show', compact('user'))}}">{{$user->name}}</a>
</li>
@endforeach
</ul>
@endsection
リスト 10.39: ユーザーのindexページ用のCSS resources/sass/_custom.scss
/* Users index */
.users {
list-style: none;
margin: 0;
li {
overflow: auto;
padding: 10px 0;
border-bottom: 1px solid $gray-medium-light;
}
}
以下のコマンドを実行
yarn dev
リスト 10.40: ユーザー一覧ページへのリンクを更新する resources/views/layouts/header.blade.php
<header>
:
:
@auth
<li class="nav-item"><a href="{{route('users.index')}}" class="nav-link">Users</a></li> //追加
<li class="nav-item dropdown" dusk="Users">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Account
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{route('users.show', $auth_id)}}">Profile</a>
<a class="dropdown-item" href="{{route('users.edit', $auth_id)}}" dusk="Settings">Settings</a>
<div class="dropdown-divider"></div>
<form action="{{route('logout')}}" method="post">
@csrf
<button type="submit" class="dropdown-item" dusk="Logout">Logout</button>
</form>
</div>
</li>
@else
<li class="nav-item"><a href="{{route('login')}}" class="nav-link">Login</a></li>
@endauth
:
:
</header>
リスト 10.41: green
php artisan dusk
演習 tests/Browser/SiteLayoutBrowserTest.php
class SiteLayoutBrowserTest extends DuskTestCase
{
use DatabaseMigrations;
:
public function testAuthorizeHeaderMenu()
{
$user = $this->registerUser();
$this->browse(function (Browser $browser) use ($user) {
$browser->visit(route('root'))
->assertSeeLink('Home')
->assertSeeLink('Help')
->assertSeeLink('Login')
->assertDontSeeLink('Users')
->assertDontSee('Account')
->visit(route('login'))
->type('email', $user->email)
->type('password', 'password')
->press('Log in!')
->assertDontSeeLink('Login')
->assertSeeLink('Users')
->assertSee('Account');
});
}
}
リスト 10.43: データベース上にサンプルユーザーを生成するRailsタスク
Laravelでは、factoryという機能が標準で搭載されているため、それを利用する。UserFactoryは、初めから用意されているため、以下のコマンドは打たなくても良い。
factoryの作成
php artisan make:factory 作成したい名前
database/factories/UserFactory.php
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => Hash::make('password'),
];
シーダーの作成
php artisan make:seeder UsersTableSeeder
<?php
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(\App\User::class)->create([
"name" => "Example User",
"email" => "example@railstutorial.org",
"password" => Hash::make('password')
]);
factory(\App\User::class, 99)->create();
}
}
シーダーの登録 database/seeds/DatabaseSeeder.php
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
}
}
ファクトリー・シーダーの作成が完了したら以下のコマンドでシーダーを実行する。
php artisan migrate:refresh --seed
リスト 10.45: indexページでpaginationを使う resources/views/users/index.blade.php
@php
$title = 'All users';
@endphp
@extends('layouts.layout')
@section('content')
<h1>All users</h1>
{{$users->links()}} //ページネーションを表示する
<ul class="users">
@foreach($users as $user)
<li>
<img src={{gravator_for($user, ['size'=>50])}}>
<a href="{{route('users.show', compact('user'))}}">{{$user->name}}</a>
</li>
@endforeach
</ul>
{{$users->links()}} //ページネーションを表示する
@endsection
リスト 10.46: indexアクションでUsersをページネートする app/Http/Controllers/UsersController.php
public function index()
{
$users = User::paginate(30); //1ページに30件のデータを表示する
return view('users.index', compact('users'));
}
リスト 10.48: ページネーションを含めたUsersIndexのテストgreen
テストファイルの作成
php artisan dusk:make UsersIndexTest
tests/Browser/UsersIndexTest.php
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class UsersIndexTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* A Dusk test example.
*
* @return void
*/
public function testIndexIncludingPagination()
{
$users = $this->anyUsers();
$this->browse(function (Browser $browser) use ($users) {
foreach ($users as $user) {
$browser->visit(route('login'))
->type('email', $user->email)
->type('password', 'password')
->press('Log in!')
->visit(route('users.index'))
->assertPathIs('/users')
->assertPresent('ul.pagination')
->assertSeeLink($user->name)
->assertSee($user->name)
->click('@Users')
->press('Logout');
if ($user->id >= 30) {
break;
}
}
});
}
}
リスト 10.49: green
php artisan dusk
Laravelでは、コンポーネントを利用することで、デザインの使い回しをすることができる。
resources/views/users/index.blade.php
@php
$title = 'All users';
@endphp
@extends('layouts.layout')
@section('content')
<h1>All users</h1>
{{$users->links()}}
<ul class="users">
@each('components.user', $users, 'user') //ここを書き換える
</ul>
{{$users->links()}}
@endsection
@eachディレクティブで@foreachのようなことができる。第一引数に表示したいコンポネント、第二引数にデータをまとめた配列(ここでは、Userモデルのデータ)、第三引数にコンポーネント側で配列から取り出し、格納するための変数名を指定する。
コンポーネントの作成
php artisan make:component user
リスト 10.51: 各ユーザーを表示するパーシャル resources/views/components/user.blade.php
<li>
<img src={{gravator_for($user, ['size'=>50])}}>
<a href="{{route('users.show', compact('user'))}}">{{$user->name}}</a>
</li>
リスト 10.53: green
php artisan dusk
マイグレーションの作成
php artisan make:migration add_admin_to_users
リスト 10.54: boolean型のadmin属性をUserに追加するマイグレーション database/migrations/[日付]_add_admin_to_users.php
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('admin')->default(false);
});
}
boolean型はデフォルトで値を代入しないとマイグレーション実行時にエラーになるため、falseを指定する。
MySQLの場合は、指定ののカラムの後に追加できるafterメソッドが利用できる。
$table->boolean('admin')->default(false)->after('カラム名');
これで、カラム名の後に追加される。
以下コマンドでマイグレーションを実行
php artisan migrate
リスト 10.55: サンプルデータ生成タスクに管理者を1人追加する database/seeds/UsersTableSeeder.php
class UsersTableSeeder extends Seeder
{
:
public function run()
{
factory(\App\User::class)->create([
"name" => "Example User",
"email" => "example@railstutorial.org",
"password" => Hash::make('password'),
"admin" => true //adminの追加
]);
factory(\App\User::class, 99)->create();
}
以下のコマンドでシーダーを実行する
php artisan migrate:refresh --seed
Laravelでは$guard・$fillableで編集をしてもよい属性の指定ができる。
laravelで初めから用意されているUserモデルファイルには、初めから設定されているため、ここでは気にしない。$hiddenはtinkerでデータを取得した際に指定したカラムの値を表示させない設定のため、ここでadminを指定する。
app/User.php
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token', 'admin', //adminの追加
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
リスト 10.57: ユーザー削除用リンクの実装 (管理者にのみ表示される) resources/views/components/user.blade.php
<li>
<img src={{gravator_for($user, ['size'=>50])}}>
<a href="{{route('users.show', compact('user'))}}">{{$user->name}}</a>
@auth
@if (auth()->user()->admin === true)
<form action="{{route('users.destroy', compact('user'))}}" method="post" class="delete" style="display: inline">
@method('DELETE')
@csrf
|<button type="submit" value="delete" onclick="return confirm('You sure?')">delete</button>
</form>
@endif
@endauth
</li>
resources/sass/_custom.scss
:
.delete {
display: inline;
button[type="submit"] {
background: none;
border: none;
color: #3490dc;
&:hover {
color: #1d68a7;
text-decoration: underline;
}
&:focus {
outline: none;
}
}
}
yarn dev
リスト 10.58: 実際に動作するdestroyアクションを追加する app/Http/Controllers/UsersController.php
class UsersController extends Controller
{
public function __construct()
{
$this->middleware('auth', [
'only' => ['index', 'edit', 'update', 'destroy'] //destoryメソッドの追加
]);
}
:
:
public function destroy(Request $request, User $user)
{
$user = $request->user;
$user->delete();
session()->flash('success', 'User deleted');
return redirect()->route('users.index');
}
リスト 10.59: beforeフィルターでdestroyアクションを管理者だけに限定する
Laravelではプロバイダを使ってbefore,afterのアクションを実行することができる。
php artisan make:middleware AdminUser
app/Http/Middleware/AdminUser.php
<?php
namespace App\Http\Middleware;
use Closure;
class AdminUser
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (auth()->user()->admin !== true) {
return redirect()->route('root');
}
return $next($request);
}
}
app/Http/Controllers/UsersController.php
:
use app\Http\Middleware\AdminUser;
class UsersController extends Controller
{
public function __construct()
{
$this->middleware('auth', [
'only' => ['index', 'edit', 'update', 'destroy']
]);
$this->middleware(AdminUser::class, [
'only' => ['destroy']
]);
}
:
:
}
10.4.3 ユーザー削除のテスト リスト 10.60: fixture内の最初のユーザーを管理者にする tests/DuskTestCase.php
protected function registerUser()
{
return factory(User::class)->create([
"name" => "Michael Example",
"email" => "michael@example.com",
"password" => Hash::make('password')
"admin" => true //追加
]);
}
branch: updating-users