How to use Laravel Livewire with Eloquent Models?

Viewed 9704

I am in the process of trying out Laravel Livewire with a project for the first time. I am trying to build a fairly simple form with one set of cascading dropdowns - one input dependent on the other. But, when I try to pass an eloquent object to a component property, it is converted to a string so I can not access any of the object's properties.

I feel like there is something very simple I may be missing.

Here is my component code:

PostComponent.php

class PostComponent extends Component
{
    public $manufacturers;
    public $manufacturer = 'Select a Manufacturer';
    public $cars;

    public function mount()
    {
        $this->manufacturers = Manufacturer::orderBy('name')->get(); 
    }
    
    public function updated()
    {
        $this->cars = Car::where('manufacturer_id', $this->manufacturer->id)->get();
    }

    public function render()
    {
        return view('livewire.post-component');
    }

Here is my blade file:

<label for="manufacturer">Manufacturer</label>
<select wire:model="manufacturer" id="manufacturer">
    <option selected="selected" disabled>Select a Manufacturer</option>
        @foreach($manufacturers as $selectableManufacturer)
            <option value="{{ $selectableManufacturer }}">{{ $selectableManufacturer->name }}</option>
        @endforeach
</select>

When I attempt to select a manufacturer in the dropdown, I can log the $manufacturer and see that the value is

{"id":16,"name":"Manufacturer1","description":"lorem ipsum","created_at":"2020-06-26T23:44:37.000000Z","updated_at":"2020-06-26T23:44:37.000000Z"}

But when I try to get the id of the manufacturer when attempting to select cars in the updated lifecycle hook, I get the error

Trying to get property 'id' of non-object

Any idea as to why this is happening and how to fix it?

3 Answers

The issue with your implementation is that, in the select box what you echo out in the blade is a string, not an object, so wire modal will do its job by getting the value of the input event and sync it with the backend. There are two ways that you can achieve your goal.

  1. Give the value of the select option as the manufacturer id, then you can directly use it in the updated lifecycle hook.

 <label for="manufacturer_id">Manufacturer</label>
    <select wire:model="manufacturer_id" id="manufacturer_id">
        <option selected="selected" disabled>Select a Manufacturer</option>
        @foreach($manufacturers as $selectableManufacturer)
        <option value="{{ $selectableManufacturer->id }}">{{ $selectableManufacturer->name }}</option>
        @endforeach
    </select>

In the updated hook,

 
 public $manufacturer_id; 
 public function updated() 
    {
        $this->cars = Car::where('manufacturer_id', $this->manufacturer_id)->get();
    }

  1. Or just use the json_decode() function to make it an object and then access the id property as you have done.

 public function updated()
    {
        $manufacturer = json_decode($this->manufacturer);
        $this->cars = Car::where('manufacturer_id', $manufacturer->id)->get();
    }

But the healthy approach would be to use the id as the value.

You should sync the id of the selected manufacturer and use that in an updated hook. Here is how you can do it.

class PostComponent extends Component
{
    public $manufacturers;
    public $manufacturer_id;
    public $manufacturer = 'Select a Manufacturer';
    public $cars;

    public function mount()
    {
        $this->manufacturers = Manufacturer::orderBy('name')->get(); 
    }
    
    public function updatedManufacturerId()
    {
        $this->cars = Car::where('manufacturer_id', $this->manufacturer_id)->get();
    }

    public function render()
    {
        return view('livewire.post-component');
    }
}

The frontend can remain same.

    <label for="manufacturer_id">Manufacturer</label>
    <select wire:model="manufacturer_id" id="manufacturer_id">
        <option selected="selected" disabled>Select a Manufacturer</option>
        @foreach($manufacturers as $selectableManufacturer)
        <option value="{{ $selectableManufacturer->id }}">{{ $selectableManufacturer->name }}</option>
        @endforeach
    </select>
Related