Version 2: no lockPropertyFromSync!

I am trying to make an upgrade from V1 to V2 and found out that function lockPropertyFromSync does not exist anymore. This is not a problem can be replaced with something new, but… I do not see how the protection of properties are implemented in version 2?? In the src/ComponentConcerns/HandlesActions.php the row 25 was just deleted:

    throw_if(in_array($propertyName, $this->lockedModelProperties), new CannotBindDataToEloquentModelException($name));

And I do not see anywhere an alternative way to protect properties except throw exceptions in a onUpdatingXXXProperty function.

What is now the (un)official way to protect properties from modification?

Hey @Dimonka2,

what di you mean by protecting properties from modification?

Since you have to “whitelist” the bindable model properties using the $rules property, there is no need for any further protection. When you try to bind properties of a model, which are not defined within the $rules property, Livewire will automatically throw an exception.

Thanks, @maxeckel! this sounds good! So what will be a rule to let’s say make a user_id property read only? I do not see it in help.

Hey @Dimonka2,

i’d say, if it should be readonly, don’t bind it. Since it is good old Blade, simply echo out the value you need like:

<input type="text" readonly value="{{something.user_id}}"> 

Or where ever you need it :wink:

Ah no, @maxeckel. This was already massively discussed here. Basically you cannot make anything read only in Livewire. User may open browser console at any time and change user_id of the component by typing:

component.set('user_id', 1);

Currently it is possible to catch it in updating() function by throwing exception, but it was much easier in v1…

Yes that‘s true @Dimonka2, for that the $rules property won‘t help you. I got you wrong. If the user_id is a property of a model you have within your component, simply omit it from the rules array, you can still echo it out using blade, but it will be protected against modification. Only the properties of a model which have associated rules within the rules array can be bound and modified.

If the user_id is it’s own property within the component I’d simply model the old behavior within the updating hook (the global, not the field specific one)

I have a nice trait to manage such kind of “read only” properties:

    <?php
    namespace App\Traits;
    use Illuminate\Database\Eloquent\Model;

    trait PersistentProperties
    {

        protected $securePersistentProperties = true;
        public $persistentProperties = [];

        // add properties in a format ['propertyName' => Model]

        protected function addPersistentProperties(array $modelProperies)
        {
            foreach($modelProperies as $property => $model){
                if($model instanceof Model){
                    $this->persistentProperties[$property] = [
                        'id' => $model->getKey(),
                        'class' => get_class($model)
                    ];
                    $this->computedPropertyCache[$property] = $model;
                }
            }
        }

        // just in case you need to remove
        protected function removePersistentProperty($property)
        {
            if(!is_array($property)) $property = [$property];
            foreach ($property as $prop) {

                unset($this->persistentProperties[$prop]);
                unset($this->computedPropertyCache[$prop]);
            }
        }

        public function initializePersistentProperties()
        {

            if($this->securePersistentProperties){
                $this->lockPropertyFromSync('persistentProperties');
            }
        }

        public function __get($property)
        {
            // check if this is a persistent property
            $persistentProperty = $this->persistentProperties[$property] ?? null;

            if($persistentProperty) {
                $model = $this->computedPropertyCache[$property] ?? null;
                if(!$model) {
                    $model = $persistentProperty['class']::find($persistentProperty['id']);
                    $this->computedPropertyCache[$property] = $model;
                }
                return $model;
            }
            return parent::__get($property);
        }
    }

What it was doing is making given properties cached, defined as component property and read only with a very simple syntax:

    use PersistentProperties;
    public function mount(Forum $forum)
    {
        $this->addPersistentProperties(['forum' => $forum]);
    }

And after it is possible to use $this->forum and forum was authorized in the controller and you do not need to think about authorization again. But now I am starting from with the same problem again. If I define updating function in the trait, then I cannot define it in the component, not very flexible… Somehow version 1 was better in this sense.

Hey @Dimonka2,

very neat solution, i thought about something similar yesterday evening.
But if you need to authorize model properties it isn’t needed anymore, as this behavior comes through the $rules property.

Only the model properties defined in the $rules prop are bindable and have to follow the validation you define for them. All other properties of the model won’t be bindable and can also not be changed through “set”, so basically they are readonly in this case as you are still able to echo them out blade style.

Maybe I still get you wrong, but the $rules prop should be what you need.

Actually I do not have $rules in my components meaning $rules = [] and all public properties are bindable and modifiable.

All public properties yes, but not model properties. So when you have a model “$user” and want to access like i.e. “username” on it, this won’t work. You can echo it out, for sure, but not bind it to i.e. an input field. When you make the properties of the model accessible through public properties within the component, than this protection won’t work.

I never use Model as a public property because it is (in my opinion) not secure, especially with a $user example. I want to pass the user only fields I want to show him and not the entire model.
For a model I want to pass only model ID and the model Class so I can reload it again and I want to protect model ID and Class from user modification. That’s it.

Actually I found a simple solution for my particular case with trait:

    public function updatingPersistentProperties()
    {
        throw new Exception('Unable to modify "persistentProperties"!');
    }

And it seems that I am completely migrated to v2! I had to modify only $queryString, afterDomUpdate and this property lock. Not bad!