4

Can You plz guide me how to create the logic that makes those posts sortable either by the first letter of title or numbers?

I want when I click on a "letter" the posts must get updated with the given first letter I want when I click on a "letter" the posts must get updated with the given first letter

If you want to check the original page: https://neverquit.000webhostapp.com/library

2
  • Take a look here for an answer.
    – Charles
    Jun 12, 2017 at 3:47
  • Note: That solution only filters the already queried posts. Which may or may not be what you want. Mar 16, 2018 at 23:20

2 Answers 2

6
+25

I think there's a better approach, just create a custom taxonomy that holds the alphanumeric terms, then assign each post to the correct term.

You can use the save post action to auto assign posts to correct term on post save:

function save_index( $post_id ) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }

    $slug   = 'post';
    $letter = '';

    // only run for posts
    if ( isset( $_POST['post_type'] ) && ( $slug != $_POST['post_type'] ) ) {
        return;
    }

    // Check user capabilities
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    $taxonomy = 'index'; // our custom taxonomy

    if ( isset( $_POST['post_type'] ) ) {

        // Get the title of the post
        $title = strtolower( $_POST['post_title'] );

        // The next few lines remove A, An, or The from the start of the title
        $splitTitle    = explode( ' ', $title );
        $blacklist     = [ 'an ', 'a ', 'the ' ];
        $splitTitle[0] = str_replace( $blacklist, '', strtolower( $splitTitle[0] ) );
        $title         = implode( ' ', $splitTitle );

        // Get the first letter of the title
        $letter = substr( $title, 0, 1 );

        // Set to 0-9 if it's a number
        if ( is_numeric( $letter ) ) {
            $letter = '0-9';
        }

    // set term as first letter of post title, lower case
    wp_set_post_terms( $post_id, $letter, $taxonomy );
    }
}

add_action( 'save_post', 'save_index' );
7
  • need obviously to also register a taxonomy. probably should be a private one. Mar 10, 2018 at 18:00
  • So this will assign posts to a custom taxonomy "index" where terms are like 0-9, A, B, C, etc, correct? Creating the taxonomy is fast but editing all the posts manually isn't possible. Also could this take an array of post types? And how would i call it? i'm guess "/index/A/?post_type=movies" Then there's the ASC issue, i'm not sure how to display them alphabetically, i know i gotta change the order to ASC but how and where i'm not sure. The links (A,B,C) will be placed on the custom post type archive. Mar 10, 2018 at 18:12
  • To assign current posts you can do something like this gist.github.com/alexsancho/045b3cfedf4c6f32874e47ec2fa50499 Mar 10, 2018 at 18:26
  • Not sure what you mean with the ASC issue, you can query your posts by taxonomy and modify the order by ASC or DESC Mar 10, 2018 at 18:35
  • @AlexSancho hi this is working fine, i just want to say there seems to be a bug when using "bulk edit" and adding a different taxonomy (not index) it will delete the index term from those modified posts, i don't udnerstand why it does that. Any idea appreciated. Mar 23, 2018 at 19:13
1

This is a little short and not much detail. I have plans to come back to this in more detail at some point.

However, in the short run, this solution should do exactly what you need. You'll need to expand on it obviously, but it does the basics.

This example assumes the Post Type is mbe-members and the rewrite slug of the Post Type is /member/.

Step 1) Add the Custom Rewrite Rule and Query Variable

add_action( 'init', function () {

    add_rewrite_tag( '%member-filter%', 'members/filter/([a-zA-Z0-9-]{1,3})', 'post_type=mbe-members&filter=' );
    add_permastruct( 'member-filter', '%member-filter%', array( 'with_front' => false ) );

} );

You need to make sure that this is performed before your Post Type has been registered.

You'll also need to make sure you flush your rewrite rules.

Note: For extreme simplicity for now, the remainder of the tasks would be completed in your Post Type Archive theme template file. (I'll make this part better in the future)

Step 2) Determine What Needs to be Filtered

$filter       = get_query_var( 'filter' );
$filter_start = null;
$filter_end   = null;

if ( strpos( $filter, '-' ) !== false ) {

    if ( $pieces = explode( '-', $filter ) ) {

        if ( isset( $pieces[0] ) ) {
            $filter_start = $pieces[0];
        }

        if ( isset( $pieces[1] ) ) {
            $filter_end = $pieces[1];
        }

    }

} else {
    $filter_start = substr( $filter, 0, 1 );
}

This filtering also allows a range of letters or numbers.

