24

I have a multidimensional collection. I want to iterate it and alter some of its child objects and arrays using the map() function: https://laravel.com/docs/5.1/collections#method-map

Sample content:

[
    {
        'address': 'Somestreet 99'
        'orders': [
            {'id': 11},
            {'id': 67}
        ]
    }
]

Example

  $deliveries = $delivery_addresses->map(function($delivery_address){
     $orders = $delivery_address->orders->filter(function($order){
        return $order->id == 67;
     });

     $delivery_address['address'] = 'A different street 44'
     $delivery_address['orders'] = $orders;
     $delivery_address['a_new_attribute'] = 'Some data...';

     return $delivery_address;
  });

Expected result:

[
    {
        'address': 'A different street 44'
        'orders': [
            {'id': 67}
        ],
        'a_new_attribute': 'Some data...;
    }
]

The result is that only string type variables will be changed. Any arrays or objects will stay the same. Why is this and how to get around it? Thanks! =)

MikaelL
  • 364
  • 1
  • 3
  • 14
  • Do you have an example collection that would give the result you're describing? Modification of all kinds of data from within map should give desired results as elements of the collection are just overwritten by what you return from your callback. – jedrzej.kurylo Jan 15 '16 at 09:29
  • Could you also post the real code that you're executing, not some pseudocode? $old_collection.map won't work in PHP – jedrzej.kurylo Jan 15 '16 at 09:30
  • Could you also post a desired result, and what you are actually getting? I am also curious why you are accessing $delivery_address as an object, then assigning a key as an array later. – jardis Jan 15 '16 at 09:45
  • @Emn1ty Laravel treats them the same. – Sturm Jan 15 '16 at 09:56
  • @Sturm -- Well, while I didn't know that I also find that a huge can of worms that should never have been opened. :P Shouldn't let people treat objects like arrays, ever. But that's a topic for another day, thanks for the info. – jardis Jan 15 '16 at 09:58

4 Answers4

38
collect($deliver_addresses)->map(function ($address) use ($input) {

    $address['id']              = $input['id'];
    $address['a_new_attribute'] = $input['a_new_attribute'];

    return $address;

});
Mayuri Pansuriya
  • 934
  • 6
  • 13
  • 1
    @Al0x this will add id and a_new_attribute these two attributes to your object – Mayuri Pansuriya Mar 13 '18 at 11:49
  • You are Awesome, I was attemting the same but was missing the use($param) bit so was not able to access data outside the map function. – Arnaud Bouchot Mar 14 '18 at 15:16
  • 1
    @MayuriPansuriya upvoted but it is always best to include some explanation with an answer instead of just posting code – Gharbad The Weak Sep 26 '19 at 20:28
  • 4
    Note that map does not modify original collection but it returns the modified collection, so you have to assign result to a variable else the code above has no effect. – KoviNET Mar 25 '22 at 09:35
7

Addressing your recent edits, try this:

$deliveries = $deliver_addresses->map(function($da) {
    $orders = $da->orders->filter(function($order) {
        return $order->id == 67;
    });

    $da->unused_attribute = $orders->all();

    return $da;
});

What the case most likely is here is that you are correctly overwriting that attribute. Then when you are attempting to access it Laravel is querying the orders() relationship and undoing your changes. As far as Laravel is concerned these are the same:

$delivery_address->orders;
$delivery_address['orders'];

This is why the changes are only working on objects. If you want to save that permanently then actually save it, if not use a temporary attribute to contain that data.

Sturm
  • 4,105
  • 3
  • 24
  • 39
  • 1
    That sounds plausible. It's true that I can add new attributes to the collection. – MikaelL Jan 15 '16 at 09:57
  • Since don't want to save any data, just print it as JSON, I'm thinking of converting the collection to an array and then work with whats available - like foreach()... – MikaelL Jan 15 '16 at 10:22
2

$paymentMethods = $user->paymentMethods()->map(function($paymentMethod){
            return $paymentMethod->asStripePaymentMethod();
        });
1

Eloquent collections has a put method (since v5.1), that can be used to add a field to a collection while keeping the 'pipe-style' chaining. It can also be used with the new arrow functions syntax:

    $deliveries = $delivery_addresses
        ->map(fn ($delivery_address) => collect($delivery_address)
            ->put('orders', Orders::where('delivery_addresses_id', '=', $delivery_address->id))
            ->toArray()
        );
HynekS
  • 2,738
  • 1
  • 19
  • 34