diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php new file mode 100644 index 0000000..4f48f06 --- /dev/null +++ b/app/Http/Controllers/ProfileController.php @@ -0,0 +1,42 @@ +with('user', $user); + } + + /** + * Show the form for editing a user's profile data. + */ + public function edit(User $user): View + { + return view('profiles.edit')->with('user', $user); + } + + /** + * Update the user profile data. + */ + public function update(UpdateUserRequest $request, User $user): RedirectResponse + { + $this->updateUser($request, $user); + $user->refresh(); + session()->flash('message', "Profile updated!"); + return redirect()->route('profiles.show', $user); + } + +} diff --git a/app/Http/Controllers/Traits/UpdatesUser.php b/app/Http/Controllers/Traits/UpdatesUser.php new file mode 100644 index 0000000..e257893 --- /dev/null +++ b/app/Http/Controllers/Traits/UpdatesUser.php @@ -0,0 +1,45 @@ +validated(); + $input['remember_token'] = Str::random(10); + if (!empty($input['password'])) { + $input['password'] = Hash::make($input['password']); + } + else { + unset($input['password']); + } + $input['admin'] = $input['admin'] ?? false; + + $user->fill($input)->save(); + + // Handle image. + if (!empty($input['image'])) { + /** @var \Illuminate\Http\UploadedFile $file */ + $file = $input['image']; + $user->clearMediaCollection(); + $user + ->addMediaFromRequest('image') + ->usingName($user->username) + ->usingFileName("{$user->slug}.{$file->extension()}") + ->toMediaCollection(); + } + elseif (isset($input['remove_image']) && $input['remove_image']) { + $user->clearMediaCollection(); + } + } + +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 0bc7c0e..adaa87f 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -2,15 +2,16 @@ namespace App\Http\Controllers; +use App\Http\Controllers\Traits\UpdatesUser; use App\Http\Requests\UpdateUserRequest; use App\Models\User; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; -use Illuminate\Support\Facades\Hash; -use Illuminate\Support\Str; class UserController extends Controller { + use UpdatesUser; + /** * Display a listing of the resource. */ @@ -50,33 +51,7 @@ class UserController extends Controller */ public function update(UpdateUserRequest $request, User $user): RedirectResponse { - $input = $request->validated(); - $input['remember_token'] = Str::random(10); - if (!empty($input['password'])) { - $input['password'] = Hash::make($input['password']); - } - else { - unset($input['password']); - } - $input['admin'] = $input['admin'] ?? false; - - $user->fill($input)->save(); - - // Handle image. - if (!empty($input['image'])) { - /** @var \Illuminate\Http\UploadedFile $file */ - $file = $input['image']; - $user->clearMediaCollection(); - $user - ->addMediaFromRequest('image') - ->usingName($user->username) - ->usingFileName("{$user->slug}.{$file->extension()}") - ->toMediaCollection(); - } - elseif (isset($input['remove_image']) && $input['remove_image']) { - $user->clearMediaCollection(); - } - + $this->updateUser($request, $user); session()->flash('message', "User {$user->name} updated!"); return redirect()->route('users.index'); } diff --git a/app/Models/User.php b/app/Models/User.php index 0633864..e84dcc6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -83,6 +83,14 @@ final class User extends Authenticatable implements HasMedia 'admin' => 'bool', ]; + /** + * @inheritdoc + */ + public function sluggable(): array + { + return ['slug' => ['source' => 'username']]; + } + /** * Get the User's goals. */ diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php index a77e091..f0e0d88 100644 --- a/app/Policies/UserPolicy.php +++ b/app/Policies/UserPolicy.php @@ -16,6 +16,13 @@ class UserPolicy return $user->admin; } + /** + * Determine whether the user can edit a user via the "profile". + */ + public function editProfile(User $user, User $model): bool { + return $user->id === $model->id; + } + /** * Determine whether the user can delete the model. */ diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index d68f311..75e4c49 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -8,10 +8,9 @@ use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvid class AuthServiceProvider extends ServiceProvider { + /** - * The policy mappings for the application. - * - * @var array + * @inheritdoc */ protected $policies = [ User::class => UserPolicy::class, diff --git a/resources/views/profiles/edit.blade.php b/resources/views/profiles/edit.blade.php new file mode 100644 index 0000000..b90240b --- /dev/null +++ b/resources/views/profiles/edit.blade.php @@ -0,0 +1,70 @@ + + Edit Profile + +

Edit Profile

+
+
+ @method('put') + @csrf +
+
+ +
+ + + +
+ + +
+ + + +
+ +
+ +
+ +
+ + + +
+ + +
+ + + +
+
+ + +
+ +
+
+ +
+ Save +
+
+
diff --git a/resources/views/profiles/show.blade.php b/resources/views/profiles/show.blade.php new file mode 100644 index 0000000..35c2a7e --- /dev/null +++ b/resources/views/profiles/show.blade.php @@ -0,0 +1,9 @@ + + Profile + +
+

Profile

+
+
+ {{ $user->name }} +
diff --git a/routes/auth.php b/routes/auth.php index 3813b29..7628fdc 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -4,6 +4,7 @@ use App\Http\Controllers\FoodController; use App\Http\Controllers\GoalController; use App\Http\Controllers\IngredientPickerController; use App\Http\Controllers\JournalEntryController; +use App\Http\Controllers\ProfileController; use App\Http\Controllers\RecipeController; use App\Http\Controllers\Auth\AuthenticatedSessionController; use Illuminate\Support\Facades\Route; @@ -15,7 +16,9 @@ use Illuminate\Support\Facades\Route; */ Route::middleware(['auth'])->group(function () { + // Auth. Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])->name('logout'); + // Foods. Route::resource('foods', FoodController::class); Route::get('/foods/{food}/delete', [FoodController::class, 'delete'])->name('foods.delete'); @@ -36,4 +39,13 @@ Route::middleware(['auth'])->group(function () { // Recipes. Route::resource('recipes', RecipeController::class); Route::get('/recipes/{recipe}/delete', [RecipeController::class, 'delete'])->name('recipes.delete'); + + // Users. + Route::get('/profile/{user}', [ProfileController::class, 'show'])->name('profiles.show'); +}); + +Route::middleware(['auth', 'can:editProfile,user'])->group(function () { + // Profiles (non-admin Users variant). + Route::get('/profile/{user}/edit', [ProfileController::class, 'edit'])->name('profiles.edit'); + Route::put('/profile/{user}', [ProfileController::class, 'update'])->name('profiles.update'); });