Two select fields for filtering

Hello
I originally asked a question about single item filtering here :- Call to a member function title() on string, which worked perfectly.
The client has now asked for two field filtering and my idea of using alpine.js for filtering will no longer work.
I am struggling to find/implement a solution, what I have so far is :-

fields:
  eventCategories:
    label: Categories that events can be filtered by.
    type: tags
    sort: asc
  eventMonths:
    label: Months that events can be filtered by.
    type: tags
    sort: asc
  events:
    type: structure
    label: Upcoming Events
    sortBy: startdate desc
    fields:
      category:
        label: Type of Event
        type: select
        sort: asc
        options:
          type: query
          query: page.eventCategories.split
      month:
        label: Month Event Takes Place
        type: select
        options:
          type: query
          query: page.eventMonths.split

My filter form is :-

<form id="filters" class="filter--block" method="GET">
  <div>
  <p class="is--meta">Choose a type of Event:</p>
  <select name="event" onchange="this.form.submit()">
    <option value="">-</option>
    <?php foreach($page->eventCategories()->split() as $item): ?>
      <?php if(!$item) continue ?>
      <option<?php e(isset($data['catFilter']) && $data['catFilter'] == $item, ' selected') ?> value="<?= Str::lower($item) ?>"><?= html($item) ?></option>
    <?php endforeach ?>
  </select>
  </div>
  <div>
  <p class="is--meta">Select a Month</p>
  <select name="month" onchange="this.form.submit()">
    <option value="">-</option>
    <?php foreach($page->eventMonths()->split() as $item): ?>
      <?php if(!$item) continue ?>
      <option<?php e(isset($data['monthFilter']) && $data['monthFilter'] == $item, ' selected') ?> value="<?= Str::lower($item) ?>"><?= html($item) ?></option>
    <?php endforeach ?>
  </select>
  </div>
</form>

And my controller is :-

<?php

return function($page) {

  $unfiltered   = collection('events');
  $catFilter    = param('category');
  $monthFilter  = param('month');

  $eventCategories = $unfiltered->pluck('eventCategories', ',', true);
  $eventMonths = $unfiltered->pluck('eventMonths', ',', true);

  // filter conditionally
  $unfiltered = $unfiltered
  ->when($catFilter, function ($catFilter) {
    return $this->filterBy('eventCategories', $catFilter);
  })
  ->when($monthFilter, function ($monthFilter) {
      return $this->filterBy('eventMonths', $monthFilter);
  });

  return [
    'catFilter'       => $catFilter,
    'monthFilter'     => $monthFilter,
    'eventCategories' => $eventCategories,
    'eventMonths'     => $eventMonths,
    'events'
  ];
};

When I attempt to use the select my URL changes but only one select is available at a time ie.

http://localhost:8888/events?event=knitting&month=

or

http://localhost:8888/events?event=&month=april

And when I look at the source code, no option in the select is marked as selected.

Could you offer some guidance/ a solution please?

Use get() instead of param()

Thank you, I now have :-

<?php

return function($page) {

  $unfiltered   = collection('events');
  $catFilter    = get('category');
  $monthFilter  = get('month');

  $eventCategories = $unfiltered->pluck('eventCategories', ',', true);
  $eventMonths = $unfiltered->pluck('eventMonths', ',', true);

  // filter conditionally
  $unfiltered = $unfiltered
  ->when($catFilter, function ($catFilter) {
    return $this->filterBy('eventCategories', $catFilter);
  })
  ->when($monthFilter, function ($monthFilter) {
      return $this->filterBy('eventMonths', $monthFilter);
  });

  return [
    'catFilter'       => $catFilter,
    'monthFilter'     => $monthFilter,
    'eventCategories' => $eventCategories,
    'eventMonths'     => $eventMonths,
    'events'
  ];
};

This appears to make no difference when using the selects, I still get the same URL, i.e only one or the other not both.

Your fields are called month and event, but you use get('category')

On a side note, this piece of code is superfluous.

I’m still trying to figure this out, so I’m trying to sort and tidy my code, I have started with the collection.php which is this:-

<?php

return function ($site) {
  return $site->find('events')
  ->toStructure()
  ->children()
  ->listed()
  ->filter(function ($child) {return $child->startdate()->toDate() > time() && $child->enddate()->toDate() < time();})
  ->flip();
};

My controller is this :-1:

<?php

return function($page) {

  $unfiltered   = kirby()->collection('events');
  $catFilter    = get('category');
  $monthFilter  = get('month');

  $eventCategories = $unfiltered->pluck('eventCategories', ',', true);
  $eventMonths = $unfiltered->pluck('eventMonths', ',', true);

  // filter conditionally
  $unfiltered = $unfiltered
  ->when($catFilter, function ($catFilter) {
    return $this->filterBy('eventCategories', $catFilter);
  })
  ->when($monthFilter, function ($monthFilter) {
      return $this->filterBy('eventMonths', $monthFilter);
  });

  return [
    'unfiltered'      => $unfiltered,
    'catFilter'       => $catFilter,
    'monthFilter'     => $monthFilter,
    'eventCategories' => $eventCategories,
    'eventMonths'     => $eventMonths
  ];
};

And then I’m just dumping this in the page template, but I get this returned:-

