Filtering Content on a page

Hello,

iam searching for a way to filter content on a page, so for example iam working
on a job portal and have a list of fifty jobs, but just wanna display all jobs
in a specific city by selecting from a dropdown menu.

Is there a way that works with pagination? For me it does´t matter
if its a javascript solution or php, also i thought about to use
the search as filter. Do this makes sense?

Thank you for any help.

See this cookbook recipe: https://getkirby.com/docs/cookbook/filtering#using-filters-with-forms-e-g-select-fields. Of course, you can paginate the resulting results like any other page collections.

i see that i need a controller script for it, can i have two controller files for one template?
Do i have to set something up in my blueprint to test the script?

No, you can’t have two controllers. But why would you need them?

The script as is requires certain fields (color, brand, size) to be present in your pages. But of course, you can just adapt it to filter by the fields present in your content files.

Ok…no i don´t need them. I thought i had a form on the page, but its on the landing page,
where people can demand on a job.

Your script looks like what i need, but its hard for me to understand.
What i have to do to set it up?

Is… $item = $page->children()->color() for example?

All you need is the form with the select fields in your template where you display your jobs, and the controller code in the controller for that template. Of course, you have to adapt the fields used in the controller/form to fit your filter criteria.

Can you explain me where items come from?

<?php foreach($color as $item): ?>

Do have to define item?

