【Laravel】画像アップロード、管理者権限、パスワード確認欄、ページングを追加する
はじめに
CRUD機能を追加した状態として進めます。
画像をアップロードしてユーザーアイコンの設定、パスワード確認欄、管理者権限、ソフトデリート、認証機能を追加したいと思います。
初回の管理者権限についてはブラウザからは選択できないようにしています。
マイグレーション作成
users
テーブルを更新するマイグレーションを作成してください。
php artisan make:migration add_columns_to_users_table --table=users
マイギュレーションの詳しい内容はこちらの記事にまとめています。
管理者フラグ(is_admin
)がboolean
型ではない理由は、string
型を使用すると、1には削除権限あり、2には編集権限あり、0は全ての権限ありなどのように将来的に管理者の種類をさらに細かく区分することが可能なためです。
<?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('image')->nullable();
$table->string('is_admin');
$table->softDeletes();
$table->dropUnique('users_email_unique');
$table->unique(['email', 'deleted_at'], 'users_email_unique');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('image');
$table->dropColumn('is_admin');
$table->dropSoftDeletes();
$table->dropUnique('users_email_unique');
$table->unique('email', 'users_email_unique');
});
}
};
softDeletes
テーブルにdeleted_at
カラムを追加するためのものです。deleted_at
カラムは、ソフトデリート機能を実装するために使用されます。
これによって削除した場合、deleted_at
カラムに削除した時点のタイムスタンプが値に保存されるようになっています。
ソフトデリートとは、データベースからレコードを物理的に削除するのではなく、削除されたことを示すために特定のフラグやカラムを設定する方法です。
dropUnique('users_email_unique')
データベースのテーブルから特定のタイプのインデックス情報(制約)を削除する方法の一つです。
インデックスとは、データベース内のテーブルにおいて、検索の高速化やデータの一貫性を確保するための制約です。
今回は、email
カラムに同じメールアドレスの重複を防ぐために使用(一意性制約)されていた条件を下記の理由から一度削除しています。
ソフトデリートを追加することで、削除されたデータはdeleted_at
にタイムスタンプが値に保存されてデータベースに残るようになります。
しかし、ソフトデリート追加前に設定した一意性制約がemail
カラムに設定されている場合、deleted_at
の値に関わらず、同じemail
の値を持つ新しいレコードの挿入が許可されず、ソフトデリートされたユーザーのemail
と同じ値を登録しようとするとエラーが発生してしまいます。
これを解決するためにdropUnique('users_email_unique')
で一度、ソフトデリート追加前に設定した一意性制約を削除した後にdeleted_at
を含めた新しい一意性制約を設定することが必要になります。
マイグレーションの実行
マイグレーションを実行してテーブルにカラムの追加を行ってください。
// sailを使用している場合
sail artisan migrate
コンテナ内でマイグレーションを実行する場合はコンテナ内に入ってから下記を実行してください。
php artisan migrate
マイグレーションファイルの内容がデータベースに反映されます。設定した通りにテーブルが作成されているか確認してください。
制約を表示する
SHOW INDEXES FROM <データベース名>.<テーブル名>;
下記はデータベースのlaravelスキーマ内にあるusers
テーブルのインデックスを表示するMySQLのSQLコマンドです。
SHOW INDEXES FROM laravel.users;
ページングの作成
ダミーデータの挿入
ファクトリの設定
今回は既に作成してある、database/factories/UserFactory.php
を使用しましたが作成するには次のコマンドを実行してください。
php artisan make:factory UserFactory --model=User
下記のようにモデルに合わせて内容を変更してください。
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(30),
'is_admin' => '0',
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
シーダーの作成
今回は既に作成してある、database/seeders/DatabaseSeeder.php
を使用しましたが作成するには次のコマンドを実行してください。
php artisan make:seeder UsersTableSeeder
下記のようにモデルに合わせて内容を変更してください。
<?php
namespace Database\Seeders;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
User::factory(30)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
}
}
定義したシーダーを実行して、データベースに30件の新しいユーザが挿入されているか確認してください。
sail artisan db:seed --class=DatabaseSeeder
リクエストの修正
登録用のリクエスト
<?php
namespace App\Http\Requests\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->whereNull('deleted_at'),
],
'password' => ['required', 'string', 'min:8', 'regex:/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[@$!%*?&])/', 'confirmed'],
'image' => [
'image',
'max:1024',
'mimes:jpg,png',
],
'is_admin' => 'nullable|string|max:10',
];
}
/**
* Get the error messages for the defined validation rules.
*/
public function messages(): array
{
return [
'password.regex' => 'パスワードは少なくとも1つの半角英字、数字、および記号(@$!%*?&)を含む必要があります。',
];
}
}
更新用のリクエスト
<?php
namespace App\Http\Requests\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateUserRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules(): array
{
$userId = $this->user->id;
$rules = [
'name' => 'required|string|max:255',
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($userId)->whereNull('deleted_at'),
],
'image' => [
'image',
'max:1024',
'mimes:jpg,png',
],
'is_admin' => 'required|string|max:10',
];
if (!empty($this->input('password'))) {
$rules['password'] = ['sometimes', 'required', 'string', 'min:8', 'regex:/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[@$!%*?&])/', 'confirmed'];
}
return $rules;
}
public function messages(): array
{
return [
'password.regex' => 'パスワードは少なくとも1つの半角英字、数字、および記号(@$!%*?&)を含む必要があります。',
];
}
}
sometimes
フィールドが存在する場合にのみ、そのフィールドにバリデーションルールを適用するために使用されます。
confirmed
指定されたフィールドがその名前のフィールドの確認用であることを示します。
今回の場合では、password
フィールドの値がconfirmed
ルールによってpassword_confirmation
フィールド(ビューの修正で確認してください)と一致することがバリデーションになっています。
認可を定義する
認可はAuthServiceProvider
というサービスプロバイダー内に記述します。これは、アプリケーションの認証と認可の設定を集中管理するための場所です。
boot
メソッドで定義した認可ロジックは、アプリケーション全体で利用可能になります。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Gate::define('admin', function ($user) {
return $user->is_admin === '1'; // is_adminが '1' の場合に true を返す
});
Gate::define('edit_update', function ($user, $authUser, $editingUser) {
return $authUser->is_admin === '1' || $authUser->id === $editingUser->id;
});
}
}
Gate
app/Providers/AppServiceProvider.php
に記載しているGate
は必ず第一引数にGateは必ず第一引数にログインしているユーザーインスタンスを受け取っています。
下記のように第二引数までしか記載していない場合でも
Gate::authorize('show_edit_update', [auth()->user()]);
AppServiceProvider.php
では下記のように受け取ることができます。
public function boot(): void
{
Gate::define('show_edit_update', function ($user, $authUser, $editingUser) {
return $authUser->is_admin === '1' || $authUser->id === $editingUser->id;
});
}
コントローラーの修正
各メソッドに認可などを追加してください。
<?php
namespace App\Http\Controllers;
use App\Http\Requests\User\UpdateUserRequest;
use App\Http\Requests\User\UserRequest;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
class UsersController
{
/**
* Display a listing of the resource.
*/
public function index()
{
$authUser = auth()->user();
if (Gate::allows('admin', $authUser)) {
$users = User::paginate(5); // 管理者はすべてのユーザーを5件ごとに表示
} else {
$users = User::where('id', $authUser->id)->get();; // 管理者でない場合は自分の情報のみ表示なのでページングは不要
}
return view('users.index', compact('users', 'authUser'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
Gate::authorize('admin');
return view('users.create'); // ここで 'create.blade.php' ビューを返します
}
/**
* Store a newly created resource in storage.
*/
public function store(UserRequest $request)
{
Gate::authorize('admin');
DB::beginTransaction();
try {
$user = new User();
// 投稿フォームから送信されたデータを取得し、インスタンスの属性に代入する
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->password = $request->input('password');
$user->is_admin = $request->input('is_admin', '0'); // is_adminに初期値として'0'を代入
$user->save();
// 画像がアップロードされているか確認し、保存
if ($request->hasFile('image')) {
$file = $request->file('image');
$filename = date('Ymd_His') . '_user_id_' . $user->id . '.' . $file->getClientOriginalExtension(); // タイムスタンプをファイル名に使用
$file->storeAs('public/images', $filename); // imagesディレクトリにファイルを保存
$user->image = $filename;
$user->save();
}
DB::commit();
return view('users.create', compact('user'));
} catch (\Exception $e) {
DB::rollBack();
return back()->with('message', '登録に失敗しました。' . $e->getMessage());
}
}
/**
* Display the specified resource.
*/
public function show(User $user)
{
$authUser = auth()->user();
if (Gate::allows('admin', $authUser) || auth()->user()->id === $user->id) {
return view('users.show', compact('user'));
}
// それ以外の場合は403権限なしを表示する
abort(403, 'Forbidden');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(User $user)
{
Gate::authorize('edit_update', [auth()->user(), $user]);
return view('users.edit', compact('user'));
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateUserRequest $request, User $user)
{
Gate::authorize('edit_update', [auth()->user(), $user]);
$data = $request->validated();
$passwordChanged = false;
if (!empty($data['password'])) {
$passwordChanged = true;
} else {
unset($data['password']); // パスワードフィールドをデータ配列から削除
}
// 画像を更新する前に旧画像のファイル名を保持
$oldImage = $user->image;
if ($request->hasFile('image')) {
$file = $request->file('image');
$filename = date('Ymd_His') . '_user_id_' . $user->id . '.' . $file->getClientOriginalExtension(); // タイムスタンプをファイル名に使用
$file->storeAs('public/images', $filename); // imagesディレクトリにファイルを保存
$data['image'] = $filename; // DBにはファイル名のみを保存
}
DB::beginTransaction();
try {
$user->update($data);
// 新しい画像がアップロードされていれば、古い画像を削除
if ($request->hasFile('image') && $oldImage) {
Storage::disk('public')->delete('images/' . $oldImage);
}
DB::commit();
return redirect()->route('users.edit', $user->id)->with(compact('user', 'passwordChanged'));
} catch (\Exception $e) {
DB::rollback();
return back()->with(['message' => '更新中にエラーが発生しました。' . $e->getMessage()]);
}
}
public function destroy(User $user)
{
Gate::authorize('admin');
DB::beginTransaction();
try {
$oldImage = $user->image;
$user->delete(); // ユーザー情報をデータベースから削除
// 画像が存在する場合、削除する
if ($oldImage && Storage::disk('local')->exists('public/images/' . $oldImage)) {
Storage::disk('local')->delete('public/images/' . $oldImage);
}
DB::commit();
return redirect('/users')->with('message', '削除が完了しました!');
} catch (\Exception $e) {
DB::rollBack();
return back()->with(['message' => '削除に失敗しました。' . $e->getMessage()]);
}
}
}
paginate
Gate::authorize
指定したゲートが許可されない場合に\Illuminate\Auth\Access\AuthorizationException
を自動的に投げます。この例外は通常、Laravelがハンドルして適切なHTTP応答(デフォルトで403 Forbidden)を返します。
getClientOriginalExtensionメソッド
アップロードされたファイルの元の拡張子を取得します。
date('Ymd_His')
現在の日付と時刻を年月日時分秒で表します。
今回はこの日付にユーザーのIDと拡張子を組み合わせることで、ファイル名を20240101_120000_user_id_1.jpg
のようにして他のアップロードと重複しないようにします。
storeAs
ファイルを指定されたディレクトリに保存します。
第1引数には保存先のディレクトリを指定し、第2引数には保存後のファイル名を指定します。
403エラーページの作成
現在のままでは、認可がない操作を行った場合に403エラーが表示されてしまいます。
下記を参考にエラーページを作成してください。
シンボリックリンクの作成
シンボリックリンクとは
シンボリックリンクは、ファイルやディレクトリのショートカットとして機能し、本来のアクセスとは異なる代理のフォルダからファイルやフォルダを参照することができます。
Laravelアプリケーションでは、storage/app/public
ディレクトリ(ユーザーがアップロードした画像などを保存するディレクトリ)に保存されたファイルをWebサーバーから直接アクセスを可能にするためには、公開ディレクトリであるpublic
にシンボリックリンクを作成する必要があります。
シンボリックリンクを作成してください。
sail artisan storage:link
シンボリックリンクが作成されたか確認してください。
ls -la public
下記の表示が含まれていれば問題ないです。
storage -> /var/www/html/storage/app/public
またはpublic
で下記のように表示されていると思います。これは削除しないようにしてください。
今回はDockerコンテナを使用しているので、下記のようになっている場合はpublic/storage
を削除してシンボリックリンクを再作成してください。
storage -> /Users/<ユーザー名>/Desktop/<プロジェクト名>/storage/app/public
public/storage
を削除するコマンドです。
rm public/storage
削除せずにシンボリックリンクを再作成しようとすると、次のように表示されます。
ビューの修正
一覧
@extends('layouts.app')
@section('title', 'ユーザー一覧')
@section('content')
<h1 class="text-2xl font-bold mb-4">ユーザー一覧</h1>
<!-- ユーザー登録ページへのリンク -->
<a href="{{ route('users.create') }}"
class="mb-4 inline-block bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
ユーザー登録
</a>
<!-- セッションメッセージ -->
@if (session('message'))
<div><strong>{{ session('message') }}</strong></div>
@endif
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th class="px-6 py-3 bg-gray-50 text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 bg-gray-50 text-xs font-medium text-gray-500 uppercase tracking-wider">名前</th>
<th class="px-6 py-3 bg-gray-50 text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@if (isset($users))
@foreach ($users as $user)
<tr>
<td class="px-6 py-4 whitespace-nowrap">{{ $user->id }}</td>
<td class="px-6 py-4 whitespace-nowrap"><a href="{{ route('users.show', $user->id) }}"
class="text-blue-500 hover:underline">{{ $user->name }}</a></td>
<td class="px-6 py-4 whitespace-nowrap">
<a href="{{ route('users.edit', $user->id) }}"
class="text-indigo-600 hover:text-indigo-900">編集</a>
@can('admin', $authUser)
<form action="{{ route('users.destroy', $user->id) }}" method="POST" class="inline">
@csrf
@method('DELETE')
<button type="submit" class="text-red-600 hover:text-red-900 ml-20"
onclick="return confirm('本当に削除しますか?')">削除</button>
</form>
@endcan
</td>
</tr>
@endforeach
@endif
</tbody>
</table>
<!-- ページネーションリンク -->
<div class="mt-4">
{{ $users->links() }}
</div>
</div>
@endsection
@can
ユーザーに与えられた権限が特定のアクションを実行する権限があるかどうかをチェックするために使われます。
@can('admin', $authUser)
のadmin
はapp/Providers/AppServiceProvider.php
で定義された認可です。
@cannot
@can
の逆の動作をするディレクティブとして@cannot
もあります。
@cannot
は指定された権限がユーザーにない場合にコンテンツを表示するために使用します。
登録
@extends('layouts.app')
@section('title', '新規作成')
@section('content')
<form method="POST" action="{{ route('users.store') }}" enctype="multipart/form-data">
@csrf
<div>
<h1 class='text-center font-bold '>新規作成</h1>
<!-- 名前フィールド -->
<div class="mt-4">
<label for="name">名前</label>
<input id="name" type="text"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="name" value="{{ old('name') }}" required placeholder="">
@error('name')
<div class="text-red-500">{{ $message }}</div>
@enderror
</div>
<!-- メールアドレスフィールド -->
<div class="mt-4">
<label for="email">メールアドレス</label>
<input id="email" type="email"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="email" value="{{ old('email') }}" required placeholder="">
@error('email')
<div class="text-red-500">{{ $message }}</div>
@enderror
</div>
<!-- パスワードフィールド -->
<div class="mt-4">
<label for="password">パスワード</label>
<input id="password" type="password"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="password" required placeholder="">
@error('password')
<div class="text-red-500">{{ $message }}</div>
@enderror
</div>
<!-- パスワード確認フィールド -->
<div class="mt-4">
<label for="password_confirmation">パスワード確認</label>
<input id="password_confirmation" type="password"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="password_confirmation" required placeholder="">
</div>
<!-- 画像アップロードフィールド -->
<div class="mt-4">
<label for="image">ユーザーアイコン(jpg / png の形式のみで1MB以内)</label>
<input id="image" type="file" class="block" name="image" accept="image/png,image/jpeg">
@error('image')
<div class="text-red-500">{{ $message }}</div>
@enderror
</div>
<!-- 登録ボタン -->
<div class="flex items-center justify-center my-4">
<button type="submit">
登録を完了する
</button>
</div>
<!-- セッションメッセージ -->
@if (session('message'))
<div><strong>{{ session('message') }}</strong></div>
@endif
<!-- ユーザー情報 -->
@isset($user)
<strong>ユーザー登録が完了しました。</strong>
<h2>登録したユーザーの情報</h2>
<p>名前: {{ $user->name }}</p>
<p>メールアドレス: {{ $user->email }}</p>
<p>パスワード: ********</p>
@if ($user->image)
<img src="{{ asset('storage/images/' . $user->image) }}" alt="現在のアイコン" class="mx-auto h-[300px]">
@endif
@endisset
</div>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="mt-4">
<a href="{{ route('users.index') }}" class="text-blue-500 hover:underline">ユーザー一覧に戻る</a>
</div>
</form>
@endsection
詳細表示
@extends('layouts.app')
@section('title', 'ユーザー情報')
@section('content')
<div class="container mx-auto">
@isset($user)
<h1 class="text-2xl font-semibold mb-4">{{ $user->name }} さんの情報</h1>
<table class="min-w-full border-collapse border border-gray-200">
<tr>
<td class="border border-gray-200 px-4 py-2 font-semibold">ユーザーアイコン</td>
<td class="border border-gray-200 px-4 py-2">
@if ($user->image)
@if ($user->image)
<img src="{{ asset('storage/images/' . $user->image) }}" alt="現在のアイコン" class="mx-auto h-[300px]">
@endif
@else
ユーザーアイコンは設定されていません。
@endif
</td>
</tr>
<tr>
<td class="border border-gray-200 px-4 py-2 font-semibold">ID</td>
<td class="border border-gray-200 px-4 py-2">{{ $user->id }}</td>
</tr>
<tr>
<td class="border border-gray-200 px-4 py-2 font-semibold">名前</td>
<td class="border border-gray-200 px-4 py-2">{{ $user->name }}</td>
</tr>
<tr>
<td class="border border-gray-200 px-4 py-2 font-semibold">メールアドレス</td>
<td class="border border-gray-200 px-4 py-2">{{ $user->email }}</td>
</tr>
<tr>
<td class="border border-gray-200 px-4 py-2 font-semibold">管理者権限</td>
<td class="border border-gray-200 px-4 py-2">{{ $user->is_admin ? 'はい' : 'いいえ' }}</td>
</tr>
</table>
@endisset
<div class="mt-4">
<a href="{{ route('users.index') }}" class="text-blue-500 hover:underline">ユーザー一覧に戻る</a>
</div>
</div>
@endsection
更新
@extends('layouts.app')
@section('title', 'ユーザー情報編集')
@section('content')
<form method="POST" action="{{ route('users.update', $user->id) }}" enctype="multipart/form-data">
@csrf
@method('PUT')
<div>
<h1 class='text-center font-bold '>ユーザー情報編集(変更する箇所のみ入力してください)</h1>
<!-- 名前フィールド -->
<div class="mt-4">
<label for="name">名前</label>
<input id="name" type="text"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="name" value="{{ old('name', $user->name) }}" required placeholder="">
@error('name')
<div class="text-red-500">{{ $message }}</div>
@enderror
</div>
<!-- メールアドレスフィールド -->
<div class="mt-4">
<label for="email">メールアドレス</label>
<input id="email" type="email"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="email" value="{{ old('email', $user->email) }}" required placeholder="">
@error('email')
<div class="text-red-500">{{ $message }}</div>
@enderror
</div>
<!-- パスワードフィールド -->
<div class="mt-4">
<label for="password">新しいパスワード</label>
<input id="password" type="password"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="password" placeholder="">
@error('password')
<div class="text-red-500">{{ $message }}</div>
@enderror
</div>
<!-- パスワード確認フィールド -->
<div class="mt-4">
<label for="password_confirmation">新しいパスワード確認</label>
<input id="password_confirmation" type="password"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
name="password_confirmation" placeholder="">
</div>
<!-- 画像アップロードフィールド -->
<div class="mt-4">
<label for="image">ユーザーアイコン(jpg / png の形式のみで1MB以内)</label>
<input id="image" type="file" class="block" name="image" accept="image/png,image/jpeg">
@error('image')
<div class="text-red-500">{{ $message }}</div>
@enderror
<!-- 現在のアイコン表示 -->
@if ($user->image)
<p>現在のアイコン</p>
<img src="{{ asset('storage/images/' . $user->image) }}" alt="現在のアイコン" class="mx-auto h-[300px]">
@endif
</div>
<!-- 管理者権限フィールド -->
<div class="mt-4">
<label for="is_admin">管理者権限</label>
@if ($user->is_admin == '1')
<p>現在は権限あり</p>
@else
<p>現在は権限なし</p>
@endif
<div>
<input type="radio" id="admin_yes" name="is_admin" value="1"
{{ $user->is_admin == '1' ? 'checked' : '' }}>
<label for="admin_yes">あり</label>
<input type="radio" id="admin_no" name="is_admin" value="0"
{{ $user->is_admin != '1' ? 'checked' : '' }}>
<label for="admin_no">なし</label>
</div>
</div>
<!-- セッションメッセージ -->
@if (session('message'))
<div><strong>{{ session('message') }}</strong></div>
@endif
<!-- ユーザー情報の表示 -->
@if (session('user'))
<div>
<strong>ユーザー情報が更新されました。</strong>
<h2>更新したユーザーの情報</h2>
<p>名前: {{ session('user')->name }}</p>
<p>メールアドレス: {{ session('user')->email }}</p>
@if (session('passwordChanged'))
<p>パスワード: ********</p>
@endif
@if (session('user')->is_admin === '1')
<p>管理者権限: あり</p>
@else
<p>管理者権限: なし</p>
@endif
@if (session('user')->image)
@if ($user->image)
<img src="{{ asset('storage/images/' . $user->image) }}" alt="現在のアイコン"
class="mx-auto h-[300px]">
@endif
@endif
</div>
@endif
<!-- 更新ボタン -->
<div class="flex items-center justify-center my-4">
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
更新を完了する
</button>
</div>
<div class="mt-4">
<a href="{{ route('users.index') }}" class="text-blue-500 hover:underline">ユーザー一覧に戻る</a>
</div>
</div>
</form>
@endsection
SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for mysql failed: nodename nor servname provided, or not known
コンテナ内でマイギュレーションを実行しているか確認してください。
php artisan migrate
Serialization of 'Illuminate\Http\UploadedFile' is not allowed
セッションにファイルインスタンスを保存しようとしたときに発生します。Laravelのセッションは、データをシリアライズして保存するため、UploadedFile
のようなオブジェクトは直接保存できません。
SQLSTATE[HY000]: General error: 1364 Field '<カラム名>' doesn't have a default value
データベースに挿入される際にリクエストクラスでnullable
と定義していても、デフォルト値が設定されていないと、このエラーが発生します。
値がnull
の場合でもデータベースに明示的にnull
を代入してください。
終わりに
何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉
Discussion