Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Seydina's avatar

How to add picture profile update function in Laravel (v 10.28.0)breeze blade (update-profile-information)? [SOLVED]

I just added a picture column to the users table in Laravel breeze. Pictures are stored app/pictures with a symlink. I also created a view component in which an alpine js picture preview is defined. The component code is here :

<div class="flex items-center" x-data="picturePreview()">
    <div class="rounded-md bg-gray-200 mr-2">
        <span class="text-xs text-center text-gray-500">
            <img src="imageUrl" :src="imageUrl" alt="Photo de profil"
                class="w-24 h-24 rounded-md object-cover border-4 border-dark-300">
        </span>
    </div>
    <div>
        <x-secondary-button @click="document.getElementById('picture').click()" class="relative">
            <div class="flex items-center text-sm font-normal normal-case">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24"
                    stroke="currentColor" stroke-width="2">
                    <path stroke-linecap="round" stroke-linejoin="round"
                        d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
                </svg>
                Ajouter une photo
            </div>
            <input @change="fileChosen(event)" type="file" name="picture" id="picture"
                class="absolute inset-0 -z-10 opacity-0">
        </x-secondary-button>
    </div>
    <script>
        function picturePreview() {
    return {
        imageUrl: "",

        fileChosen(event) {
            this.fileToDataUrl(event, (src) => (this.imageUrl = src));
        },

        fileToDataUrl(event, callback) {
            if (!event.target.files.length) return;

            let file = event.target.files[0],
                reader = new FileReader();

            reader.readAsDataURL(file);
            reader.onload = (e) => callback(e.target.result);
        },
    };
}
    </script>
</div>

All works fine. I added this component in register.blade.php form, modified RegisterController and User model. Picture image is uploaded normally and I show it as profile picture well. as you can see with this code :

                               @if (Auth::user()->picture)
                                <img src="{{ asset(Auth::user()->picture) }}" alt="" class="w-16 h-16 rounded-full object-cover">
                                @else
                                <!-- If profile photo not available -->
                                <img src="{{ URL('images/user-default-avatar.png') }}" alt=""
                                    class="w-16 h-16 object-cover mx-2">
                                @endif

Now I want to add above breeze Profile Information inputs (name & email) in update-profile-information-form.blade.php picture photo and updating button as I do on register form. What I want now is to show already uploaded image or default user picture (when user has not given one when registering) on update-profile-information-form.blade.php with the possibility to change it. How can I achieve this?

0 likes
13 replies
tangtang's avatar

@seydina

have you already add all the necessary field in the profile update form ?

and you can use this in your form update again

<input @change="fileChosen(event)" type="file" name="picture" id="picture" class="absolute inset-0 -z-10 opacity-0">

and in your controller function update profile, check if this input file have value or not, just process file when input file have the value.

Seydina's avatar

@tangtang In my profile update form view I added this above name and email fields to show already uploaded picture :

                               @if (Auth::user()->picture)
                                <img src="{{ asset(Auth::user()->picture) }}" alt="" class="w-16 h-16 rounded-full object-cover">
                                @else
                                <!-- If profile photo not available -->
                                <img src="{{ URL('images/user-default-avatar.png') }}" alt=""
                                    class="w-16 h-16 object-cover mx-2">
                                @endif

This is what I do for RegisterUserController :

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Illuminate\View\View;
use App\Helpers\Helper;

class RegisteredUserController extends Controller
{
    /**
     * Display the registration view.
     */
    public function create(): View
    {
        return view('auth.register');
    }

