How to deal with disabled inputs


I’m trying to find the best way to deal with disabled inputs. This is the problem: you have a form, linked to a model, and some inputs can be filled when creating a new model, but cannot be changed if the model already exists.

So, putting a “disabled” into the input is the easy part, and works as expected. But if the input is wired to a model property, a malicious user can enable the input, change it, and the model property would be saved.

I’ve been toying with validator rules (mainly prohibited) but it’s not working because the property already has a value, so prohibited kicks even if the property hasn’t changed.

My final solution has been to check if the model exists in rules() and then load the original model from the database and add a rule in: . $original->property, but it seems like a hack.

How do you guys deal with this?

Either by removing the form element from the page completely, or remove the wire:model from it and assign it the value instead.

Yeah, but that feels kinda wrong also, because you’re putting some business rules into the view (what can and cannot be edited), and I would like that to be on the Livewire controller, so just looking at one place you know how it works.

However you’re doing your logic to add disabled to the element can be used to also conditionally add the wire:model or render the element, it’s fundamentally the same. Or are you saying you don’t like how you’re doing that to begin with?

I think it’s not the same thing, setting an input element as disabled is done to show the user the value, but tell him that that value cannot be changed, and that logic has to be on the view.

But i’m talking about enforcing that property to be immutable, and that logic should live on the backend (on the controller, the model or wherever fits your application).

With the first (disabling the element, making it read-only, removing the wiring or the input to show only the value) doesn’t assures you that a malicious user cannot forge a request and change that property.

To start, when you have an element like this on your initial page load:

<input value="whatever" disabled />

If you try to inject wire:model="$var" on that input, it won’t do anything because livewire isn’t tracking it, so that wire:model should be put in via a conditional if it’s not disabled. It’s still possible to change the $var on a different element though. (It will revert back to the original $var you modeled it to after the round trip, but the change will still happen first.) Plus to me it’s a smell to have a wire:model on a disabled input anyway.

Edit To Add: It’s been a while since I tested this and I doubt that it has changed, but it’s possible this is outdated and you should test it for yourself.

Let’s go down the million ways to skin a cat road, and start out in the component.

Hard code the attributes being updated in a separate method, conditional the method to call in your wire:click or form:submit, however you’re calling them. Then even if another property gets changed, it’s never being assigned to the model.

You could also have your component set up with only properties available, extend it with another component class and add the properties available for creation, do a polymorphic call for which of those two components should be called. Would be possible to have them both use the same view.

Go the crazy route of extreme obfuscation and name your modelable properties some randomness.

Create a immutable concept for your models. Something like (psudo code):

protected array $immutable = ['always', 'immutable'];

public function makeImmutable(string $attribute)
    $this->immutable[] = $attribute;

public function __set($key, $value)
    throw_if(array_key_exists($key, $this->immutable), AttributeImmutableException::class);
    parent::__set($key, $value);

Something like that would give you some defaults and the ability to call $model->makeImmutable('another') as needed. I’m sure I’m not the first person to come up with something like that and there’s probably a more feature rich package out there somewhere.

Just keep experimenting until you find something comfortable.

Thanks for your explanation!

I’ll dig into that and see how it works, but it seems to me that this kind of solution will fit great.