So next we're gonna work on our user profiles. We're gonna give them a page that lists out all of the movies that they've watched and it also will display their name
and biography information. So first we need to give them the ability to change their name and biography. So we'll give them a little button right up here that says something like edit profile. So let's go ahead and hide our browser back away,
jump into our partials, nav, and we'll put this behind our watch list. We don't have a route for this made quite yet, but we can go ahead and use the knowledge that we have gained so far
to know that we're gonna call this route profiles.edit. And a single authenticated user is only ever gonna be able to edit their profile.
So we don't need to specify an ID in here. Instead, we can just use the authenticated user to pull which user is actually editing their profile.
And then we'll get our class text extra small, add it on there. And for the text, we'll do edit profile. We'll give that a save and that will temporarily result in an error in our browser
because this route currently does not exist. So let's go ahead and create that by jumping into our routes file. And we'll put this at the end of our application-based routes. So right about here,
just above where we reach our authentication routes. We'll do router. And this will be a get because we need a page to actually show the form. A user only has one profile,
so we'll call the actual URL profile here, /edit, as they're specifically going to be editing their profile. For this, we'll use the profiles controller that we created a while ago.
Hit tab to auto-import that. And I don't think we stubbed any methods inside of it. No, we didn't. But we wanna use a method called edit for this. And we'll give this a name of profiles.edit,
matching what we've added in our navigation. Then we only want authenticated users to be able to use this. So we'll do use middleware.andRequireAuthentication. Give that a save.
And let's go add our edit method to our profiles controller. So profiles controller. Looks like it's empty at this moment. So let's go ahead and uncomment our HTTP context import.
Async, edit. We're gonna want our view and auth out of our HTTP context. And let's go ahead and grab our profile as a separate entity.
So we'll do profile there equals auth user, exclamation point to assert that we verify there is a user. Related profile.query.firstOrFail.
And we also wanna await that call. Now, during registration, we should be creating a profile for our users. However, at present, we are not doing that.
And this query assumes that we are doing that. So in order to actually be able to make this assertion, we're gonna need to verify that our user is actually always going to have a profile in our application.
So first, within our auth register controller, we're gonna wanna create a profile for the user in addition to creating the user. So add an additional step here
as step three, create profile for user, which makes our step three, step four, and our step four, step five. In a created profile,
all that we need to do is await user.relatedProfile.create. And really the only thing that we have on our profile that actually takes information is the description, and that's nullable.
So we can just pass this an empty object and it will be happy with that. So that fixes our issue forward-looking. But what about for users that already exist inside of our application? Well, why don't we jump into our terminal,
stop our application, clear that out, node.ace.makeMigration, and we'll call this addMissingUserProfiles. We could, of course, just patch this in the database
ourselves relatively easily. However, we wanna make sure that this change goes out at the exact same time that our registration fix goes out so that we don't have any people that sneak in between.
And we should always be running our pending migrations as we're deploying out our application changes. So by placing this change inside of a migration,
we kind of just give ourselves a way to make this change at the same time that our registration fix goes out. So let's run that. Probably should have added alter to that, but that's okay. We can change that name just slightly.
Scroll down, database, migrations. I'm gonna go ahead and right-click on this, rename it, and just remove the word create. Okay, jump into that. Now for this, unlike a traditional migration,
we're actually not gonna wanna perform any of these actions against our database, specifically for any tables. So we can clear out our up and down. And within our up method, instead what we wanna do
is perform an action against our database. So we can do this. And use the defer method to make alterations to our database as needed. And this defer method is here to ensure
that if we were creating a table or something in conjunction to this defer call, that this defer runs after the up method has executed. And it's provided a callback function
that gets our database query client. We'll also wanna make that async. And for this, we're gonna wanna get our users that have a missing profile. So we'll do const users equals await.
And rather than using this database module, let's go ahead and use our models as it will make this call a little bit easier. So we can import our user model.query,
where doesn't have, and then just check for users that doesn't have a profile record. Looks like that wants some query callbacks. So we'll go ahead and give it one to make it happy.
But we don't need to filter that down any further. So we'll just leave that as it is. Give that a save to fix formatting. Okay, so now we just need an array of objects
with a user ID to pass into a create mini call for our profile. So we'll do const profiles equals users.map, take our user.
And we'll return back an object that has user ID as user.id. Since we really only care about that user's ID, we can go ahead and add a select onto the end of our user query,
specifically selecting just that user's ID to make that call a little bit quicker and give that a save for formatting. All right, so now we have our users that don't have an ID and we have an array of profile objects
with that user's ID mapped in, meaning that all that we need to do now is await profile, import that model.createMini
and pass in our profiles array of objects, just like so. We can give ourselves a little comment up here to know exactly what we're doing.
Create profiles for any users missing one. Let's go ahead and try and run it. So we'll hide that back away, open up our terminal,
clear that out, node, ACE, migration run. There we go. So our migration successfully now ran, meaning that if we were to jump into pgAdmin, not sure exactly when we last refreshed this.
So I'm gonna go ahead and scroll up to Adana 6, click that, refresh, just to make sure it has all of our recent changes. Come back down to our profiles, right click that, view edit data, go ahead and select all rows. And there we go.
So you can see the ones that already have descriptions within there are the ones that we've created with our fake data. And then we have these three down here with null information that we likely created with our authentication.
If we scroll down a little bit further, we should be able to find our users. Go ahead and right click on that. So here we have user IDs nine, 10 and seven. So right click on our users, view edit data,
all rows, seven, nine and 10, are indeed our John Doe, John and AuthUser1. So these are indeed our authenticated users. Cool, so we can hide that back away,
clear this out and boot back up our server, jump back into our text editor. We can collapse down our database and jump back into our profiles controller. And now we are safe to make this assertion
because we've now verified that our users will always have a profile record. Meaning we can go ahead and just return view.render and pass in pages, profiles, edit
with stateful information of our profile. And if you wanted to, you could also pass your users information directly in there, but we will have that accessible just via Auth.user.
So it's not necessarily needed. Give that a save and let's go create that page real quick. So right click pages, new file, create a folder called profiles
with a page of edit.edge inside of it. Again, before we end, I do want to emphasize the only reason that we added this migration with this defer call is because we had some users
in our database that did not have a profile because we were missing the profile creation in our registration step for those users. If we had added this from the get go,
we would not have needed this migration to add those missing profiles. So if you do need your users to always have a profile and you have this from the moment that you launch your authentication
and you don't have any users missing profiles, then you do not need this migration whatsoever. But if you're launching something like profiles inside of your application, where it might come after you've launched
your authentication, then this is an approach for how you can make sure that those records exist for users that already exist inside of your database.
How to Create and Fix Missing User Profiles in Your Application
In This Lesson
Learn how to create and manage user profiles in your application. This tutorial covers adding an edit profile button, setting up routes and controllers, ensuring profile creation during registration, handling existing users without profiles, and verifying
00:00 - Overview of User Profiles Explanation of the user profile page, listing watched movies, and displaying name and biography.
00:13 - Adding Edit Profile Button Adding a button for users to edit their name and biography.
00:55 - Defining the Edit Profile Route Defining the GET route for our edit profile page that also required a user to be authenticated.
01:38 - Controller Setup Setting up the profiles controller with the edit method.
02:06 - Ensuring Profile Creation During Registration Creating a profile for users during the registration process.
02:50 - Handling Existing Users Without Profiles Adding a migration to create profiles for existing users. We cover how you can remedy this situation when it also exists in your production database. However, if this is only on your local database, this step is not needed.
05:25 - Applying Our Migration Running the migration to add missing profiles.
05:35 - Verifying Profiles in the Database Verifying the creation of profiles in pgAdmin.
06:30 - Rendering the Edit Profile Page Rendering the edit profile view with the user's profile information.
06:55 - Conclusion and Migration Explanation Explanation of why the migration was needed and when it wouldn't be necessary.