Home > other >  Problem with Laravel Livewire : Attempt to read property "recipes" on array
Problem with Laravel Livewire : Attempt to read property "recipes" on array

Time:02-10

I'm trying to do a search input from 2 datatables of my database, one containing vegetables and the other one containing recipes made of vegetables, i first made my search input with only the elements from the vegetable table. But now im trying to include the repice table but it wont work: I'm getting the error "Attempt to read property "recipes" on array" but I do't understand where the problem comes from.

This is my code:

Search.php:

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\Vegetable;

class Search extends Component
{
    public $query = '';

    public $vegetables;
    public $recipes;

    public function updatedQuery() 
    {
        $this->vegetables = Vegetable::where('name', 'like', '%'.$this->query.'%')->get()->toArray();
    }

    public function render()
    {
        return view('livewire.search');
    }
}

search.blade.php :

<div >
    <h1>Recherche</h1>
    <input wire:model="query" type="text" placeholder="Rechercher...">
    
    @if(!empty($query))
        <ul>
            @if(!empty($vegetables))
                <div >

                    @foreach($vegetables as $vegetable)
                        <li><span >lunch_dining</span>{{ $vegetable['name'] }}</li>
                    @endforeach
                
                </div>
            @else
                <li>Pas de résultat</li>
            @endif
            
            <div >

                @foreach($vegetables as $vegetable)
                    @foreach($vegetable->recipes as $recipe)
                        <li><span >menu_book</span>{{ $recipe['name'] }}<span >Ingrédient: {{ $vegetable['name'] }}</span></li>
                    @endforeach
                @endforeach
            </div>
        </ul>
    @endif
</div>

My model Vegetable :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Vegetable extends Model
{
    use HasFactory;

    public $timestamps = false;

    protected $fillable = ['name'];

    public function recipes(){
        return $this->belongsToMany(Recipe::class, 'vegetables_recipes', 'vegetable_id', 'recipe_id');
    }

    public function getName($id) {
        return $this->name;
    }
}

My model Recipe :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Recipe extends Model
{
    use HasFactory;
    public $timestamps = false;

    protected $fillable = ['name'];

    public function vegetables(){
        return $this->hasOne(Vegetable::class, 'vegetables_recipes', 'recipe_id', 'vegetable_id');
    }

    public function getName($id) {
        return $this->name;
    }
}

The model from my pivot table VegetablesRecipe :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class VegetablesRecipe extends Model
{
    use HasFactory;

    public $timestamps = false;

    protected $fillable = ['vegetable_id', 'recipe_id'];
}

Thank you in advance

CodePudding user response:

So I found the issue with your code; I didn't notice it yesterday as it was hidden off-screen in your Search.php's updatedQuery() function:

$this->vegetables = Vegetable::where('name', 'like', '%'.$this->query.'%')
->get()
->toArray();

When you call ->toArray(), you convert the Collection of Vegetable Model instances into an Array of Arrays. This means you cannot use Object Access to get Properties:

$vegetable->recipes; // Fails with error "Attempt to read property "recipes" on array"

Additionally, you cannot access any Model functions, as an Array is not an instance of a Model:

$vegetables['recipes']; // Undefined array key "recipes"

Since public function recipes() is a method of the Vegetable.php Model class, that won't work. It can work, if you load it first before casting to an Array:

$this->vegetables = Vegetable::where('name', 'like', '%'.$this->query.'%')
->with('recipes')
->get()
->toArray();

Now, $vegetable['recipes'] will work, but it's better to just drop the ->toArray() completely:

$this->vegetables = Vegetable::where('name', 'like', '%'.$this->query.'%')
->with('recipes')
->get();

Additionally, for performance reasons, you want to include ->with('recipes') to prevent N 1 queries being called while looping.

With those changes made, you can write your livewire/search.blade.php code as follows:

<div >
  <h1>Recherche</h1>
  <input wire:model="query" type="text" placeholder="Rechercher...">
  @if(!empty($query))
    <ul>
      @if(!empty($vegetables))
        <div >
          @foreach($vegetables as $vegetable)
            <li><span >lunch_dining</span>{{ $vegetable->name }}</li>
          @endforeach
        </div>
      @else
        <li>Pas de résultat</li>
      @endif
      <div >
        @foreach($vegetables as $vegetable)
          @foreach($vegetable->recipes as $recipe)
            <li><span >menu_book</span>{{ $recipe->name }}<span >Ingrédient: {{ $vegetable->name }}</span></li>
          @endforeach
        @endforeach
      </div>
    </ul>
  @endif
</div>

Lastly, some cleanup:

Vegetable.php and Recipe.php

The methods getName($id) have no purpose; you're not doing anything with $id, and name is not a private property; you can simply do $vegetable->name or $recipe->name and it will do the same as what these methods are doing.

Recipe.php

As stated in the comments, belongsToMany() is the inverse of belongsToMany(), not hasOne():

public function vegetables(){
  return $this->belongsToMany(Vegetable::class, 'vegetables_recipes');
}

Additionally, if the primary and foreign key names match the model names, you don't need them. The proper name for the pivot table would be recipes_vegetables (plural, alphabetical), so specifying that is required. You can do the same for Vegetable.php:

public function recipes(){
  return $this->belongsToMany(Recipe::class, 'vegetables_recipes');
}

Lastly, your model VegetablesRecipe.php is not needed, and is currently not being used. You typically don't define a Model for a Pivot table, so you can either remove it, or keep it around should you ever need to directly modify the Pivot.

  • Related