Skip to content

Instantly share code, notes, and snippets.

@xnau
Last active June 27, 2025 23:58
Show Gist options
  • Select an option

  • Save xnau/506c16a56da3967a61bc0c1d235757d8 to your computer and use it in GitHub Desktop.

Select an option

Save xnau/506c16a56da3967a61bc0c1d235757d8 to your computer and use it in GitHub Desktop.
Shows how to set up a click-to-sort header for a Participants Database list table template
<?php
/**
* @version 1.1
*
* template for participants list shortcode output
*
* this demonstrates how to add a click-to-sort header to the list
*
*/
add_filter('pdb-list_header_title', 'xnau_list_column_sort_link', 10, 3 );
if ( !function_exists( 'xnau_list_column_sort_link' ) ) :
/**
*
* @param string $title
* @param PDb_Form_Field_Def $field
* @param array $filter the current list filter
* @return string title
*/
function xnau_list_column_sort_link( $title, $field, $filter )
{
if ( !$field->is_sortable() )
{
return $title;
}
parse_str( $_SERVER['QUERY_STRING'], $get_array );
global $post;
$base_url = get_permalink( $post->ID );
$ascdesc = 'ASC';
if ( isset($get_array['sortBy']) && $field->name() === $get_array['sortBy'] && isset( $get_array['ascdesc'] ) )
{
// we're getting the opposite of the submitted sort direction
$ascdesc = stripos( $get_array['ascdesc'], 'ASC' ) !== false ? 'DESC' : 'ASC';
}
$arg_array = array(
'sortBy' => $field->name(),
'ascdesc' => $ascdesc,
);
if ( xnau_get_input_value( 'search_field' ) )
{
$arg_array = array_merge( $arg_array,
array(
'search_field' => xnau_get_input_value( 'search_field' ) ? : 'searchfield',
'value' => xnau_get_input_value( 'value' ) ? : '*',
) );
}
$title = sprintf( '<a href="%1$s">%2$s%3$s</a>', add_query_arg( $arg_array, $base_url ), $title, xnau_sort_icon( $ascdesc ) );
return $title;
}
/**
* provides the sort icon
*
* @param string $ascdesc
* @return string icon HTML
*/
function xnau_sort_icon( $ascdesc )
{
return sprintf( '<span class="sort-arrow-%s"></span>', ( $ascdesc === 'ASC' ? 'down' : 'up' ) );
}
/**
* supplies the requested value from the input
*
* @param string $name of the value
* @return the value or bool false if not found
*/
function xnau_get_input_value( $name )
{
$inputval = filter_input( INPUT_POST, $name, FILTER_DEFAULT, array('flags' => FILTER_NULL_ON_FAILURE ) );
if ( $inputval === false )
{
$inputval = filter_input( INPUT_GET, $name, FILTER_DEFAULT, array('flags' => FILTER_NULL_ON_FAILURE ) );
}
return $inputval;
}
endif; // function check
?>
<div class="wrap <?php esc_attr_e( $this->wrap_class ) ?>" id="<?php esc_attr_e( $this->list_anchor ) ?>">
<?php
/*
* SEARCH/SORT FORM
*
* the search/sort form is only presented when enabled in the shortcode.
*
*/
$this->show_search_sort_form();
/* LIST DISPLAY */
/*
* NOTE: the container for the list itself (excluding search and pagination
* controls) must have a class of "list-container" for AJAX search/sort to
* function
*/
?>
<style>
.sort-arrow-up::after {
content: '\25B2';
}
.sort-arrow-down::after {
content: '\25BC';
}
</style>
<table class="wp-list-table widefat fixed pages list-container" >
<?php if ( has_action( 'pdb-prepend_to_list_container_content' ) ) : ?>
<caption>
<?php do_action( 'pdb-prepend_to_list_container_content' ) ?>
<?php $this->print_list_count( '<div class="%s"><span class="list-display-count">' ) ?>
</caption>
<?php else : ?>
<?php
/* print the count if enabled in the shortcode
*
* the tag wrapping the count statment can be supplied in the function argument, example here
*/
$this->print_list_count( '<caption class="%s" ><span class="list-display-count">' );
?>
<?php endif ?>
<?php if ( $record_count > 0 ) : // print only if there are records to show ?>
<thead>
<tr>
<?php
/*
* this function prints headers for all the fields
* replacement codes:
* %2$s is the form element type identifier
* %1$s is the title of the field
*/
$this->print_header_row( '<th class="%2$s" scope="col">%1$s</th>' );
?>
</tr>
</thead>
<tbody>
<?php while ( $this->have_records() ) : $this->the_record(); // each record is one row ?>
<tr>
<?php while ( $this->have_fields() ) : $this->the_field(); // each field is one cell ?>
<td class="<?php esc_attr_e( $this->field->name() ) ?>-field">
<?php $this->field->print_value() ?>
</td>
<?php endwhile; // each field ?>
</tr>
<?php endwhile; // each record ?>
</tbody>
<?php else : // if there are no records ?>
<tbody>
<tr>
<td><?php if ( $this->is_search_result ) echo wp_kses_post( Participants_Db::plugin_setting('no_records_message') ) ?></td>
</tr>
</tbody>
<?php endif; // $record_count > 0 ?>
</table>
<?php
/*
* this shortcut function presents a pagination control with default layout
*/
$this->show_pagination_control();
?>
</div>
@xnau
Copy link
Author

