Live filtering: refactor or rebuild?

I have a form that uses various filters to query a set of pages. This is all done via a controller (structurePluck is a custom method):

<?php

return function ($page) {
  
  //get Used Filters
  $query        = get('q');
  $topPlace     = get('topPlace');
  $type         = get('type');
  $ucategories  = get('categories');
  $utags        = get('tags');

  //Get All Places for Filter Values
  $places = $page->children()->listed()->flip()
    ->when($type, function ($type) {
      if($type != 'all'){
        $return = $this->filterBy('type', $type);
      } else {
        $return = $this;
      }
      return $return;
  });

  //Get All Filter Values
  $categories = $places->pluck('category',',',true);
  $tags = $places->pluck('tags',',',true);
  $districts = $places->structurePluck('locations', 'district');
  $kiez = $places->structurePluck('locations', 'kiez');

  //Apply Used Filters
  $aplaces = $page->children()->listed()->flip()
    ->when($query, function ($query) {
        return $this->search($query, 'title|category|tags|locations');
    })
    ->when($topPlace, function ($topPlace) {
      return $this->filterBy('topPlace', true);
    })
    ->when($type, function ($type) {
        if($type != 'all'){
          $return = $this->filterBy('type', $type);
        } else {
          $return = $this;
        }
        return $return;
    })
    ->when($ucategories, function ($ucategories) {
      return $this->filterBy('category', 'in', $ucategories, ',');
    })
    ->when($utags, function ($utags) {
        return $this->filterBy('tags', 'in', $utags, ',');
    });

  //Get Available Filter Values
  $acategories = $aplaces->pluck('category',',',true);
  $atags = $aplaces->pluck('tags',',',true);
  $adistricts = $aplaces->structurePluck('locations', 'district');
  $akiez = $aplaces->structurePluck('locations', 'kiez');

  return [
    //results
    'aplaces' => $aplaces,
    //All Filter Values
    'categories' => $categories,
    'tags' => $tags,
    'districts' => $districts,
    'kiez' => $kiez,
    //Used Filters
    'query' => $query,
    'topPlace' => $topPlace,
    'type' => $type,
    'ucategories' => $ucategories,
    'utags' => $utags,
    //Available Filters
    'acategories' => $acategories,
    'atags' => $atags,
    'adistricts' => $adistricts,
    'akiez' => $akiez,
  ];

};

ā€˜All Filter Valuesā€™ are exactly that: all the available filter options. ā€˜Used Filtersā€™ are filters that have been set in the frontend and passed via URL parameters. ā€˜Available Filtersā€™ are all the possible filters plucked from the filtered results (if that makes sense).

The form works great and I am able to show/hide, enabled/disable relevant fields based on what the user has already selected. However, ideally Iā€™d like to have this function as a ā€˜live searchā€™. Iā€™ve crudely mocked up this behaviour by refreshing the page with JS on a form change event. Is it possible to reuse any of the existing logic (with the addition of routes), or would I need to rebuild the whole thing using a JSON content representation and deal with all the filtering via JS?

If you use Ajax for filtering, you can reuse your logic in a content representation.

The alternative would be something purely client side based.

Trying to get my head around how this should work and finding it quite tricky. Are there any examples of this kind of functionality I could look at? Iā€™ve dealt with content representations and AJAX before (Load More with AJAX). Do all of the variables need to be in the content representation somehow, or dealt with purely by Javascript?

As I said, you can do this purely client side with a JS library like https://listjs.com/.

Or set it up like you would normally, but using Ajax and content represenations to replace only the relevant part of the template. The controller would basically stay the same.

Iā€™m building out the JSON content representation but running into some problems. Iā€™d like to return the URL of the first image from a page, but it doesnā€™t seem to be working like this:

<?php

$places = $page->children()->listed()->flip();
$json = [];

foreach($places as $place) {

  $json[] = [
    'url'         => (string)$place->url(),
    'title'       => (string)$place->title(),
    'image'       => function($place){
                        $image = $place->images()->first();
                        return ($image)? $image->url() : null;
                     },
  ];

}

echo json_encode($json);

This is returning ā€œimageā€:{} even when one exists. Is there there something obvious that Iā€™m missing here?

Also, how would I got about passing data from a structure field to JSON?

'image'       => ($image = $place->images()->first() ) ? $image->url() : null,