Checking that element appears after another in a collection

Viewed 146

I have a collection returned from the database through a hasMany-relation.

$items = collect([
    [
        'ref' => 'efaef954',
        'children' => collect([
            [
                'ref' => 'wetk4',
                'order' => 1,
            ],
            [
                'ref' => 'wetk5',
                'order' => 2,
            ],
            [
                'ref' => 'wetk6',
                'order' => 3,
            ],
        ]),
    ],
    [
        'ref' => 'efgjlf954',
        'children' => collect([
            [
                'ref' => 'wetk5',
                'order' => 1,
            ],
            [
                'ref' => 'wetk6',
                'order' => 2,
            ],
            [
                'ref' => 'wetk4',
                'order' => 3,
            ],
        ]),
    ],
]);

This $items-collection can contain any number of items with children. These children has a specific order, which is random until they are sorted.

In my user interface I can input refs of the children to get parent items that qualify. Only parent-items which have the specific order of what I input e.g. wetk4 and wetk6 should be returned.

return $items->filter(fn ($item) => $item->children->contains('wetk4') && $item->children->contains('wetk6'))

This will correctly give me items with the children, but this does not take into account the fact that the order is important in which items would qualify, considering some items will have the same children.

I've yet to find an elegant solution to this, attempting to only use collection functions alone.

2 Answers

I would probably use a macro here in conjunction with other collect functions. There is a mysql function called FIELD, which sounds similar to this. We can write a macro called FIELD.

use Illuminate\Support\Collection;

AppServiceProvider()
{
    public function boot()
    {
        Collection::macro('field', function ($key, array $order) {
            $ordering = collect();

            foreach($order as $searchValue) {
                $ordering = $ordering->concat($this->where($key,$searchValue));
            }

            $ordering = $ordering->concat($this->whereNotIn($key,$order));

            return $ordering;
        });
    }
}

From there you can use this macro to filter your results.

$ordering = ['wetk4','wetk5'];

return $items->filter(function($item) use($ordering){
    return count($item['children']->pluck('ref')->diffAssoc($item['children']->field('ref',$ordering)->pluck('ref'))) == 0;
});
Related