Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

How to update some data inside a collection ?

Hello,

I have this code to reorder a collection according to its new order.

public function reorder($ids)
{
    $this->steps = collect($ids)->map(function ($id) {
        return collect($this->steps)->where('order', (int) $id)->first();
    })->each(function ($item, $key) {
        $item['order'] = $key + 1;
    });
    dd($this->steps);
}

My dd shows me that $item['order'] is not changed, but inside the each loop, it's changed.

Is it possible to change the value of some data inside a collection ?

How to do that ?

Thank you ;).

V

0 likes
18 replies
Sinnbeck's avatar

Try this instead

$this->steps = collect($ids)->map(function ($id) {
        return collect($this->steps)->where('order', (int) $id)->first();
    })->map(function ($item, $key) {
        $item['order'] = $key + 1;
        return $item;
    });
    dd($this->steps); 
vincent15000's avatar

@Sinnbeck Here it is ;).

$this->steps = $this->recipe->steps->sortBy('order')->toArray();

array:4 [▼
  0 => array:6 [▼
    "id" => 197
    "order" => 1
    "description" => "Faire cuire les pâtes dans de l'eau salée."
    "recipe_id" => 11
    "created_at" => "2022-02-15T16:13:10.000000Z"
    "updated_at" => "2022-02-15T16:23:27.000000Z"
  ]
  1 => array:6 [▼
    "id" => 198
    "order" => 2
    "description" => "Egoutter les pâtes et les faire revenir dans un peu d'huile."
    "recipe_id" => 11
    "created_at" => "2022-02-15T16:13:10.000000Z"
    "updated_at" => "2022-02-15T16:23:27.000000Z"
  ]
  2 => array:6 [▶]
  3 => array:6 [▶]
kokoshneta's avatar

So… you create a property steps in the current object, which you assign to be a collection of $ids (presumably an array or iterable of sorts). That’s collection #1.

Then you create a new collection, #2, by mapping each $id in collection #1 to a function. For each element in collection #2, the function returns a new collection, #3, from whatever is in $this->steps, and then you create yet another collection, #4, by filtering collection #3, and then you retrieve the first element in collection #4 and use that as the function return value for insertion into collection #2.

Then you create collection #5 by iterating over collection #2 and updating each item’s order key to be the element’s key + 1.

Even after writing all that out, I still cannot figure out what your code is supposed to do, but I’m 99% certain you’re going about it in a very roundabout and over-complicated way. Why do you need to create five levels of collections for this? What is it you’re actually trying to do? What is “its new order”?

vincent15000's avatar

@kokoshneta

Here are some explanations inside the code (only 3 collections at all ;)).

$this->steps = collect($ids)->map(function ($id) { // 1rst collection in order to manipulate the array with the helpers
        return collect($this->steps)->where('order', (int) $id)->first(); // to order the array in the new order
    })->map(function ($item, $key) {
        $item['order'] = $key + 1; // to change the order property in the array
        return $item;
    }); // soon => ->toArray() to retrieve the modified array
    dd($this->steps); 
kokoshneta's avatar

@vincent15000 map() returns a new collection, and so does where() – so it is five levels (for a total of 2n+3 collections, so if $ids has ten elements, you’re generating 23 collections in all). Splitting it out in pseudo-code (and ignoring scopes and such things), this is what it does:

$c1 = collect($ids);
$c2 = $c1->map(func_1($id));

function func_1($id) {
	$c3 = collect($this->steps);
	$c4 = $c3->where('order', (int) $id);
	return $c4->first();
}

$c5 = $c2->each(func_2($item, $key));

function func_2($item, $key) {
	$item['order'] = $key++;
	return $item;
}

But more importantly, you still haven’t explained what you’re trying to do. What is the logic that you want to rearrange by? How are the items supported to be ordered? It sort of looks like you’re just trying to sort $this->steps by each step’s $order property, but I can’t see how your code would do that.

Also, it might help to know what $this is. What class/model are you defining this function in? It seems to be an entity that has a recipe model, but also has a steps property (just like the recipe model does)…

