Laravel 11 Update User Profile Tutorial

Building a user profile update feature in a Laravel 11 application—where users can modify their username, email, password, mobile number, profile picture, and more—is a staple for modern web applications. Whether you’re working on a personal project or a professional app, this functionality is essential for providing a personalized user experience.

Here’s an engaging guide to help you implement Laravel Update User Profile feature in your Laravel application:

Step 1 – Install Laravel 11

Begin by setting up a new Laravel 11 project using Composer:

composer create-project --prefer-dist laravel/laravel UserProfileApp

Step 2 – Configure the Database

Edit the .env file to set up your database configuration:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database_name
DB_USERNAME=your_database_username
DB_PASSWORD=your_database_password

Step 3 – Add Laravel UI Package

Install the Laravel UI package for authentication scaffolding:

composer require laravel/ui

Step 4 – Generate Authentication Scaffolding

Generate the authentication scaffolding with Bootstrap styling:

php artisan ui bootstrap --auth
npm install && npm run dev

Step 5 – Create and Run Migration

Create a migration file to add additional fields to the users table:

php artisan make:migration add_fields_to_users

Edit the migration file located at database/migrations/_add_fields_to_users.php and include the following code:

<?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('avatar')->nullable();
$table->string('phone')->nullable();
$table->string('city')->nullable();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['avatar', 'phone', 'city']);
});
}
};

Apply the migration:

php artisan migrate

Step 6 – Update the User Model

Modify the app/Models/User.php file to include the new fields:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
'avatar',
'phone',
'city',
];

/**
* The attributes that should be hidden for serialization.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}

Step 7 – Define Routes

Add routes in routes/web.php to handle profile update requests:

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

Route::get('/profile', [App\Http\Controllers\ProfileController::class, 'index'])->name('user.profile');
Route::post('/profile', [App\Http\Controllers\ProfileController::class, 'store'])->name('user.profile.store');

Step 8 – Create the Controller

Generate a controller for managing profile updates:

php artisan make:controller ProfileController

Edit app/Http/Controllers/ProfileController.php and implement the update logic:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class ProfileController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}

/**
* Show the profile edit form.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('profile');
}

/**
* Handle the profile update request.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|max:255',
'confirm_password' => 'required_with:password|same:password',
'avatar' => 'nullable|image',
]);

$data = $request->all();

if ($request->hasFile('avatar')) {
$avatarName = time() . '.' . $request->avatar->getClientOriginalExtension();
$request->avatar->move(public_path('avatars'), $avatarName);

$data['avatar'] = $avatarName;
} else {
unset($data['avatar']);
}

if ($request->filled('password')) {
$data['password'] = Hash::make($data['password']);
} else {
unset($data['password']);
}

auth()->user()->update($data);

return back()->with('success', 'Profile updated successfully.');
}
}

Step 9 – Create the Blade View

Create the profile.blade.php view in resources/views and add the following code:

@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
<div class="card">
<div class="card-header">{{ __('Profile') }}</div>

<div class="card-body">
<form method="POST" action="{{ route('user.profile.store') }}" enctype="multipart/form-data">
@csrf

@if (session('success'))
<div class="alert alert-success" role="alert">
{{ session('success') }}
</div>
@endif

<div class="row">
<div class="mb-3 col-md-6">
<label for="avatar" class="form-label">Avatar:</label>
<input id="avatar" type="file" class="form-control @error('avatar') is-invalid @enderror" name="avatar">

@error('avatar')
<span class="text-danger">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>

<div class="mb-3 col-md-6">
<img src="/avatars/{{ auth()->user()->avatar }}" style="width:80px;margin-top:10px;">
</div>
</div>

<div class="row">
<div class="mb-3 col-md-6">
<label for="name" class="form-label">Name:</label>
<input id="name" type="text" class="form-control" name="name" value="{{ auth()->user()->name }}">

@error('name')
<span class="text-danger">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>

<div class="mb-3 col-md-6">
<label for="email" class="form-label">Email:</label>
<input id="email" type="email" class="form-control" name="email" value="{{ auth()->user()->email }}">

@error('email')
<span class="text-danger">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row">
<div class="mb-3 col-md-6">
<label for="password" class="form-label">Password:</label>
<input id="password" type="password" class="form-control" name="password">

@error('password')
<span class="text-danger">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>

<div class="mb-3 col-md-6">
<label for="confirm_password" class="form-label">Confirm Password:</label>
<input id="confirm_password" type="password" class="form-control" name="confirm_password">

@error('confirm_password')
<span class="text-danger">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row">
<div class="mb-3 col-md-6">
<label for="phone" class="form-label">Phone:</label>
<input id="phone" type="text" class="form-control" name="phone" value="{{ auth()->user()->phone }}">

@error('phone')
<span class="text-danger">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>

<div class="mb-3 col-md-6">
<label for="city" class="form-label">City:</label>
<input id="city" type="text" class="form-control" name="city" value="{{ auth()->user()->city }}">

@error('city')
<span class="text-danger">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-0">
<div class="col-md-12 text-center">
<button type="submit" class="btn btn-primary">
{{ __('Update Profile') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

Additionally, update the profile link in resources/views/layouts/app.blade.php:

<!-- Right Side Of Navbar -->
<ul class="navbar-nav ms-auto">
<!-- Authentication Links -->
@guest
@if (Route::has('login'))
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@endif

@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
<img src="/avatars/{{ Auth::user()->avatar }}" style="width: 30px; border-radius: 10%">
{{ Auth::user()->name }}
</a>

<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<a href="{{ route('user.profile') }}" class="dropdown-item">Profile</a>
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>

<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</li>
@endguest
</ul>

Step 10 – Test the Application

Start the Laravel development server:

php artisan serve

Visit http://127.0.0.1:8000/ in your browser to test the user profile update functionality.