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

basvandertogt's avatar

forceFill() incorrectly contains properties of $this->rules

I implemented the jetstream profile photo upload field and it's working like a charm but only when i comment out some rules

protected function rules()
{
    return [
        'user.firstname' => '',
        'user.lastname' => '',
        'user.password' => '',
//        'user.password_confirmation' => '',
        'user.email' => '',
        'user.phone' => '',
//        'user.team' => '',
//        'user.role' => ''
    ];
}

When i enable those rules i get this error when save or delete the photo: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'password_confirmation' in 'field list' (SQL: update users set profile_photo_path = ?, password_confirmation = ?, team = ?, role = ?, users.updated_at = 2022-11-24 15:39:19 where id = 4)

Why are the fields password_confirmation, team and role included? How can i exclude those fields from updateProfilePhoto()?

Below my entire Livewire Component:

<?php

namespace App\Http\Livewire\Users;

use App\Models\Proposal;
use App\Models\Team;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\Rule;
use Laravel\Jetstream\Jetstream;
use Laravel\Jetstream\Mail\TeamInvitation;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\WithPagination;
use App\Http\Livewire\DataTable\WithSorting;
use Illuminate\Support\Arr;

class Users extends Component
{
    use WithPagination, WithSorting, WithFileUploads;

    public $photo;
    public User $user;
    public $isOpen = 0;
    public $isInviteOpen = 0;
    public $search = [
        'firstname' => '',
        'lastname' => '',
        'email' => '',
        'team_id' => ''
    ];
    public $userDeletion;
    public $confirmingUserDeletion = false;
    public $sortField = 'lastname';
    public $sortDirection = 'asc';
    public $queryString = ['sortField', 'sortDirection'];
    public $edit = false;
    public $teamRelationDeletion;
    public $teamsWithoutCurrent;
    public $moveRelationsToUser;

    public function updatingSearch()
    {
        $this->resetPage();
    }

    protected function rules()
    {
        return [
            'user.firstname' => '',
            'user.lastname' => '',
            'user.password' => '',
//            'user.password_confirmation' => '',
            'user.email' => '',
            'user.phone' => '',
//            'user.team' => '',
//            'user.role' => ''
        ];
    }

    public function mount()
    {
        if (!Auth::user()->hasTeamRole(Auth::user()->currentTeam, 'admin')) {
            return abort(403);
        }

        $this->user = new User();
    }

    public function render()
    {
        $roles = Jetstream::$roles;

        return view('livewire.users.users', [
            'teams' => Team::pluck('name', 'id'),
            'roles' => Arr::pluck($roles, 'name', 'key'),
            'users' => User::filter($this->search)->orderBy($this->sortField, $this->sortDirection)->paginate(25),
        ]);
    }

    public function store()
    {
        $validate = [
            'photo' => ['nullable', 'mimes:jpg,jpeg,png', 'max:1024'],
            'user.firstname' => 'required',
            'user.lastname' => 'required',
            'user.email' => ['required', Rule::unique('users', 'email')->ignore($this->user->id, 'id')->whereNull('deleted_at')],
            'user.phone' => 'required',
            'user.password' => ['required', 'min:8', 'confirmed', 'case_diff', 'numbers', 'letters', 'symbols'],
            'user.password_confirmation' => 'required',
            'user.team' => 'required',
            'user.role' => 'required'
        ];

        if ($this->edit) {
            unset($validate['user.password']);
            unset($validate['user.password_confirmation']);
            unset($validate['user.team']);
            unset($validate['user.role']);
        }

        $this->validate($validate);

        if ($this->user->id) {
            $user = User::find($this->user->id);
            $user->update($this->user->toArray());
        } else {
            $this->user->current_team_id = $this->user->team;
            $this->user->password = Hash::make($this->user->password);
            $user = User::create($this->user->toArray());
            $user->teams()->attach($this->user->team, ['role' => $this->user->role]);

    //            $invitation = $team->teamInvitations()->create([
    //                'email' => $this->user['email'],
    //                'role' => $this->user['role']
    //            ]);
    //
    //            Mail::to($this->user['email'])->send(new TeamInvitation($invitation));
        }

        if ($this->photo) {
            $this->user->updateProfilePhoto($this->photo);
        }

        $this->dispatchBrowserEvent('banner-message', [
            'style' => 'info',
            'message' => sprintf('Gebruiker %s succesvol ', $this->user->fullname) . ($this->user->id ? 'bijgewerkt!' : 'toegevoegd!')
        ]);

        $this->closeModal();
        $this->resetInputFields();
    }

    /**
     * Delete user's profile photo.
     *
     * @return void
     */
    public function deleteProfilePhoto()
    {
        $this->user->deleteProfilePhoto();

        $this->edit($this->user->id);
    }