All variable like $color,$brandand$sizes` are defined in the controller. You don’t have to define $item as it get’s defined in the foreach loop. Please read about using foreach loop in PHP if you don’t know how they work. You need a basic understanding of PHP to be able to do this, we can’t offer that sort of support here.

Yes i understand loops, but don´t understand the script…so what i have changed:

<form id="filters" method="post">
  <!-- give the select the name of the category to filter by -->
  <select name="ort" onchange="this.form.submit()">
    <option selected value="">Select a color</option>

    <!-- let's fill the options with our colors -->
    <?php foreach($ort as $item): ?>
      <!-- we don't want empty items -->
      <?php if(!$item) continue ?>

      <!-- set the option to selected if selected -->
      <option<?php e(isset($data['ort']) && $data['ort'] == $item, ' selected') ?> value="<?php echo $item ?>"><?php echo $item?></option>
    <?php endforeach ?>
  </select>
</form>

i replaced color with ort because i have a field ort in my blueprint. And this is my controller:

<?php

return function($site, $pages, $page) {

  $ort = $page->children()->pluck('ort', null, true);
  $keys = array('ort');

  // return all children if nothing is selected
  $projects = $page->children()->visible();

  // if there is a post request, filter the projects collection
  if(r::is('POST') && $data = get()) {
    $projects = $page->children()->visible()->filter(function($child) use($keys, $data) {

      // loop through the post request
      foreach($data as $key => $value) {

        // only act if the value is not empty and the key is valid
        if($value && in_array($key, $keys)) {

          // return false if the child page's category and value don't match
          if(!$match = $child->$key() == $value) {
            return false;
          }
        }
      }

      // otherwise return the child page
      return $child;

    });
  }
  return compact('products', 'ort', 'data');
};


but the selector is empty

Ok…seems like the fields have to be filled all with info to work. now i see the value in my selector, but the filter does´t work :confused:

here is my content i wanna filter:


			<div class="row small_pad">
				<?php foreach($nom_list as $proj):?>
				<div class=" col-sm-12 col-md-6 col-lg-6  wow fadeIn"  data-wow-delay="0.5s" data-wow-duration="1.5s">
				<div class="teaser_box">
				<h2><span class="white_t"><?php echo $proj->title() ?></span></h2>	
				<p>Einsatzort:  <span class="subtitle"><?php echo $proj->ort() ?></span></p>
				<p><?php echo excerpt($proj->beschreibung(), 100) ?></p>
				<p><a class="link_b" href="<?php echo $proj->url() ?>">JETZT ANSEHEN<span class="arrow_icon"></span></a></p>
				</div>
				</div>
			<?php endforeach ?>	
			</div>	

You can use the following syntax to fetch the values from all pages instead even if some are empty:

$ort = $page->children()->pluck('ort', ',', true);

Where is $nom_list defined? This is what you have to filter in your controller!

its defined in my template on the top:

$nom_list = $page->children()->flip()->visible()->paginate(6);

And where do you filter the list? Because all I see in the controller is projects, not $nom_list.

Mmmh…Ok: So i replace it in my controller:

  // return all children if nothing is selected
  $nom_list = $page->children()->visible();

  // if there is a post request, filter the projects collection
  if(r::is('POST') && $data = get()) {
    $nom_list = $page->children()->visible()->filter(function($child) use($keys, $data) {

anything else i have to change, does´t work for me?

This my controller:

<?php

return function($site, $pages, $page) {

  $ort = $page->children()->pluck('ort', ',', true);
  $keys = array('ort');

  // return all children if nothing is selected
  $nom_list = $page->children()->visible();

  // if there is a post request, filter the projects collection
  if(r::is('POST') && $data = get()) {
    $nom_list = $page->children()->visible()->filter(function($child) use($keys, $data) {

      // loop through the post request
      foreach($data as $key => $value) {

        // only act if the value is not empty and the key is valid
        if($value && in_array($key, $keys)) {

          // return false if the child page's category and value don't match
          if(!$match = $child->$key() == $value) {
            return false;
          }
        }
      }

      // otherwise return the child page
      return $child;

    });
  }
  return compact('products', 'ort', 'data');
};

and this is my template:


<?php
$nom_list = $page->children()->flip()->visible()->paginate(6);
$pagination = $nom_list->pagination();
?>

<?php snippet('header') ?>
<div class="grey_bg">
	<div class="container small_pad  min_h">
		<div class="row"><div class="trenner"></div></div>	
		


<form id="filters" method="post">
  <!-- give the select the name of the category to filter by -->
  <select name="ort" onchange="this.form.submit()">
    <option selected value="">Select a color</option>

    <!-- let's fill the options with our colors -->
    <?php foreach($ort as $item): ?>
      <!-- we don't want empty items -->
      <?php if(!$item) continue ?>

      <!-- set the option to selected if selected -->
      <option<?php e(isset($data['ort']) && $data['ort'] == $item, ' selected') ?> value="<?php echo $item ?>"><?php echo $item?></option>
    <?php endforeach ?>
  </select>
</form>
				
			
			<div class="row small_pad">
				<?php foreach($nom_list as $proj):?>
				<div class=" col-sm-12 col-md-6 col-lg-6  wow fadeIn"  data-wow-delay="0.5s" data-wow-duration="1.5s">
				<div class="teaser_box">
				<h2><span class="white_t"><?php echo $proj->title() ?></span></h2>	
				<p>Einsatzort:  <span class="subtitle"><?php echo $proj->ort() ?></span></p>
				<p><?php echo excerpt($proj->beschreibung(), 100) ?></p>
				<p><a class="link_b" href="<?php echo $proj->url() ?>">JETZT ANSEHEN<span class="arrow_icon"></span></a></p>
				</div>
				</div>
			<?php endforeach ?>	
			</div>	


i don´t understand why this does´t work :confused:

This does not work for two reasons:

  • you do not return $nom_list from your controller
  • instead you define $nom_list in the template again.

Thank you so far, but iam really confused with this script so i updated my template and deleted the nom_list definition and add the controller to this:

<?php

return function($site, $pages, $page) {

  $ort = $page->children()->pluck('ort', ',', true);
  $keys = array('ort');

  // return all children if nothing is selected
  $nom_list = $page->children()->flip()->visible()->paginate(6);

  // if there is a post request, filter the projects collection
  if(r::is('POST') && $data = get()) {
    $nom_list = $page->children()->visible()->filter(function($child) use($keys, $data) {

      // loop through the post request
      foreach($data as $key => $value) {

        // only act if the value is not empty and the key is valid
        if($value && in_array($key, $keys)) {

          // return false if the child page's category and value don't match
          if(!$match = $child->$key() == $value) {
            return false;
          }
        }
      }

      // otherwise return the child page
      return $child;

    });
  }
  return compact('nom_list', 'ort', 'data');
};

But nothing happens, also $child confuse me, where this come from? It always showing all entries, so it seems that the filtering does´t work

$child is just a variable that is used to refer to a single element of the collection inside the filter callback function.

What does your template look like now?

This is my whole template, maybe the pagination on top can be delete:


<?php

$pagination = $nom_list->pagination();
?>

<?php snippet('header') ?>
<div class="grey_bg">
	<div class="container small_pad  min_h">
		<div class="row"><div class="trenner"></div></div>	
		


<form id="filters" method="post">
  <select name="ort" onchange="this.form.submit()">
    <option selected value="">Select a city</option>


    <?php foreach($ort as $item): ?>
      <?php if(!$item) continue ?>
      <!-- set the option to selected if selected -->
      <option<?php e(isset($data['ort']) && $data['ort'] == $item, ' selected') ?> value="<?php echo $item ?>"><?php echo $item?></option>
    <?php endforeach ?>
  </select>
</form>							
			<div class="row small_pad">
				<?php foreach($nom_list as $proj):?>
				<div class=" col-sm-12 col-md-6 col-lg-6  wow fadeIn"  data-wow-delay="0.5s" data-wow-duration="1.5s">
				<div class="teaser_box">
				<h2><span class="white_t"><?php echo $proj->title() ?></span></h2>	
				<p>Einsatzort:  <span class="subtitle"><?php echo $proj->ort() ?></span></p>
				<p><?php echo excerpt($proj->beschreibung(), 100) ?></p>
				<p><a class="link_b" href="<?php echo $proj->url() ?>">JETZT ANSEHEN<span class="arrow_icon"></span></a></p>
				</div>
				</div>
			<?php endforeach ?>	
			</div>	
		
			<div class="row text-left small_pad">
			<div class="col-xs-12  col-lg-12">	
<?php if($nom_list->pagination()->hasPages()): ?>
<nav class="pagination">

  <?php if($nom_list->pagination()->hasNextPage()): ?>
  <a class="next" href="<?php echo $nom_list->pagination()->nextPageURL() ?>">&lsaquo; weitere Jobs</a>
  <?php endif ?>
	


  <?php if($nom_list->pagination()->hasPrevPage()): ?>
  <a class="prev" href="<?php echo $nom_list->pagination()->prevPageURL() ?>">vorherige Jobs  &rsaquo;</a>
  <?php endif ?>

</nav>
<?php endif ?>
				
			</div>	
			</div>
			
			
	</div>
</div>
<?php snippet('footer') ?>