vincent15000's avatar

@kokoshneta $thisis to access a public property in a Livewire component. $this->steps is nothing else than a simple PHP array.

Perhaps I should have explained that I have seen the tutorial to do drag and drop with Livewire.

https://laravel-livewire.com/screencasts/s8-dragging-list

What I'm trying to do is, after reordering by drag and drop, having the right value for the order field for each step.

  • ordering by drag and drop
  • passing the new orders in the PHP method
  • resetting the order field for each step according to the new orders

Sure it shoud be a better way to do that.

webrobert's avatar
Level 51

@vincent15000,

is the update response like this??

[
    [
        'order' => 1,   // order of item (starts at 1, not 0)
        'value' => 20,  // item id
    ],
]

I use a draggable 'inspired by' the livewire version... https://github.com/nextapps-be/livewire-sortablejs

I think the format is the same.

why bother sorting the items??

Here is my update function (when the drop is complete)

   public function updateItemOrder($items)
    {
        if (! $this->activeList ) return;

        collect($items)->each( function($item) {
			// update your items here
            $this->activeList->items()->updateExistingPivot( $item['value'], [
                'order' => $item['order'],
            ]);
        });
    }
vincent15000's avatar

@webrobert Thank you ... well ... it's a list in which I can add more elements and I let the possibility to cancel the sorting. The sorting is only validated if the user clicks on the save button.

webrobert's avatar

@vincent15000 then I guess the same applies. It’s already being sort on the front end so why are sending it back to sort it again?

vincent15000's avatar

@webrobert That's the first time I do sorting with drag and drop and I have followed the tutorial on livewire website. It sends the ids to sort in the back because on the front it is already sorted but not in the back. Well ... sure I haven't thought ... I have only copied the code ;). I will try something with your code and I tell you if it's ok for me.

vincent15000's avatar

@webrobert Hello ... what you suggest is much easier than what I tried to do.

I wanted to submit the form only once as all steps are added. But instead of doing that, I update the database for each sort action and I have added a form below the sortable list and I add dynamically each new step to the database.

1 like
MichalOravec's avatar
$iteration = [1, 2];
$steps = [
    ['order' => 1],
    ['order' => 2]
];

Array index starts with 0, that's why you increment it by 1.

Try to think about your code and you find out that the result of your code is the same data inside $steps what you had there before.

vincent15000's avatar

@MichalOravec Here are some details.

Just after reordering.

array:4 [▼
  0 => "2"
  1 => "1"
  2 => "3"
  3 => "4"
]

So I have the orders : 2, 1, 3, 4 ... and for each step (2, 1, 3, 4), I need to replace only the order with 1, 2, 3, 4, what I do with $key + 1.

2 becomes $key (0) + 1 = 1
1 becomes $key (1) + 1 = 2
...

I don't understand where it's wrong and how I can obtain the same result as the original orders.

But sure something is wrong, otherwise it would work ;).

MichalOravec's avatar
$iteration = [2, 1, 3];

$steps = [
    ['name' => 'A','order' => 1],
    ['name' => 'B', 'order' => 2],
    ['name' => 'C', 'order' => 3]
];

$result = collect($steps)->sortBy(function ($step) use ($iteration) {
    return array_search($step['order'], $iteration);
})->values()->toArray();

And the result is

[
    ['name' => 'B', 'order' => 2],
    ['name' => 'A', 'order' => 1],
    ['name' => 'C', 'order' => 3]
]

So your code could look like this

public function reorder($iteration)
{
    $this->steps = collect($steps)->sortBy(function ($step) use ($iteration) {
        return array_search($step['order'], $iteration);
    })->values()->toArray();
}

or with pure php

public function reorder($iteration)
{ 
    usort($this->steps, function ($step) use ($iteration) {
        return array_search($step['order'], $iteration);
    });
}

with arrow functions (since PHP 7.4)

public function reorder($iteration)
{ 
    usort($this->steps, fn ($step) => array_search($step['order'], $iteration));
}

Please or to participate in this conversation.