    /**
     * Handle an incoming registration request.
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:' . User::class],
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
            'picture' => ['file', 'mimes:jpg,png,gif', 'max:3072'],
        ]);
        $path = null;
        if ($request->hasFile('picture')) {
            $path = $request->file('picture')->storePublicly('pictures');
        }
       
        
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
            'picture' => $path,
        ]);

        event(new Registered($user));

        Auth::login($user);

        return redirect(RouteServiceProvider::HOME);
    }
}

For profile controller I haven't changed anything yet :

<section>
    <header>
        <h2 class="text-lg font-medium text-gray-900">
            {{ __('Informations de profil') }}
            {{-- Profile Information --}}
        </h2>

        <p class="mt-1 text-sm text-gray-600">
            {{ __("Mettez à jour les informations de profil et l'adresse e-mail de votre compte.") }}
            {{-- Update your account's profile information and email address. --}}
        </p>
    </header>

    <form id="send-verification" method="post" action="{{ route('verification.send') }}">
        @csrf
    </form>

    <form method="post" action="{{ route('profile.update') }}" class="mt-6 space-y-6">
        @csrf
        @method('patch')
        <div>
            <x-input-label for="name" :value="__('Prénom et nom')" />
            <x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name', $user->name)"
                required autofocus autocomplete="name" />
            <x-input-error class="mt-2" :messages="$errors->get('name')" />
        </div>

        <div>
            <x-input-label for="email" :value="__('Email')" />
            <x-text-input id="email" name="email" type="email" class="mt-1 block w-full"
                :value="old('email', $user->email)" required autocomplete="username" />
            <x-input-error class="mt-2" :messages="$errors->get('email')" />

            @if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())
            <div>
                <p class="text-sm mt-2 text-gray-800">
                    {{ __('Votre adresse e-mail n\'est pas vérifiée.') }}
                    {{-- Your email address is unverified. --}}

                    <button form="send-verification"
                        class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                        {{ __('Cliquez ici pour renvoyer l\'e-mail de vérification.') }}
                        {{-- Click here to re-send the verification email. --}}
                    </button>
                </p>

                @if (session('status') === 'verification-link-sent')
                <p class="mt-2 font-medium text-sm text-green-600">
                    {{ __('Un nouveau lien de vérification a été envoyé à votre adresse e-mail.') }}
                    {{-- A new verification link has been sent to your email address. --}}
                </p>
                @endif
            </div>
            @endif
        </div>

        <div class="flex items-center gap-4">
            <x-primary-button>{{ __('Enregistrer') }}</x-primary-button>
            {{-- Save --}}

            @if (session('status') === 'profile-updated')
            <p x-data="{ show: true }" x-show="show" x-transition x-init="setTimeout(() => show = false, 2000)"
                class="text-sm text-gray-600">{{ __('Enregistré.') }}</p>
            {{-- Saved. --}}
            @endif
        </div>
    </form>
</section>
tangtang's avatar

@Seydina

your code seems good. just add the input type file in the form like the code you do in register user.

you already have this route {{ route('profile.update') }}

and I assume this route target is for update profile controller, right ?

and you can do this code for update function, but customize it with your app requirement.

public function update(Request $request): RedirectResponse
{
    $request->validate([
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,' . $id],
        'password' => ['nullable', 'confirmed', Rules\Password::defaults()],
        'picture' => ['nullable', 'file', 'mimes:jpg,png,gif', 'max:3072'],
    ]);

    $user = User::find($request->id); // find the user by ID or email or what you choose to be unique key for this user

    if (!$user) {
        return redirect()->route(' route here . . .'); // handle the case where the user is not found.
    }

    $user->name = $request->name; 
    $user->email = $request->email;

    if (!empty($request->password)) {
        $user->password = Hash::make($request->password);
    }

    if ($request->hasFile('picture')) {
        // handle updating the user's picture as you did in the 'store' method
        $path = $request->file('picture')->storePublicly('pictures');
        $user->picture = $path;
    }

    $user->save();

    return ??? // do the return code you need here
}
Seydina's avatar

@tangtang Sorry I sent you update-profile-information-form.blade.php file instead of ProfileController.php file which is below :

<?php

namespace App\Http\Controllers;

use App\Http\Requests\ProfileUpdateRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\View\View;

class ProfileController extends Controller
{
    /**
     * Display the user's profile form.
     */
    public function edit(Request $request): View
    {
        return view('profile.edit', [
            'user' => $request->user(),
        ]);
    }

    /**
     * Update the user's profile information.
     */
    public function update(ProfileUpdateRequest $request): RedirectResponse
    {
        $request->user()->fill($request->validated());

        if ($request->user()->isDirty('email')) {
            $request->user()->email_verified_at = null;
        }

        $request->user()->save();

        return Redirect::route('profile.edit')->with('status', 'profile-updated');
    }

    /**
     * Delete the user's account.
     */
    public function destroy(Request $request): RedirectResponse
    {
        $request->validateWithBag('userDeletion', [
            'password' => ['required', 'current_password'],
        ]);

        $user = $request->user();

        Auth::logout();

        $user->delete();

        $request->session()->invalidate();
        $request->session()->regenerateToken();

        return Redirect::to('/');
    }
}

So before I use you custom code, I thought I'd show you the original Profile controller code before making the changes suggested by your Profile controller.

tangtang's avatar

@Seydina

this code is fine, and with this code not need to add the full customized code from my response before. just add this code before $request->user()->save(); in your function update.

if ($request->hasFile('picture')) {
        // Handle updating the user's picture as you did in the 'store' method
        $path = $request->file('picture')->storePublicly('pictures');
        $request->user()->picture = $path;
}
Seydina's avatar

