In this post, I covered from minute 2:31:48 until 2:54:50 of this video.
Creating 'Edit Profile' page
First, let's add an 'edit profile' link in our profile. For now, let's just
ignore about fancy styling. We can do that later. The link is would be
/profile/{{ $user->id }}/edit
(refer back to
resource controllers).
10 11 2 13 14 |
<div class="d-flex justify-content-between align-items-baseline">
<h2 class="font-weight-light">{{ $user->username }}</h2>
<a href="/p/create">Add New Post</a>
</div>
<a href="/profile/{{ $user->id }}/edit">Edit Profile</a>
|
resources > views > profiles > index.blade.php
Create the route for the page.
26 27 |
Route::get('/profile/{user}', [App\Http\Controllers\ProfilesController::class, 'index'])->name('profile.show');
Route::get('/profile/{user}/edit', [App\Http\Controllers\ProfilesController::class, 'edit'])->name('profile.edit');
|
routes > web.php
Create the edit method in Profiles controller. We also can simplify the
existing code in index method by using
route model binding
we've learned in previous part.
10 11 12 13 14 15 16 17 18 19 20 21 |
class ProfilesController extends Controller { public function index(User $user) { return view('profiles/index', compact('user')); } public function edit(User $user) { return view('profiles/edit', compact('user')); } } |
app > Http > Controllers > ProfilesController.php
Notice that we use
User
instead of
\App\Models\User
? That's because we've already imported the
class before with use App\Models\User;
under the namespace.
Now, let's create a new view for edit profile in profile folder. Just copy
the code from create.blade.php and do all the necessary changes to
the name.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
@extends('layouts.app') @section('content') <div class="container"> <form action="/profile/{{ $user->id }}" enctype="multipart/form-data" method="post"> @csrf @method('PATCH') <div class="row"> <div class="col-8 offset-2"> <!-- Title --> <div class="row"> <h1>Edit Profile</h1> </div> <!-- Title --> <div class="form-group row"> <label for="title" class="col-md-4 col-form-label">Title</label> <input id="title" type="text" class="form-control @error('title') is-invalid @enderror" name="title" value="{{ old('title') ?? $user->profile->title }}" autocomplete="title" autofocus> @error('title') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> <!-- Description --> <div class="form-group row"> <label for="description" class="col-md-4 col-form-label">Description</label> <input id="description" type="text" class="form-control @error('description') is-invalid @enderror" name="description" value="{{ old('description') ?? $user->profile->description }}" autocomplete="description" autofocus> @error('description') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> <!-- URL --> <div class="form-group row"> <label for="url" class="col-md-4 col-form-label">URL</label> <input id="url" type="text" class="form-control @error('url') is-invalid @enderror" name="url" value="{{ old('url') ?? $user->profile->url }}" autocomplete="url" autofocus> @error('url') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> <!-- Image --> <div class="row"> <label for="image" class="col-md-4 col-form-label">Profile Image</label> <input type="file" class="form-control-file" id="image" name="image"> @error('image') <strong>{{ $message }}</strong> @enderror </div> <!-- Add Button --> <div class="row pt-4"> <button class="btn btn-primary">Save</button> </div> </div> </div> </form> </div> @endsection |
resources > views > profiles > edit.blade.php
Please take a look on the codes highlighted in blue.
-
/profile/{{ $user->id }}
- refer back to resource controller (again) for the action of update -
@method('PATCH')
- blade directive for updating a portion or single attribute -
{{ old('title') ?? $user->profile->title }}
- this is to prefilled the fields with the current value so it will not just empty field. old('title') is for when you fail validation, and when you come back, those fields will be populated with the data that you entered. In our case, we add some extra code to pull the value from the table
Make updating work
We have completed in creating the edit profile page. But now, we can't
use the form to update our profile just yet. Although we already use the
PATCH method in the form, we still need to create the route and method
for update.
26 27 28 |
Route::get('/profile/{user}', [App\Http\Controllers\ProfilesController::class, 'index'])->name('profile.show');
Route::get('/profile/{user}/edit', [App\Http\Controllers\ProfilesController::class, 'edit'])->name('profile.edit');
Route::patch('/profile/{user}', [App\Http\Controllers\ProfilesController::class, 'update'])->name('profile.update');
|
routes > web.php
And for the method:
19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public function update(User $user) { $data = request()->validate([ 'title' => '', 'description' => '', 'url' => 'url', 'image' => '' ]); auth()->user()->profile->update($data); return redirect("/profile/{$user->id}"); } |
app > Http > Controllers >
ProfilesControler.php
There's a validation rule for url, so if you enter the field other than
a url, it will return an error. However, you need to write the full url
with the protocol. We'll fix that later.
At this point, if you try using the form and click Save, you will get
this error:
Illuminate\Database\Eloqu ent\MassAssignmentException
Add [title] to fillable property to allow mass assignment on
[App\Models\Profile].
To fix that, one last thing that you need to do is disabling mass
assignment. This is because we are very particular about how we bring in
each of those fields, as stated in line 23 - 26 of Profile controller.
That protection that we get out of the box, we are disabling it
as we're not just passing in the entire request into the update.
Just add this one line of code in User model.
8 9 10 11 17 |
class Profile extends Model
{
protected $guarded = [];
...
}
|
app > Models > User.php
The form is now working. Try to update anything! But for now, let's
leave the profile image empty first because we are gonna do that in
the next part.
Protecting the 'Edit Profile' page / Restrict access for unauthorized user
Although the form is working, we have a problem. If we logged out and
view some user's profile, we can also see the 'Edit Profile' link in the
profile. For that, we are going to use 'policies'. Policies are a
simple way for us to restrict what a user can or cannot do with a
particular resource. Policies are associated to a specific model. In our
case, we're going to do a policy for the profile.
Let's run
php artisan help make:policy
first to see what is the
argument and option when creating a policy.
Run
php artisan make:policy ProfilePolicy -m Profile
, meaning that
we are creating policy named ProfilePolicy for the model Profile.
So now, if we look in app folder, there's a new folder named
Policies and inside of it there is ProfilePolicy.php. Each of the methods in the profile policy represents an action
that can be taken on by Profile that based around the User. Each of
these methods will going to return a boolean, either true or false. For
example, if you return true in the view method, meaning that the User
will be able to view the current Profile. Right now, we want to use the
update method.
54 55 56 57 |
public function update(User $user, Profile $profile)
{
return $user->id == $profile->user_id;
}
|
app > Policies > ProfilePolicy.php
For the code above, we say that a user's id for the profile
(
$profile->user_id
) must match the user's id ($user->id
). It makes
sense if you think about it. Let's say our profile has the user id of 1
,
viewing other user's profile with the id of 2
. The other user's profile
id doesn't match with our user id wasn't it? So, we supposedly shouldn't
be able to view the /profile/2/edit
. One more thing to do to make that
work is we need to add an authorization in the edit and update method of
the profile controller.
15 16 17 18 19 20 |
public function edit(User $user)
{
$this->authorize('update', $user->profile);
return view('profiles/edit', compact('user'));
}
|
21 22 23 24 35 |
public function update(User $user)
{
$this->authorize('update', $user->profile);
...
}
|
app > Http > Controllers > ProfilesController.php
The edit page is now protected. A user can no longer view the edit page
of other users with a direct link.
Hide some elements and only show it to authorized user
Although all pages have been protected, of course we would want to hide
the 'Add New Post' and 'Edit Profile' link on the profile. Right now,
even if we not logged in, we could see the link in the profile. To solve
this, it's actually very simple. We're going to wrap the link in a
@can
blade directive in the profile view.
10 11 12 13 14 15 16 17 18 |
<div class="d-flex justify-content-between align-items-baseline"> <h2 class="font-weight-light">{{ $user->username }}</h2> @can('update', $user->profile) <a href="/p/create">Add New Post</a> @endcan </div> @can('update', $user->profile) <a href="/profile/{{ $user->id }}/edit">Edit Profile</a> @endcan |
resources > views > profiles > index.blade.php
The final result:
We haven't got to editing the profile image yet. So in the next part,
we're gonna do that.
No comments:
Post a Comment