A/K/A -- You can specify: /members/filter/a or /members/filter/a-z/

Step 3) Query Post IDs for Filtered Matches

global $wpdb;

$post_ids = array();

if ( ! is_null( $filter_start ) && ! is_null( $filter_end ) ) {

    $range = range( $filter_start, $filter_end );

    foreach ( $range as $value ) {

        $query_args = array(
            'SELECT' => 'ID',
            'FROM'   => $wpdb->posts,
            'WHERE'  => 'post_title'
        );

        if ( $query = $wpdb->get_results( $wpdb->prepare( "
            SELECT {$query_args['SELECT']}
            FROM {$query_args['FROM']}
            WHERE {$query_args['WHERE']}
            LIKE %s
            AND post_type = %s
            ", "{$value}%", 'mbe-members' ) ) ) {

            foreach ( $query as $result ) {

                if ( ! in_array( $result->ID, $post_ids ) ) {
                    $post_ids[] = $result->ID;
                }

            }

        }

    }

} else {

    if ( ! is_null( $filter_start ) ) {

        $value = $filter_start;

        $query_args = array(
            'SELECT' => 'ID',
            'FROM'   => $wpdb->posts,
            'WHERE'  => 'post_title',
        );

        if ( is_numeric( $value ) ) {
            $query_args['LIKE'] = '%d';
        } else {
            $query_args['LIKE'] = '%s';
        }

        if ( $query = $wpdb->get_results( $wpdb->prepare( "
            SELECT {$query_args['SELECT']}
            FROM {$query_args['FROM']}
            WHERE {$query_args['WHERE']}
            LIKE {$query_args['LIKE']}
            AND post_type = %s
            ", "{$value}%", "mbe-members" ) ) ) {

            foreach ( $query as $result ) {

                if ( ! in_array( $result->ID, $post_ids ) ) {
                    $post_ids[] = $result->ID;
                }

            }

        }

    }

}

Step 4) Query the Post Objects and Output the Display

$content = '';

if ( ! empty( $post_ids ) ) {

    $query = new \WP_Query( array(
        'post_type'              => 'mbe-members',
        'posts_per_page'         => absint( get_option( 'posts_per_page' ) ),
        'post_status'            => 'publish',
        'orderby'                => 'post_title',
        'order'                  => 'ASC',
        'update_post_meta_cache' => false,
        'update_post_term_cache' => false,
        'suppress_filters'       => true,
        'post__in'               => $post_ids
    ) );

    if ( $query->have_posts() ) {

        foreach ( $query->posts as $post ) {
            $content .= $post->post_title . '<br />';
        }

    }

}

echo '<div>' . PHP_EOL;

echo '<ul class="nav nav-tabs" role="tablist">' . PHP_EOL;

$class = '';

if ( $filter == '0-9' ) {
    $class = 'active';
}

echo '<li role="presentation" class="' . $class . '"><a href="' . site_url( '/member/filter/0-9/' ) . '" aria-controls="0-9" role="tab">0-9</a></li>' . PHP_EOL;

foreach ( range( 'a', 'z' ) as $letter ) {

    $class = '';

    if ( $letter == $filter_start ) {
        $class = 'active';
    }

    echo '<li role="presentation" class="' . $class . '"><a href="' . site_url( '/member/filter/' . $letter . '/' ) . '" aria-controls="' . $letter . '" role="tab">' . $letter . '</a></li>' . PHP_EOL;

}

echo '</ul>' . PHP_EOL;

echo '<div class="tab-content">' . PHP_EOL;

$class = '';

if ( $filter == '0-9' ) {
    $class = 'active';
}

echo '<div role="tabpanel" class="tab-pane ' . $class . '" id="0-9">' . PHP_EOL;

if ( $filter == '0-9' ) {
    echo $content;
}

echo '</div>' . PHP_EOL;

foreach ( range( 'a', 'z' ) as $letter ) {

    $class = '';

    if ( $letter == $filter_start ) {
        $class = 'active';
    }

    echo '<div role="tabpanel" class="tab-pane ' . $class . '" id="' . $letter . '">' . PHP_EOL;

    if ( $letter == $filter_start ) {
        echo $content;
    }

    echo '</div>' . PHP_EOL;

}

echo '</div>' . PHP_EOL;

echo '</div>' . PHP_EOL;

Conclusion

Like I said, this was a very quick answer. Which does indeed answer your question. It's very rough and simple, but it does what you need. I'll try to improve this answer to be a bit more of an elegant solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.