Sorting with select form

Hi, I’m trying to sort some articles based on a select field (form) but it doesn’t work yet.

This is the controller:

<?php

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


  // return all children if nothing is selected
  $articles = $page->children()->listed()->flip();


  // if there is a post request, sort the articles collection
  if(r::is('POST') && $data = get()) {

    $articles = page()->children()->listed()->sortBy(function ($data) {
      return $data;
    }, 'desc');

  };
  
  return compact( 'articles');
};

here’s the select field inside the template:

<section class="subnav">
        <form id="filters" method="post">
            <select name="sort" onchange="this.form.submit()">
                <option selected value="">Sort by</option>

                <option<?php e(isset($data['title']) && $data['title'] == $title, ' selected') ?> value="<?= $title ?>">
                    Title</option>

                    <option<?php e(isset($data['year']) && $data['year'] == $year, ' selected') ?> value="<?= $year ?>">
                        Year</option>

            </select>
        </form>
    </section>

There are several issues with your form and controller:

Form:

<section class="subnav">
        <form id="filters" method="post">
            <select name="sort" onchange="this.form.submit()">
                <option selected value="">Sort by</option>

                <option<?php e( isset($sortField) && $sortField === 'title', ' selected') ?> value="title">
                    Title</option>

                    <option<?php e(isset($sortField )&& $sortField === 'year', ' selected') ?> value="year">
                        Year</option>

            </select>
        </form>
    </section>

Controller:

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

  $sortField = null;

  // return all children if nothing is selected
  $articles = $page->children()->listed()->flip();


  // if there is a post request, sort the articles collection
  if(R::is('POST') && get('sort')) {
    $sortField = esc(get('sort'));

    $articles = $articles->sortBy($sortField, 'desc');

  };
  
  return compact( 'articles', 'sortField');
};

If this is not self explanatory, don’t hesitate to ask for explanations. You might want to consider using a GET instead of a POST request to prevent the browser popup when a page with a post request is reloaded.

Additionally, you might want to make sure you only get one of two values:

$sortField = in_array(get('sort'), ['title', 'year']) ? get('sort') : null;

Thanks! It works so far. I’m slowly getting more familiar with it.
Is there a way to include ‘asc’ and ‘desc’ for each value into the same field?

You either need a second form field for the direction or you have to duplicate the options, i.e. title-desc, title-asc etc.

The Duplicate Version seems the best to me, but I’m not sure if I’m on the right track here:

options:

 <option<?php e( isset($sortField) && $sortField === "'title','asc'", ' selected') ?> value="title-asc">
                    Title-asc</option>

controller:

<?php

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

  $sortField = null;

  // return all children if nothing is selected
  $articles = $page->children()->listed()->flip();


  // if there is a post request, sort the articles collection
  if(R::is('GET') && get('sort')) {
    $sortField = esc(get('sort'));

    $articles = $articles->sortBy($sortField);

  };
  
  return compact( 'articles', 'sortField');
};
<section class="subnav">
  <form id="filters" method="GET">
    <select name="sort" onchange="this.form.submit()">
      <option selected value="">Sort by</option>
      <option<?php e(isset($sortString) && $sortString === 'title-asc', ' selected') ?> value="title-asc">Title asc</option>
      <option<?php e(isset($sortString) && $sortString === 'title-desc', ' selected') ?> value="title-desc">Title desc</option>
      <option<?php e(isset($sortString) && $sortString === 'year-asc', ' selected') ?> value="year-asc">Year asc</option>
      <option<?php e(isset($sortString) && $sortString === 'year-desc', ' selected') ?> value="year-desc">Year desc</option>


    </select>
  </form>
</section>
<?php
use Kirby\Cms\R;
use Kirby\Toolkit\Str;
return function($site, $pages, $page) {

  $sortField = null;
  // return all children if nothing is selected
  $articles = $page->children()->listed()->flip();


  // if there is a post request, sort the articles collection
  if(get('sort')) {
    $sortString = get('sort');
    $sortField = Str::split($sortString, '-')[0];
    $direction = Str::split($sortString, '-')[1];
    $sortField = in_array($sortField, ['title', 'year'] ) ? $sortField : null;
    $direction = in_array($direction, ['desc', 'asc'] ) ? $direction : null;
    
  

    $articles =$articles->sortBy($sortField, $direction);

  };
  
  return compact( 'articles', 'sortString');
};

Awesome! First, it gave me an error, so i added I added $sortString = null; into the controller.
Thanks @texnixe !