    public function storeInvite()
    {
        $this->validate([
            'user.team' => 'required',
            'user.role' => 'required'
        ]);

        $this->user->teams()->attach($this->user->team, ['role' => $this->user->role]);

//        $invitation = $team->teamInvitations()->create([
//            'email' => $this->user['email'],
//            'role' => $this->user['role']
//        ]);
//
//        Mail::to($this->user['email'])->send(new TeamInvitation($invitation));

        $this->closeInviteModal();
        $this->resetInputFields();
    }

    public function deletingTeamRelation($userId, $teamId)
    {
        $this->teamRelationDeletion = [];
        $this->teamRelationDeletion['user'] = User::findOrFail($userId);
        $this->teamRelationDeletion['team'] = Team::findOrFail($teamId);
        $this->confirmingTeamRelationDeletion = true;
    }

    public function deleteTeamRelation($userId, $teamId)
    {
        User::find($userId)->teams()->detach($teamId);
        $this->teamRelationDeletion = [];
        $this->confirmingTeamRelationDeletion = false;
    }

    public function cancelDeletionTeamRelation()
    {
        $this->teamRelationDeletion = [];
        $this->confirmingTeamRelationDeletion = false;
    }

    public function deletingUser($id)
    {
        $this->userDeletion = User::findOrFail($id);
        $this->confirmingUserDeletion = true;

        $this->otherUsers = User::where('id', '!=', $this->userDeletion->id)->get()->pluck('name', 'id')->toArray();
    }

    public function deleteUser($id)
    {
        $validate = ['moveRelationsToUser' => 'required'];
        $this->validate($validate);

        Proposal::where('user_id', '=', $id)->update(['user_id' => $this->moveRelationsToUser]);

        User::find($id)->delete();
        $this->confirmingUserDeletion = false;
        $this->moveRelationsToUser = null;
    }

    public function edit($id)
    {
        $this->user = User::findOrFail($id);
        $this->edit = true;
        $this->openModal();
    }

    public function create()
    {
        $this->resetInputFields();
        $this->openModal();
    }

    public function openModal()
    {
        $this->isOpen = true;
    }

    public function closeModal()
    {
        $this->isOpen = false;
        $this->edit = false;
        $this->resetValidation();
    }

    public function createInvite($id)
    {
        $this->resetInputFields();
        $this->user = User::findOrFail($id);
        $this->teamsWithoutCurrent = Team::whereNotIn('id', $this->user->teams->pluck('id'))->pluck('name', 'id');
        $this->openInviteModal();
    }

    public function openInviteModal()
    {
        $this->isInviteOpen = true;
    }

    public function closeInviteModal()
    {
        $this->isInviteOpen = false;
        $this->resetValidation();
    }

    private function resetInputFields()
    {
        $this->user = new User();
    }

    public function sortBy($field)
    {
        if ($this->sortField === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortDirection = 'asc';
        }

        $this->sortField = $field;
    }
}
0 likes
8 replies
AungHtetPaing__'s avatar

@basvandertogt

If you defined livewire rules method, just use $this->validate() livewire validate based on rules set in rules(). Right now you have rules() with empty string and also define validation rule in store(). So I think livewire rule is adding password_confirmation team role fields. Remove livewire rules() if you don't use it.

And you don't need 'user.password_confirmation' => 'required' you already have connfirmed rule in password field.

https://laravel-livewire.com/docs/2.x/input-validation

https://laravel.com/docs/9.x/validation#rule-confirmed

basvandertogt's avatar

@AungHtetPaing__ You're right but that's an other discussion ;-) Main question is how to exclude all other fields in $this->user->updateProfilePhoto($this->photo);

1 like
AungHtetPaing__'s avatar

@basvandertogt

So I think livewire rule is adding password_confirmation team role fields.

that is what I said in my previous replies based on your question "Why are the fields password_confirmation, team and role included?". I don't see what is inside your "updateProfilePhoto" and you said that is working fine if you comment out the livewire rule right? So my assumption is livewire rule adding those columns even though you unset those columns from array.

You're right but that's an other discussion

NO I don't think so.

basvandertogt's avatar

@AungHtetPaing__ Let's make it more clear. I'm looking for a solution not a discussion :-) The updateProfilePhoto() is a trait from Jetstream (/vendor/laravel/jetstream/src/HasProfilePhoto.php).

/**
     * Update the user's profile photo.
     *
     * @param  \Illuminate\Http\UploadedFile  $photo
     * @return void
     */
    public function updateProfilePhoto(UploadedFile $photo)
    {
        tap($this->profile_photo_path, function ($previous) use ($photo) {
            $this->forceFill([
                'profile_photo_path' => $photo->storePublicly(
                    'profile-photos', ['disk' => $this->profilePhotoDisk()]
                ),
            ])->save();

            if ($previous) {
                Storage::disk($this->profilePhotoDisk())->delete($previous);
            }
        });
    }

I don't understand why $this->forceFill() includes my other fields. How can i tell forceFill() only to fill profile_photo_path and not my other fields?

Please or to participate in this conversation.