Kirby\Cms\Field Object
(
    [tostructure] => 
)

I’m assuming the collection is not being formed, could you offer some guidance please?

I’m getting closer, but still cannot get this working.
I can display the collection now by using these collection and controller files:-

<?php

return function ($site) {
  return $site->find('events')
  ->events()
  ->toStructure()
  ->filter(function ($child) {
    return $child->startdate()->toDate() > time() && $child->enddate()->toDate() < time();
  })
  ->flip();
};

and

<?php

return function() {

  $unfiltered         = kirby()->collection('events');
  $categoryFilters    = get('category');
  $monthFilters       = get('month');
  $eventCategories    = $unfiltered->pluck('eventCategories', ',', true);
  $eventMonths        = $unfiltered->pluck('eventMonths', ',', true);

  // filter conditionally
  $unfiltered = $unfiltered
  ->when($categoryFilters, function ($categoryFilters) {
    return $this->filterBy('eventCategories', $categoryFilters);
  })
  ->when($monthFilters, function ($monthFilters) {
      return $this->filterBy('eventMonths', $monthFilters);
  });

  $eventCategories    = $unfiltered->pluck('eventCategories', ',', true);
  $eventMonths        = $unfiltered->pluck('eventMonths', ',', true);

  return [
    'unfiltered'        => $unfiltered,
    'categoryFilters'   => $categoryFilters,
    'monthFilters'      => $monthFilters,
    'eventCategories'   => $eventCategories,
    'eventMonths'       => $eventMonths
  ];
};

I then have this as my two selects for the filtering

<form id="filters" class="filter--block" method="GET">
  <div>
    <p class="is--meta no--margin">Select a type of Event:</p>
    <ul class="filter--list">
      <li><a href="<?= $page->url() ?>">-</a></li>
      <?php foreach($page->eventCategories()->split() as $filter): ?>
        <li <?= $filter === $categoryFilters ? 'class="active"' : ''?>><a href="<?= $page->url() ?>?category=<?= $filter ?>"><?= $filter ?></a></li>
      <?php endforeach ?>
    </ul>
  </div>
  <div>
    <p class="is--meta no--margin">Select a Month:</p>
    <ul class="filter--list">
      <li><a href="<?= $page->url() ?>">-</a></li>
      <?php foreach($page->eventMonths()->split() as $item): ?>
        <li <?= $item === $monthFilters ? 'class="active"' : ''?>><a href="<?= $page->url() ?>?month=<?= $item ?>"><?= $item ?></a></li>
      <?php endforeach ?>
    </ul>
  </div>
</form>

With this setup I can generate a url like this:

http://localhost:8888/events?category=Knitting&month=April

But no events display even if they meet the url criteria
To try and figure this out(it’s only my second Kirby site) I have used this dump

  <?php var_dump($unfiltered); ?>
  <?php var_dump($eventCategories); ?>
  <?php var_dump($eventMonths); ?>
  <?php var_dump($categoryFilters); ?>
  <?php var_dump($monthFilters); ?>

On loading the page with no selects used I see

object(Kirby\Cms\Structure)#350 (8) { [0]=> int(7) [1]=> int(6) [2]=> int(5) [3]=> int(4) [4]=> int(3) [5]=> int(2) [6]=> int(1) [7]=> int(0) } array(0) { } array(0) { } NULL NULL

but as soon as I use a select I see

object(Kirby\Cms\Structure)#404 (0) { } array(0) { } array(0) { } string(8) "Knitting" string(3) "All"

Which I think means the data from the toStructure is not available, is this the correct assumption and if so how can I correct this please?

Assuming that your fields contains a comma separated list of items, you are missing the separator here as argument.

Thank you for your patience, above are the fields used.
Do you mean like this?

  $unfiltered = $unfiltered
  ->when($categoryFilters, function ($categoryFilters) {
    return $this->filterBy('eventCategories', ',', $categoryFilters);
  })
  ->when($monthFilters, function ($monthFilters) {
      return $this->filterBy('eventMonths', ',', $monthFilters);
  });

If so that makes no difference. But looking at my previous reply, I see that both arrays for

  <?php var_dump($eventCategories); ?>
  <?php var_dump($eventMonths); ?>

have a value of (0) is this correct?

Naa, the separator needs to be the last argument.

return $this->filterBy('eventMonths', $monthFilters, ',');

But the problem is something else. You want to filter the structure items, but as field name to filter by, you use eventCategories and eventMonths, while the fields inside your structure are called category and month, so this cannot possibly work.

I’m struggling to find the solution here, (my lack of php & Kirby knowledge).
I have looked at Filtering compendium | Kirby CMS and through the forum but I cannot get this working, All I have so far is this

  $unfiltered = $unfiltered
  ->when($categoryFilters, function ($page, $categoryFilters) {
    return $this->$page->events()->toStructure()->filterBy('category', $categoryFilters, ',');
  })
  ->when($monthFilters, function ($page, $monthFilters) {
      return $this->$page->events()->toStructure()->filterBy('month', $monthFilters, ',');
  });

Could you offer a solution please?

Look, you just need to replace the field name here with month and the other one with category, i.e. the field names you have in your structure field, not the fields outside of your structure.

return $this->filterBy('month', $monthFilters, ',');

Not change the whole logic.