@tangtang I'm going to add this code to see if it solves my problem and I'll get back to you tomorrow because I'm a little busy elsewhere

Seydina's avatar

@tangtang For simplicity I gave the ProfileController the task of creating a profile picture for the users instead of doing it when user create a new account with the RegisterUserController. Here are the changes made:

  • move method $request->validate([ 'picture' => ['file', 'mimes:jpg,png,gif', 'max:3072'],)] of RegisterUserController to ProfileController
  • delete 'picture' => $path, from $user = User::create([...]); of RegisterController
  • add this piece of code before $request->user()->save(); in update method :
if ($request->hasFile('picture')) {
            // Handle updating the user's picture as you did in the 'store' method
            $path = $request->file('picture')->storePublicly('pictures');

            $request->user()->picture = $path;
        }

when I upload picture from update-profile-information.form and click on save nothing happen in picture column (users table) that remain NULL. I think there is some issue with ProfileController of handling picture.

tangtang's avatar

@Seydina

  • you need to check this column picture in User model, is this column registered as fillable ?

like this for the model example

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
	// other code here . . .
    protected $fillable = ['name', 'picture', 'password', 'other fillable field . . .'];
	// other code here . . .
}
  • did you try to dd this $request->hasFile('picture') and $request->file('picture') to make sure this input have the value ?

  • did the picture is uploaded to target folder ?

Seydina's avatar

@tangtang yes 'picture' column is fillable in User Model :

  • protected $fillable = ['name', 'email', 'password', 'picture',];
  • dd($request->hasFile('picture')) returns false
  • dd($request->file('picture')) returns null
  • picture is not uploaded in target folder
Seydina's avatar

@tangtang Thanks I have not paid attention to that, I put enctype="multipart/form-data" and picture is loaded.

The second issue I encountered is after picture uploading I want to show it in preview zone instead of showing always gray background of preview zone as shown in theses images :

As you can see, profile picture is displayed on header with this code in navigation-blade.php view :

 @if (Auth::user()->picture)
 <img src="{{ asset(Auth::user()->picture) }}" alt="" class="w-10 h-10 rounded-full border-1 border-amber-500 object-cover mr-2">
 @else
 <img src="{{ URL('images/user-default-avatar.png') }}" alt="" class="w-8 h-8 object-cover mr-2">
 @endif

After uploading picture, I want the preview gray zone to update and show actual profile picture stored in database. I do not know how to target this in alpine js.

This is the Alpine JS preview function wrapped in a view component <x-input-picture />

<div class="flex items-center" x-data="picturePreview()">
    <div class="rounded-md bg-gray-200 mr-2">
        <span class="text-xs text-center text-gray-500">
            <img src="imageUrl" :src="imageUrl" alt="Photo de profil"
                class="w-24 h-24 rounded-md object-cover border-4 border-dark-300">
        </span>
    </div>
    <div>
        <x-secondary-button @click="document.getElementById('picture').click()" class="relative">
            <div class="flex items-center text-sm font-normal normal-case">
                <span class="material-icons-outlined">add_a_photo</span>
            </div>
            <input @change="fileChosen(event)" type="file" name="picture" id="picture"
                class="absolute inset-0 -z-10 opacity-0">
        </x-secondary-button>
    </div>
    <script>
        function picturePreview() {
    return {
        imageUrl: "",

        fileChosen(event) {
            this.fileToDataUrl(event, (src) => (this.imageUrl = src));
        },

        fileToDataUrl(event, callback) {
            if (!event.target.files.length) return;

            let file = event.target.files[0],
                reader = new FileReader();

            reader.readAsDataURL(file);
            reader.onload = (e) => callback(e.target.result);
        },
    };
}
    </script>
</div>
tangtang's avatar
tangtang
Best Answer
Level 6

@Seydina

you can try this logic to display image

just change your <img> element code

this is the reference

<img :src="imageUrl ? imageUrl : '{{ Auth::user()->picture ? asset(Auth::user()->picture) : URL('images/user-default-avatar.png') }}'" alt="Photo de profil" class="w-24 h-24 rounded-md object-cover border-4 border-dark-300">

other code seems is fine

update :

the logic of this code is

  • if user clicked on upload photo, the preview will display the user selected image
  • if the user has upload photo before, the photo will be displayed
  • user not selected an image and has never uploaded a photo before, default avatar will be displayed
Seydina's avatar

@tangtang Great and great again!!! You saved me a great time. All works fine! Congrats!!!

Please or to participate in this conversation.