Framework: [5.4] Attribute set to Collection is not converted to array

Created on 6 May 2017  路  4Comments  路  Source: laravel/framework

  • Laravel Version: 5.4.19
  • PHP Version: 5.6.4
  • Database Driver & Version: Mysql 5.6.17

Description:

Attributes that are collections are not converted to arrays when toArray() method is called on the model.
I discovered this when using collection cast, but it actually applies to all collection attributes.

Steps To Reproduce:

use Illuminate\Database\Eloquent\Model;

class Dummy extends Model {}

$dummy = new Dummy;

$dummy->foo = collect([1, 2, 3]);

dump($dummy->toArray());

Expected results:

array:1 [
  "foo" => array:3 []
]

Actual results:

array:1 [
  "foo" => Collection {#602}
]

Fix:

Adding code below to attributesToArray() method will give expected results.

foreach ($attributes as $key => $value) {
    $attributes[$key] = $value instanceof Arrayable ? $value->toArray() : $value;
}

Conclusion:

I think attributesToArray() method of the model should check if attribute implements Arrayable interface and then convert it to array with toArray() method.

Please correct me if I am wrong and there is actually a reason for current behavior.

Most helpful comment

Thanks for replies!

@Dylan-DPC I want to cast this attribute to collection so casting it to array is kinda counter-productive.

@devcircus I think your code will recurse infinitely - it should be $this->attributes['foo'] instead of $this->foo in both places. Said that it probably solves the issue, however it is almost 10 lines of extra code, with use of functionality that wasn't made for the purpose.

Don't get me wrong, but I didn't add the issue to solve my particular problem, it couldn't be easier after you recognize the cause. I wanted to point out to a behaviour that is probably unexpected by 90% of people especially considering how collections in Laravel do it.

All 4 comments

Have you tried casting it to array?

toArray() is recursive for all attributes and relations, so, one way would be to add 'foo' as an attribute.

class Dummy extends Model
{
    protected $appends = ['foo'];

    public function setFooAttribute($value)
    {
        $this->foo = $value;
    }

    public function getFooAttribute()
    {
        return $this->foo;
    }
}

then toArray() will convert 'foo' to an array as well.

Otherwise, you could:
var_dump(json_decode(json_encode($dummy), true));

Thanks for replies!

@Dylan-DPC I want to cast this attribute to collection so casting it to array is kinda counter-productive.

@devcircus I think your code will recurse infinitely - it should be $this->attributes['foo'] instead of $this->foo in both places. Said that it probably solves the issue, however it is almost 10 lines of extra code, with use of functionality that wasn't made for the purpose.

Don't get me wrong, but I didn't add the issue to solve my particular problem, it couldn't be easier after you recognize the cause. I wanted to point out to a behaviour that is probably unexpected by 90% of people especially considering how collections in Laravel do it.

Feel free to open a PR. Closing since it's not actually a bug

Was this page helpful?
0 / 5 - 0 ratings