In a Laravel 5 Collection how do you return an array of objects instead of an array of arrays?

26,093

Solution 1

Figured out the problem. I was explicitly defining the attributes in the Model. Laravel uses __get() in a particular way which causes passed parameters to be overridden whatever attributes are explicitly defined.

In other words, I was getting null values in the partial because the info I was passing to the partial was overridden.

Solution 2

According to the Docs:

$array = $collection->all();

"The all method returns the underlying array represented by the collection."

Solution 3

Your code is just fine, except you don't need to call toArray on your Eloquent query result. Let me explain what the code does, so you can understand why the following is what you want:

$models = Foo::where('id', '>', 5)->get();
return view('home.index', ['models' => $models]);

The first statememt Foo::where('id', '>', 5)->get(); returns a value of type Illuminate\Support\Collection.

That Collection class holds the collection elements in a protected property called $items (as you could see from your dump protected 'items' =>), which is of type array. The class also implements an interface called IteratorAggregate, which basically means it allows any variable of that type to be iterated using a foreach statement.

In your case this means that, even if $models is of type Illuminate\Support\Collection it will behave as an array when you go over it with foreach:

@foreach ($models as $model)
{
    {{ $model->foo }}
}

So in short Collection is an iterable object that can be treated as an array, but is better than an array because if offers extra methods that allow you to manipulate the items from the collection. You can check the Collection API to see a complete list of available methods.

So in reality you're getting an improved array of models.


Also, don't worry that the properties are not filled, they are in fact filled, I just think you're looking in the wrong place.

If you look closely at your var_dump, you'll see that you have some lines that start with public, protected or private. Those keywords mean that those lines contain object properties. In the case of Laravel Eloquent models, the values fetched from the database are not stored directly in the properties that are named like the database columns. The values are in fact stored in a single property called attributes and are fetched using PHP's magic _get. Take a look at the comments on the code below:

object(Illuminate\Support\Collection)[164]
  protected 'items' => 
    array (size=3)
      0 => 
        object(App\Foo)[172]
          public 'id' => null  // <<< THE VALUES ARE
          public 'foo' => null // <<< NOT STORED HERE
          private 'created_at' => null
          private 'updated_at' => null
          protected 'connection' => null
          protected 'table' => null
          protected 'primaryKey' => string 'id' (length=2)
          protected 'perPage' => int 15
          public 'incrementing' => boolean true
          public 'timestamps' => boolean true
          protected 'attributes' => 
            array (size=4)
              'id' => int 1 // <<< THEY ARE HERE
              'foo' => string 'Foo!' (length=4) // <<< AND HERE
              'created_at' => string '2015-02-27 15:44:09' (length=19)
              'updated_at' => null

Laravel does a lot of trickery behind the scenes to allow you to get things done with only a few lines of code. That's why a var_dump will not always display the simple data structures that you might expect.

Share:
26,093
ebakunin
Author by

ebakunin

Updated on July 09, 2022

Comments

  • ebakunin
    ebakunin almost 2 years

    I am using Laravel 5 and a Blade template. In a view I want to iterate over an array of Model objects, not an array of arrays. If I did want to iterate over an array of arrays I would do the following, which works as expected:

    $models = Foo::where('id', '>', 5)->get();
    return view('home.index', ['models' => $models->toArray()]);
    

    However I want an array of objects with accessible properties. If I were to run:

    $models = Foo::where('id', '>', 5)->get();
    return view('home.index', ['models' => $models->all()]);
    

    The var_dump would look like this:

    object(Illuminate\Support\Collection)[164]
      protected 'items' => 
        array (size=3)
          0 => 
            object(App\Foo)[172]
              public 'id' => null
              public 'foo' => null
              private 'created_at' => null
              private 'updated_at' => null
              protected 'connection' => null
              protected 'table' => null
              protected 'primaryKey' => string 'id' (length=2)
              protected 'perPage' => int 15
              public 'incrementing' => boolean true
              public 'timestamps' => boolean true
              protected 'attributes' => 
                array (size=4)
                  'id' => int 1
                  'foo' => string 'Foo!' (length=4)
                  'created_at' => string '2015-02-27 15:44:09' (length=19)
                  'updated_at' => null
    

    Not only is the Model in an 'items' object the properties are not filled.

    In a view I would like to do something like this:

    @foreach ($models as $model)
        @include('_partial') {
            'id' => $model->id,
            'foo' => $model->foo,
        }
    @endforeach
    

    How do I get an array of Models instead of an array of an array of Models?

  • ebakunin
    ebakunin about 9 years
    The problem here is that the properties return empty values. In other words, $model->id and $model->foo will always return null as shown in the var_dump above.
  • Bogdan
    Bogdan about 9 years
    I'll update my answer in a minute, explaining why that's not the case.
  • ebakunin
    ebakunin about 9 years
    Even if I use $Model->attributes I am still relying on an array. What I want is to always have $model->id, not $model['id'] or $model->attributes['id']. The magic getter does not seem to return the needed values. Maybe I'm fundamentally misunderstanding something.
  • Bogdan
    Bogdan about 9 years
    You can use $model->id. When you write $model->id, the model class determines that you want the value of the column id and knows to find and return it from the attributes array. It does this using the PHP _get method I referenced in the answer. Your code will work as you need it to, because Laravel does its magic behind the scenes.
  • Bogdan
    Bogdan about 9 years
    Also, to pass data to partial views in Blade you need to pass it as a second parameter of type array, like so: @include('_partial', ['id' => $model->id, 'foo' => $model->foo]).
  • Bogdan
    Bogdan about 9 years
    According to the var_dump you posted the values are there, so the code must work. If it doesn't, it may mean your problem lies elsewere in your code.
  • ebakunin
    ebakunin about 9 years
    This discussion is getting long but the "move this discussion to chat" is not working, so I will continue it in an another answer. Admins, please correct this as need be.
  • Bogdan
    Bogdan about 9 years
    Try outputting your the data directly in the @foreach loop (like I wrote in my answer) without using the @include. See if that works. And don't mind the move the discussion message, it's a suggestion not a requirement.
  • Bogdan
    Bogdan about 9 years
  • Nurbol Alpysbayev
    Nurbol Alpysbayev about 6 years
    This is correcct answer to the question. Thanks!
  • Serdar D.
    Serdar D. almost 3 years
    Thanks! @ebakunin please mark this one as correct answer