The very simplest way to do it is to add a filter (in your theme’s “functions.php”) to sort by the content:
function wpse181797_posts_orderby( $orderby, $query ) {
if ( ! is_admin() && $query->get( 'post_type' ) == 'post' /* && some other conditions probably... */ ) {
global $wpdb;
$pc="LTRIM(" . $wpdb->posts . '.post_content)';
$orderby = 'UPPER(' . $pc . ') ASC';
}
return $orderby;
}
add_filter( 'posts_orderby', 'wpse181797_posts_orderby', 10, 2 );
However this won’t deal with any initial punctuation or HTML markup or verse numbering/titling/epigraphs or whatever, so you’re probably better off using a secondary field as well for those cases, eg post_excerpt
if it’s available, where you put the first line as you want it to sort in exceptionable cases, so the filter becomes:
function wpse181797_posts_orderby_excerpt( $orderby, $query ) {
if ( ! is_admin() && $query->get( 'post_type' ) == 'post' /* && some other conditions probably... */ ) {
global $wpdb;
$pc="LTRIM(CONCAT(" . $wpdb->posts . '.post_excerpt, ' . $wpdb->posts . '.post_content))';
$orderby = 'UPPER(' . $pc . ') ASC';
}
return $orderby;
}
add_filter( 'posts_orderby', 'wpse181797_posts_orderby_excerpt', 10, 2 );
If you’re using the post_excerpt
for other purposes you could use a custom field instead, eg 'first_line_sort'
, and then filter on posts_clauses
(to add a join):
function wpse181797_posts_clauses( $pieces, $query ) {
if ( ! is_admin() && $query->get( 'post_type' ) == 'post' /* && some other conditions probably... */ ) {
global $wpdb;
$pieces[ 'join' ] .= $wpdb->prepare(
' LEFT JOIN ' . $wpdb->postmeta . ' wpse_pm ON wpse_pm.post_id = ' . $wpdb->posts . '.ID'
. ' AND wpse_pm.meta_key = %s'
, 'first_line_sort'
);
$pieces[ 'orderby' ] = 'UPPER(LTRIM(IFNULL(wpse_pm.meta_value, ' . $wpdb->posts . '.post_content))) ASC';
}
return $pieces;
}
add_filter( 'posts_clauses', 'wpse181797_posts_clauses', 10, 2 );