How to update Eloquent models?

I’m a bit lost on the best method to update Eloquent Models with Livewire after upgrading from 0.4.*.

For example; this is how I used to do it (I removed unrelated code):

Class Person extends Component
{
    protected $person = null;
    public $name, $emailaddress;

    public function update(){
        // Triggered on wire:click
        $this->validate([
            'name' => 'required',
            'emailaddress' => 'required|email',
        ]);

        $this->person->name = $this->name;
        $this->person->emailaddress = $this->emailaddress;
        $this->person->save();

        // The form is in a modal component
        $this->emit('close-modal');
    }

    public function mount($person_id){
        $this->person = \App\Person::find($person_id);
        $this->name = $this->person->name;
        $this->emailaddress = $this->person->emailaddress;
    }

    public function render(){
        return view('person.edit');
    }
}

Simplified blade view:

<input type="text" wire:model="name">
<input type="text" wire:model="emailaddress">
<button type="button" wire:click="update">Save</button>

Since 0.5.* we should be able to use Eloquent models in public properties, but does this simplify such a component as well? I somehow expected it to work like this, but it doesn’t:

Class Person extends Component
{
    public $person;

    public function update(){
        // Triggered on wire:click
        $this->validate([
            'person.name' => 'required',
            'person.emailaddress' => 'required|email',
        ]);

        $this->person->save();

        // The form is in a modal component
        $this->emit('close-modal');
    }

    public function mount($person_id){
        $this->person = \App\Person::find($person_id);
    }

    public function render(){
        return view('person.edit');
    }
}

Simplified blade view:

<input type="text" wire:model="person.name">
<input type="text" wire:model="person.emailaddress">
<button type="button" wire:click="update">Save</button>

Am I right that the way to go is as follows? Or can it be simplified? Which would especially be nice for models lots of attributes / large forms?

Class Person extends Component
{
    public $person, $name, $emailaddress;

    public function update(){
        // Triggered on wire:click
        $this->validate([
            'name' => 'required',
            'emailaddress' => 'required|email',
        ]);

        $this->person->name = $this->name;
        $this->person->emailaddress = $this->emailaddress;
        $this->person->save();

        // The form is in a modal component
        $this->emit('close-modal');
    }

    public function mount($person_id){
        $this->person = \App\Person::find($person_id);
        $this->fill($this->person);
    }

    public function render(){
        return view('person.edit');
    }
}

Thanks!

Yes, the last code block is correct. I went into detail here about what it going on with a model being a public property.

You can simplify it with something like $this->workingPerson = $this->person->toArray(), and bind all the properties with dot notation workingPerson.name if you don’t want a ton of properties.

2 Likes

Hi, everyone!

It’s my solution and I like to share:

Create one component form

form.blade.php

<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Edit product') }}</div>

                <div class="card-body">

                    <div class="form-group row">
                        {{ Form::hidden('fields.id', null, ['id' => 'fields.id']) }}
                        <label for="fields.name" class="col-md-2 col-form-label text-md-right">{{ __('Name') }}
                            *</label>
                        <div class="col-md-10">

                            {{ Form::text('fields.name', old('fields.name'), ['class' => 'form-control ' . ( $errors->has('fields.name') ? ' is-invalid' : '' ), 'wire:model.lazy' => 'fields.name', 'autofocus', 'placeholder' => __('Product description'), 'required' ]) }}
                            @error('fields.name')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="fields.price" class="col-md-2 col-form-label text-md-right">{{ __('Price') }}
                            *</label>

                        <div class="col-md-10">
                            {{ Form::number('fields.price', old('fields.price'), ['class' => 'form-control ' . ( $errors->has('fields.price') ? ' is-invalid' : '' ),'wire:model.lazy' => 'fields.price', 'step' => '0,01', 'placeholder' => '0,00', 'required']) }}
                            @error('fields.price')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>
                    </div>

                </div>
                <div class="card-footer justify-content-between">
                    <input class="btn btn-primary" wire:click="update" type="submit" value="{{ __('Save product') }}">
                    <a href="{{ route('products') }}" class="btn btn-secondary float-right">{{ __('Close') }}</a>
                </div>

            </div>
        </div>
    </div>
</div>

and class componet Form.php

 class Form extends Component

    {

        public $fields, $field_id = null;

        public function mount($product = null)

        {

            if ( isset($product) ) {

                $this->fields = Product::findOrFail($product)->toArray();

                $this->field_id =  $this->fields['id'];

            };

        }

        public function update()

        {

            // Triggered on wire:click

            $this->validate([

                'fields.name'  => 'required|min:3',

                'fields.price' => 'required|numeric|max:999999.99',

            ]);

            // dd($this->fields, $this->field_id);

            Product::updateOrCreate(['id' => $this->field_id], $this->fields);

            session()->flash('message', 'Product successfully created or updated.');

            return redirect()->route('products');

        }

        public function render()

        {

            return view('livewire.admin.products.form');

        }

    }

the route is:

Route::livewire('products/form/{product?}', 'admin.products.form')->name('products.form');

So, I had the same component to Edit and Createm. The only part that I wanted do something better it’s about the “id” field.

I would appreciate some tips.

Thanks!

PS: I used laravel collective html (https://laravelcollective.com/docs/6.0/html)… next I’ll try some modal form and kdion4891/laravel-livewire-forms