Efficiently sort only certain categories by custom field

I would suggest to hook onto pre_get_posts and alter the query, rather than firing off your own in the template (it keeps your template tidy, and you’ll avoid doubling your database queries!).

Below demonstrates this, plus a bonus settings field in the admin (under “Reading”) to select the categories that “special sorting” applies to – see how easy the settings API really is!

/**
 * Intercept the query & attach our own parameters if the conditions are met.
 */
function wpse_42457_custom_category_order( $wp_query )
{
    if ( ! $wp_query->is_category() ) // save proceeding processing
        return;

    $special_categories = wp_parse_id_list( get_option( 'sorted_categories' ) );
    if ( $wp_query->is_category( $special_categories ) ) {

        // We've got a winner - set our special params.
        $wp_query->set( 'meta_key', 'programnumber' );
        $wp_query->set( 'orderby', 'meta_value' );
        $wp_query->set( 'order', 'ASC' );
    }
}
add_action( 'pre_get_posts', 'wpse_42457_custom_category_order' );

/**
 * Register the sorted categories option & the settings field.
 */
function wpse_42457_admin_init()
{
    add_settings_field( 'sorted_categories', 'Special Categories', 'wpse_42457_setting_field', 'reading', 'default' );
    register_setting( 'reading', 'sorted_categories', 'wpse_42457_setting_sanitize' );
}
add_action( 'admin_init', 'wpse_42457_admin_init' );

/**
 * Sanitize our checked categories by turning back to a comma-delimited string.
 *
 * This'll save bytes in the options table, plus it can be "unserialized" more
 * efficiently with wp_parse_id_list() when it's actually needed.
 */
function wpse_42457_setting_sanitize()
{
    // wp_terms_checklist uses "post_category" POST name.
    if ( isset( $_POST['post_category'] ) )
        $value = $_POST['post_category'];
    else
        $value = array(); // none checked

    return implode( ',', wp_parse_id_list( $value ) );
}

/**
 * Display the sorted categories field.
 */
function wpse_42457_setting_field()
{
    // Please forgive me for this dirty HTML!
    ?>

<style>#sorted_categories li li { margin: 0 0 0 15px }</style>
<ul id="sorted_categories">
    <?php
        wp_terms_checklist( 0, array(
            'selected_cats' => wp_parse_id_list( get_option( 'sorted_categories' ) ),
            'checked_ontop' => false,
            'taxonomy' => 'category'
        ));
    ?>
</ul>

    <?php
}

Whilst I always feel like I should encourage others to take steps for themselves, this proved easier to code up than walk through – hopefully it’ll be educational nontheless!


You’ll need to drop this in a custom plugin, or your theme’s functions.php.