Users CRUD

This commit is contained in:
PovilasKorop 2024-09-09 08:59:02 +03:00
parent 8edb194a28
commit e05ac5031b
14 changed files with 349 additions and 18 deletions

View File

@ -30,13 +30,15 @@ public function create(): View
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'first_name' => $request->first_name,
'last_name' => $request->last_name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);

View File

@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
$users = User::paginate(10);
return view('users.index', compact('users'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
return view('users.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreUserRequest $request)
{
User::create($request->validated());
return redirect()->route('users.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(User $user)
{
return view('users.edit', compact('user'));
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateUserRequest $request, User $user)
{
$user->update($request->validated());
return redirect()->route('users.index');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(User $user)
{
$user->delete();
return redirect()->route('users.index');
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreUserRequest 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.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'first_name' => ['required'],
'last_name' => ['required'],
'email' => ['required', 'email', Rule::unique('users')],
'password' => ['required'],
];
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateUserRequest 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.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'first_name' => ['required'],
'last_name' => ['required'],
'email' => ['required', 'email', Rule::unique('users')->ignore($this->user)],
];
}
}

View File

@ -4,12 +4,13 @@
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
use HasFactory, Notifiable, SoftDeletes;
/**
* The attributes that are mass assignable.
@ -17,7 +18,8 @@ class User extends Authenticatable
* @var array<int, string>
*/
protected $fillable = [
'name',
'first_name',
'last_name',
'email',
'password',
];

View File

@ -24,7 +24,8 @@ class UserFactory extends Factory
public function definition(): array
{
return [
'name' => fake()->name(),
'first_name' => fake()->firstName(),
'last_name' => fake()->lastName(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),

View File

@ -13,12 +13,14 @@ public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('first_name');
$table->string('last_name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {

View File

@ -13,11 +13,6 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
User::factory(10)->create();
}
}

View File

@ -2,11 +2,18 @@
<form method="POST" action="{{ route('register') }}">
@csrf
<!-- Name -->
<!-- First Name -->
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
<x-input-error :messages="$errors->get('name')" class="mt-2" />
<x-input-label for="first_name" :value="__('First Name')" />
<x-text-input id="first_name" class="block mt-1 w-full" type="text" name="first_name" :value="old('first_name')" required autofocus autocomplete="first_name" />
<x-input-error :messages="$errors->get('first_name')" class="mt-2" />
</div>
<!-- Last Name -->
<div class="mt-4">
<x-input-label for="last_name" :value="__('Last Name')" />
<x-text-input id="last_name" class="block mt-1 w-full" type="text" name="last_name" :value="old('last_name')" required autocomplete="last_name" />
<x-input-error :messages="$errors->get('last_name')" class="mt-2" />
</div>
<!-- Email Address -->

View File

@ -15,6 +15,9 @@
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-nav-link>
<x-nav-link :href="route('users.index')" :active="request()->routeIs('users.*')">
{{ __('Users') }}
</x-nav-link>
</div>
</div>
@ -23,7 +26,7 @@
<x-dropdown align="right" width="48">
<x-slot name="trigger">
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150">
<div>{{ Auth::user()->name }}</div>
<div>{{ Auth::user()->first_name }}</div>
<div class="ms-1">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
@ -70,12 +73,15 @@
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('users.index')" :active="request()->routeIs('users.*')">
{{ __('Users') }}
</x-responsive-nav-link>
</div>
<!-- Responsive Settings Options -->
<div class="pt-4 pb-1 border-t border-gray-200">
<div class="px-4">
<div class="font-medium text-base text-gray-800">{{ Auth::user()->name }}</div>
<div class="font-medium text-base text-gray-800">{{ Auth::user()->first_name }}</div>
<div class="font-medium text-sm text-gray-500">{{ Auth::user()->email }}</div>
</div>

View File

@ -0,0 +1,69 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Create User') }}
</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="overflow-hidden overflow-x-auto p-6 bg-white border-b border-gray-200">
<form method="POST" action="{{ route('users.store') }}">
@csrf
<!-- First Name -->
<div>
<x-input-label for="first_name" :value="__('First Name')" />
<x-text-input id="first_name" class="block mt-1 w-full" type="text" name="first_name" :value="old('first_name')" required />
<x-input-error :messages="$errors->get('first_name')" class="mt-2" />
</div>
<!-- Last Name -->
<div class="mt-4">
<x-input-label for="last_name" :value="__('Last Name')" />
<x-text-input id="last_name" class="block mt-1 w-full" type="text" name="last_name" :value="old('last_name')" required />
<x-input-error :messages="$errors->get('last_name')" class="mt-2" />
</div>
<!-- Email Address -->
<div class="mt-4">
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div>
<x-primary-button class="mt-4">
{{ __('Save') }}
</x-primary-button>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,47 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Edit User') }}
</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="overflow-hidden overflow-x-auto p-6 bg-white border-b border-gray-200">
<form method="POST" action="{{ route('users.update', $user) }}">
@method('PUT')
@csrf
<!-- First Name -->
<div>
<x-input-label for="first_name" :value="__('First Name')" />
<x-text-input id="first_name" class="block mt-1 w-full" type="text" name="first_name" :value="old('first_name', $user->first_name)" required />
<x-input-error :messages="$errors->get('first_name')" class="mt-2" />
</div>
<!-- Last Name -->
<div class="mt-4">
<x-input-label for="last_name" :value="__('Last Name')" />
<x-text-input id="last_name" class="block mt-1 w-full" type="text" name="last_name" :value="old('last_name', $user->last_name)" required />
<x-input-error :messages="$errors->get('last_name')" class="mt-2" />
</div>
<!-- Email Address -->
<div class="mt-4">
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $user->email)" required />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<x-primary-button class="mt-4">
{{ __('Save') }}
</x-primary-button>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,67 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Users') }}
</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">
<a href="{{ route('users.create') }}" class="underline">Add new user</a>
<table class="min-w-full divide-y divide-gray-200 border mt-4">
<thead>
<tr>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">First Name</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Last Name</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Email</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200 divide-solid">
@foreach($users as $user)
<tr class="bg-white">
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $user->first_name }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $user->last_name }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $user->email }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
<a href="{{ route('users.edit', $user) }}" class="underline">Edit</a>
|
<form method="POST"
class="inline-block"
action="{{ route('users.destroy', $user) }}"
onsubmit="return confirm('Are you sure?')">
@method('DELETE')
@csrf
<button type="submit" class="text-red-500 underline">Delete</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
<div class="mt-4">
{{ $users->links() }}
</div>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -1,6 +1,7 @@
<?php
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
@ -12,6 +13,8 @@
})->middleware(['auth', 'verified'])->name('dashboard');
Route::middleware('auth')->group(function () {
Route::resource('users', UserController::class);
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');