xnau commented Apr 19, 2023

Place this template in the Participants Database template location:

wp_content/participants-database-templates/pdb-list-sort-columns.php

Then call it in the shortcode like this:

[pdb_list search=true template=sort-columns]

This only works for fields that are maked as "sortable" so you should check the field definitions.

@scohoe
Copy link

scohoe commented Jun 27, 2025

was having an issue editing pages with this updated shortcode on them, the front end worked but I couldn't get to the backend and would get this error

[27-Jun-2025 18:28:25 UTC] PHP Fatal error: Cannot redeclare xnau_list_column_sort_link() (previously declared in /home/archule2/public_html/wp-content/participants-database-templates/pdb-list-sort-columns.php:21) in /home/archule2/public_html/wp-content/participants-database-templates/pdb-list-sort-columns.php on line 21

was able to get it working again with this.

<?php
/**
 * @version 1.0
 * 
 * template for participants list shortcode output
 *
 * this demonstrates how to add a click-to-sort header to the list
 *
 */

// add the filter handler that places the sorting links on the header titles
if (!function_exists('xnau_list_column_sort_link')) {
  add_filter('pdb-list_header_title', 'xnau_list_column_sort_link', 10, 3 );
}

 /**
   *  
   * @param string $title
   * @param PDb_Form_Field_Def $field
   * @param array $filter the current list filter
   * @return string title
   */
  if (!function_exists('xnau_list_column_sort_link')) {
  function xnau_list_column_sort_link( $title, $field, $filter )
  {
    if ( !$field->is_sortable() )
    {
      return $title;
    }
    
    // place the request values into the $get_array variable
    parse_str( $_SERVER['QUERY_STRING'], $get_array );
    
    // get the link back to the list
    global $post;
    $base_url = get_permalink( $post->ID );
    
    $ascdesc = 'ASC';
    if ( isset($get_array['sortBy']) && $field->name() === $get_array['sortBy'] && isset( $get_array['ascdesc'] ) )
    {
      // we're getting the opposite of the submitted sort direction
      $ascdesc = stripos( $get_array['ascdesc'], 'ASC' ) !== false ? 'DESC' : 'ASC';
    }
    
    $arg_array = array(
        'search_field' => xnau_get_input_value( 'search_field' ) ? : 'searchfield',
        'value' => xnau_get_input_value( 'value' ) ? : '*',
        'sortBy' => $field->name(),
        'ascdesc' => $ascdesc,
    );
    
    $title = sprintf( '<a href="%1$s">%2$s%3$s</a>', add_query_arg( $arg_array, $base_url ), $title, xnau_sort_icon( $ascdesc ) );
    
    return $title;
  }
  }
  
  /**
   * provides the sort icon
   * 
   * @param string $ascdesc
   * @return string icon HTML
   */
  if (!function_exists('xnau_sort_icon')) {
  function xnau_sort_icon( $ascdesc )
  {
    return sprintf( '<span class="sort-arrow-%s"></span>', ( $ascdesc === 'ASC' ? 'down' : 'up' ) );
  }
  }
  
  /**
   * supplies the requested value from the input
   * 
   * @param string $name of the value
   * @return the value or bool false if not found
   */
  if (!function_exists('xnau_get_input_value')) {
  function xnau_get_input_value( $name )
  {
    $inputval = filter_input( INPUT_POST, $name, FILTER_DEFAULT, array('flags' => FILTER_NULL_ON_FAILURE ) );
    
    if ( $inputval === false )
    {
      $inputval = filter_input( INPUT_GET, $name, FILTER_DEFAULT, array('flags' => FILTER_NULL_ON_FAILURE ) );
    }
    
    return $inputval;
  }
  }

