Multi Filtering Tags

Hi There, im on the way of creating multi filter on site

And will have about 6-8 tag fields that will be work with bootstrap multiselect
when im selecting few tags of one field they are sending to controller as array

In controller if I create something like this:

if($categories = get('categories')) {
    
    
    if(is_array($categories)) {
	    
	    // fetch children with a date in the past
		
		$reviews = $reviews->filterBy('categories', 'in', $categories);
		
		$categories = implode (", ", $categories); 
		
		
    } 
    else {
	    $categories = explode(", ", $categories);
	    
	    $reviews = $reviews->filterBy('categories', 'in', $categories);
		
		$categories = implode (", ", $categories);
    }
    
}

filter works but it shows only reviews where categories is 100% match (for example if I select category New Reviews, it will shows only reviews where this category only one, if review have two categories New Reviews, Popular Reviews it will not shows in the loop.
implode/explode im using for adding strings to data-categories for ajax loading

If change controller to this
elseif ($categories = get(‘categories’)) {
if(is_array($categories)) {

		    foreach ($categories as $category) {
			    
				$review = $review->listed()->filterBy('categories', '*=', $category)->sortBy('date')->flip();
				
			}
		    
		    
		   
			
			$categories = implode (", ", $categories);
			
		} else {
		    $categories = explode(", ", $categories);
		    
		    foreach ($categories as $category) {
		    	$reviews = $reviews->listed()->filterBy('categories', '*=', $category)->sortBy('date')->flip();
			}
			
			$categories = implode (", ", $categories);
	    } 
  	}

It will start filtering reviews with many categories, but if I select two or more categories for filtering controller gives results only for the last selected category…

and if I try to put this all to filter() results are empty…

if(is_array($categories)) {
			$reviews = $reviews->filter(function($review) use($categories) {
		    foreach ($categories as $category) {
			    
				$review = $review->listed()->filterBy('categories', '*=', $category)->sortBy('date')->flip();
				
			}
		    
		    
		    });

I think i found solution…
but think code can be more pretty)))

elseif ($categories = get('categories')) {
	$collection = pages();
	
	if(is_array($categories)) {
		
		foreach ($categories as $category) {
		  $collection->add($reviews->listed()->filterBy('categories', '*=', $category)->sortBy('date')->flip());
		}
	    
	    $categories = implode (", ", $categories);
		
	} else {
	    $categories = explode(", ", $categories);
	    foreach ($categories as $category) {
	    	$collection->add($reviews->listed()->filterBy('categories', '*=', $category)->sortBy('date')->flip());
		}
		$categories = implode (", ", $categories);
    } 
    
    $reviews = $collection;
}

Instead of filterBy() you could use filter($callback), then you wouldn’t have to loop through all categories and you wouldn’t have the problem you ran into in your first post.

I try to create it with filter($callback) but fail with that, maybe you could help me with that? Based on last code that is works fine right now.

Something like

$reviews = $reviews->filter(function($item) use ($categories) {
  return count(array_intersect($item->categories()->split(','), $categories)) > 0;
});

You get the idea…