2

I have a model has an attribute which is cast to an array, like so

protected $casts = [
   'data' => 'array',
];

I need to make an amendment to the array before returning the Collection. Using the each method on the Collection I can make changes to the attributes inside.

$collection = $collection->each(function ($collection, $key) {
    if ($collection->type == 'foo') {
       $collection->type = 'bar';
    }
});

This works and the Collection is altered. However I need to change the array in the cast attribute.

$collection = $collection->each(function ($collection, $key) {
    if ($collection->type == 'foo') {

        foreach ($collection->data['x'] as $k => $v) {
            $collection->data['x'][$k]['string'] = 'example';
        }

    }
});

However this returns an error.

Indirect modification of overloaded property App\Models\Block::$data has no effect

I understand that accessing $collection->data will be using a magic __get() is being used, so I would need to use a setter. So how do I achieve this?

Thanks in advance.

DrKHunter
  • 424
  • 5
  • 15

1 Answers1

1

Presumably you can take the whole array, perform your modifications and then set it:

$collection = $collection->each(function ($collectionItem, $key) {
    if ($collectionItem->type == 'foo') {
        $data = $collectionItem->data;
        foreach ($data['x'] as $k => $v) {
            $data['x'][$k]['string'] = 'example';
        }
        $collectionItem->data = $data;

    }
});

Though if this modification is required for all uses of the model, perhaps it would be better to do this in the model its self:

class SomeModel
{


    //protected $casts = [
    //   'data' => 'array',
    //];

    public function getDataAttribute($value)
    {
        $data = json_decode($value);
        foreach ($data['x'] as $k => $v) {
                $data['x'][$k]['string'] = 'example';
        }
        return $data;
    }

    public function setDataAttribute($value)
    {
        $this->attributes['data'] = json_encode($value);
    }

}
Steve
  • 20,703
  • 5
  • 41
  • 67
  • Thank you, that worked. I had tried something similar but at that point another part of the code was wrong. – DrKHunter May 25 '16 at 11:49
  • How would you go about adding this to the model? There is more logic around the actual data than this example. – DrKHunter May 25 '16 at 11:50
  • 1
    Well instead of using the `$casts` array to let laravel handle the cast, you can use a getter (1Attribute accessor in laravel speak) to handle the cast and any other logic: https://laravel.com/docs/5.1/eloquent-mutators – Steve May 25 '16 at 11:56
  • Thanks. I would then also need to make sure that I encoded to json when setting data. Unless this could also be achieved in the model? – DrKHunter May 25 '16 at 12:05
  • @DrKHunter Yes, that can be done in the model with a setter (attribute mutator in docs), see edit. – Steve May 25 '16 at 12:10
  • Although this does lead to the obvious question of why you are not saving the required data in the 1st place. Perhaps you should perform the replacement in the setter (which will be called a lot less frequently under normal circumstances) – Steve May 25 '16 at 12:12
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/112923/discussion-between-drkhunter-and-steve). – DrKHunter May 25 '16 at 13:22