?>
<div class="wrap <?php esc_attr_e( $this->wrap_class ) ?>" id="<?php esc_attr_e( $this->list_anchor ) ?>">
  <?php
  /*
   * SEARCH/SORT FORM
   *
   * the search/sort form is only presented when enabled in the shortcode.
   *
   */
  $this->show_search_sort_form();

  /* LIST DISPLAY */
  /*
   * NOTE: the container for the list itself (excluding search and pagination 
   * controls) must have a class of "list-container" for AJAX search/sort to 
   * function
   */
  ?>
  
  <style>
    .sort-arrow-up::after {
      content: '\25B2';
    }
    .sort-arrow-down::after {
      content: '\25BC';
    }
  </style>
  <table class="wp-list-table widefat fixed pages list-container" >

    <?php if ( has_action( 'pdb-prepend_to_list_container_content' ) ) : ?>
      <caption>
        <?php do_action( 'pdb-prepend_to_list_container_content' ) ?>
        <?php $this->print_list_count( '<div class="%s"><span class="list-display-count">' ) ?>
      </caption>
    <?php else : ?>
      <?php
      /* print the count if enabled in the shortcode
       * 
       * the tag wrapping the count statment can be supplied in the function argument, example here
       */
      $this->print_list_count( '<caption class="%s" ><span class="list-display-count">' );
      ?>
    <?php endif ?>

    <?php if ( $record_count > 0 ) : // print only if there are records to show ?>

      <thead>
        <tr>
          
          <?php
          /*
           * this function prints headers for all the fields
           * replacement codes:
           * %2$s is the form element type identifier
           * %1$s is the title of the field
           */
          $this->print_header_row( '<th class="%2$s" scope="col">%1$s</th>' );
          ?>
        </tr>
      </thead>

      <tbody>
          <?php while ( $this->have_records() ) : $this->the_record(); // each record is one row  ?>
          <tr>
    <?php while ( $this->have_fields() ) : $this->the_field(); // each field is one cell  ?>

              <td class="<?php esc_attr_e( $this->field->name() ) ?>-field">
                
      <?php $this->field->print_value() ?>
              </td>

          <?php endwhile; // each field  ?>
          </tr>
  <?php endwhile; // each record  ?>
      </tbody>

<?php else : // if there are no records  ?>

      <tbody>
        <tr>
          <td><?php if ( $this->is_search_result ) echo wp_kses_post( Participants_Db::plugin_setting('no_records_message') ) ?></td>
        </tr>
      </tbody>

<?php endif; // $record_count > 0  ?>

  </table>
  <?php
  /*
   * this shortcut function presents a pagination control with default layout
   */
  $this->show_pagination_control();
  ?>
</div>
<?php

@xnau
Copy link
Author

xnau commented Jun 27, 2025

It is worth mentioning that this template is no longer needed, the header sorting was integrated into the main plugin with the use of the "header_sort" attribute:

[pdb_list header_sort=true]

@scohoe
Copy link

scohoe commented Jun 27, 2025

It is worth mentioning that this template is no longer needed, the header sorting was integrated into the main plugin with the use of the "header_sort" attribute:

[pdb_list header_sort=true]

🤦 Thank you, I can't believe I missed that in the docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment