Bỏ qua các bài viết ban đầu (như 'a', 'an' hoặc 'the') khi sắp xếp truy vấn?


13

Tôi hiện đang cố gắng đưa ra một danh sách các tiêu đề âm nhạc và muốn bỏ qua việc sắp xếp (nhưng vẫn hiển thị) bài viết ban đầu của tiêu đề.

Ví dụ: nếu tôi có một danh sách các ban nhạc, nó sẽ được hiển thị theo thứ tự abc trong WordPress như thế này:

  • Ngày sa-bát đen
  • Led Zeppelin
  • Hồng Floyd
  • Ban nhạc The Beatles
  • Nút thắt
  • Những hòn đá lăn
  • Lizzy mỏng

Thay vào đó, tôi muốn nó được hiển thị theo thứ tự abc trong khi bỏ qua bài viết ban đầu 'The', như thế này:

  • Ban nhạc The Beatles
  • Ngày sa-bát đen
  • Nút thắt
  • Led Zeppelin
  • Hồng Floyd
  • Những hòn đá lăn
  • Lizzy mỏng

Tôi đã bắt gặp một giải pháp trong một mục blog từ năm ngoái , điều đó cho thấy đoạn mã sau trong functions.php:

function wpcf_create_temp_column($fields) {
  global $wpdb;
  $matches = 'The';
  $has_the = " CASE 
      WHEN $wpdb->posts.post_title regexp( '^($matches)[[:space:]]' )
        THEN trim(substr($wpdb->posts.post_title from 4)) 
      ELSE $wpdb->posts.post_title 
        END AS title2";
  if ($has_the) {
    $fields .= ( preg_match( '/^(\s+)?,/', $has_the ) ) ? $has_the : ", $has_the";
  }
  return $fields;
}

function wpcf_sort_by_temp_column ($orderby) {
  $custom_orderby = " UPPER(title2) ASC";
  if ($custom_orderby) {
    $orderby = $custom_orderby;
  }
  return $orderby;
}

và sau đó gói truy vấn với add_filtertrước và remove_filtersau.

Tôi đã thử điều này, nhưng tôi tiếp tục gặp lỗi sau trên trang web của mình:

Lỗi cơ sở dữ liệu WordPress: [Cột không xác định 'title2' trong 'mệnh đề thứ tự']

CHỌN wp_posts. * TỪ wp_posts WHERE 1 = 1 VÀ wp_posts.post_type = 'phát hành' AND (wp_posts.post_status = 'xuất bản' HOẶC wp_posts.post_status = 'private') ORDER BY UP

Tôi sẽ không nói dối, tôi khá mới với phần php của WordPress, vì vậy tôi không chắc tại sao tôi lại gặp lỗi này. Tôi có thể thấy nó có liên quan đến cột 'title2', nhưng tôi hiểu rằng chức năng đầu tiên sẽ đảm nhiệm việc đó. Ngoài ra, nếu có một cách thông minh hơn để làm điều này thì tôi đều nghe thấy. Tôi đã đi vòng quanh và tìm kiếm trang web này, nhưng tôi thực sự không tìm thấy nhiều giải pháp.

Mã của tôi sử dụng các bộ lọc trông như thế này nếu có bất kỳ trợ giúp nào:

<?php 
    $args_post = array('post_type' => 'release', 'orderby' => 'title', 'order' => 'ASC', 'posts_per_page' => -1, );

    add_filter('post_fields', 'wpcf_create_temp_column'); /* remove initial 'The' from post titles */
    add_filter('posts_orderby', 'wpcf_sort_by_temp_column');

    $loop = new WP_Query($args_post);

    remove_filter('post_fields', 'wpcf_create_temp_column');
    remove_filter('posts_orderby', 'wpcf_sort_by_temp_column');

        while ($loop->have_posts() ) : $loop->the_post();
?>

1
một giải pháp thay thế có thể là lưu trữ tiêu đề bạn muốn sắp xếp theo dữ liệu meta và đặt hàng trên trường đó thay vì tiêu đề.
Milo

Tôi không chắc chắn về cách tiếp tục với điều đó. Sẽ không lưu trữ rằng trong một cột mới dẫn đến một lỗi tương tự như tôi đang gặp phải?
rpbtz

1
bạn sẽ không sử dụng bất kỳ mã nào trong số đó, bạn có thể truy vấn và sắp xếp trên meta bài đăng với các tham số truy vấn meta .
Milo

Câu trả lời:


8

Vấn đề

Tôi nghĩ rằng có một lỗi đánh máy trong đó:

Tên của bộ lọc là posts_fieldskhông post_fields.

Điều đó có thể giải thích tại sao title2trường không xác định, vì định nghĩa của nó không được thêm vào chuỗi SQL được tạo.

Thay thế - Bộ lọc đơn

Chúng tôi có thể viết lại nó để chỉ sử dụng một bộ lọc duy nhất:

add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
    // Do nothing
    if( '_custom' !== $q->get( 'orderby' ) )
        return $orderby;

    global $wpdb;

    $matches = 'The';   // REGEXP is not case sensitive here

    // Custom ordering (SQL)
    return sprintf( 
        " 
        CASE 
            WHEN {$wpdb->posts}.post_title REGEXP( '^($matches)[[:space:]]+' )
                THEN TRIM( SUBSTR( {$wpdb->posts}.post_title FROM %d )) 
            ELSE {$wpdb->posts}.post_title 
        END %s
        ",
        strlen( $matches ) + 1,
        'ASC' === strtoupper( $q->get( 'order' ) ) ? 'ASC' : 'DESC'     
    );

}, 10, 2 );

nơi bạn có thể kích hoạt thứ tự tùy chỉnh với _customtham số orderby:

$args_post = array
    'post_type'      => 'release', 
    'orderby'        => '_custom',    // Activate the custom ordering 
    'order'          => 'ASC', 
    'posts_per_page' => -1, 
);

$loop = new WP_Query($args_post);

while ($loop->have_posts() ) : $loop->the_post();

Thay thế - đệ quy TRIM()

Hãy thực hiện ý tưởng đệ quy của Pascal Birchler , nhận xét ở đây :

add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
    if( '_custom' !== $q->get( 'orderby' ) )
        return $orderby;

    global $wpdb;

    // Adjust this to your needs:
    $matches = [ 'the ', 'an ', 'a ' ];

    return sprintf( 
        " %s %s ",
        wpse_sql( $matches, " LOWER( {$wpdb->posts}.post_title) " ),
        'ASC' === strtoupper( $q->get( 'order' ) ) ? 'ASC' : 'DESC'     
    );

}, 10, 2 );

ví dụ chúng ta có thể xây dựng hàm đệ quy như:

function wpse_sql( &$matches, $sql )
{
    if( empty( $matches ) || ! is_array( $matches ) )
        return $sql;

    $sql = sprintf( " TRIM( LEADING '%s' FROM ( %s ) ) ", $matches[0], $sql );
    array_shift( $matches );    
    return wpse_sql( $matches, $sql );
}

Điều này có nghĩa rằng

$matches = [ 'the ', 'an ', 'a ' ];
echo wpse_sql( $matches, " LOWER( {$wpdb->posts}.post_title) " );

sẽ tạo ra:

TRIM( LEADING 'a ' FROM ( 
    TRIM( LEADING 'an ' FROM ( 
        TRIM( LEADING 'the ' FROM ( 
            LOWER( wp_posts.post_title) 
        ) )
    ) )
) )

Thay thế - MariaDB

Nói chung tôi thích sử dụng MariaDB thay vì MySQL . Sau đó, nó dễ dàng hơn nhiều vì MariaDB 10.0.5 hỗ trợ REGEXP_REPLACE :

/**
 * Ignore (the,an,a) in post title ordering
 *
 * @uses MariaDB 10.0.5+
 */
add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
    if( '_custom' !== $q->get( 'orderby' ) )
        return $orderby;

    global $wpdb;
    return sprintf( 
        " REGEXP_REPLACE( {$wpdb->posts}.post_title, '^(the|a|an)[[:space:]]+', '' ) %s",
        'ASC' === strtoupper( $q->get( 'order' ) ) ? 'ASC' : 'DESC'     
    );
}, 10, 2 );

Tôi nghĩ rằng điều này sẽ giải quyết vấn đề tốt hơn giải pháp của tôi
Pieter Goosen 7/2/2016

Bạn đã hoàn toàn chính xác - việc thay đổi post_fields thành post_fields đã khắc phục vấn đề và giờ đây nó đang sắp xếp chính xác theo cách tôi muốn. Cảm ơn bạn! Tôi cảm thấy hơi ngu ngốc khi thấy đó là vấn đề. Đó là những gì tôi nhận được cho mã hóa lúc 4 giờ sáng, đoán. Tôi cũng sẽ xem xét giải pháp bộ lọc duy nhất. Có vẻ như một ý tưởng thực sự tốt. Cảm ơn một lần nữa.
rpbtz

Tôi sẽ đánh dấu đây là câu trả lời đúng vì đây là câu trả lời gần nhất với câu hỏi ban đầu của tôi, mặc dù theo như tôi có thể nói các câu trả lời khác cũng là giải pháp hợp lệ.
rpbtz

Sự thay thế bộ lọc duy nhất làm việc như một sự quyến rũ là tốt. Bây giờ tôi có thể giữ mã bộ lọc functions.phpvà gọi nó qua orderbykhi tôi cần. Giải pháp tuyệt vời - cảm ơn bạn :-)
rpbtz

1
Rất vui khi biết nó hiệu quả với bạn - Tôi đã thêm phương pháp đệ quy. @rpbtz
birgire 7/2/2016

12

Một cách dễ dàng hơn có thể là đi qua và thay đổi sên permalink trên những bài đăng cần nó (dưới tiêu đề trên màn hình viết bài) và sau đó chỉ sử dụng nó để đặt hàng thay vì tiêu đề.

I E. post_namekhông sử dụng post_titleđể sắp xếp ...

Điều này cũng có nghĩa là permalink của bạn có thể khác nếu bạn sử dụng% postname% trong cấu trúc permalink của bạn, có thể là một phần thưởng bổ sung.

ví dụ. cho http://example.com/rolling-stones/ khônghttp://example.com/the-rolling-stones/

EDIT : mã để cập nhật các sên hiện có, loại bỏ các tiền tố không mong muốn khỏi post_namecột ...

global $wpdb;
$posttype = 'release';
$stripprefixes = array('a-','an-','the-');

$results = $wpdb->get_results("SELECT ID, post_name FROM ".$wpdb->prefix."posts" WHERE post_type = '".$posttype."' AND post_status = 'publish');
if (count($results) > 0) {
    foreach ($results as $result) {
        $postid = $result->ID;
        $postslug = $result->post_name;
        foreach ($stripprefixes as $stripprefix) {
            $checkprefix = strtolower(substr($postslug,0,strlen($stripprefix));
            if ($checkprefix == $stripprefix) {
                $newslug = substr($postslug,strlen($stripprefix),strlen($postslug));
                // echo $newslug; // debug point
                $query = $wpdb->prepare("UPDATE ".$wpdb->prefix."posts SET post_name = '%s' WHERE ID = '%d'", $newslug, $postid);
                $wpdb->query($query);
            }
        }
    }
}

Giải pháp tuyệt vời - rất đơn giản và hiệu quả để phân loại.
BillK

Giải pháp đánh máy từ @birgire hoạt động như một cơ duyên, nhưng điều này có vẻ như là một sự thay thế hợp lý. Bây giờ tôi sẽ đi với một bài khác vì có khá nhiều bài viết được yêu cầu với một bài viết ban đầu và việc thay đổi tất cả các sên permalink có thể mất một thời gian. Tôi thích sự đơn giản của giải pháp này, mặc dù. Cảm ơn :-)
rpbtz

1
vì bạn thích, đã thêm một số mã sẽ thay đổi tất cả các sên nếu muốn / cần. :-)
Majick 7/2/2016

6

BIÊN TẬP

Tôi đã cải thiện mã một chút. Tất cả các khối mã được cập nhật tương ứng. Chỉ là một lưu ý mặc dù trước khi nhảy vào các bản cập nhật trong TRẢ LỜI GỐC , tôi đã thiết lập mã để làm việc với những điều sau đây

  • Loại bài đăng tùy chỉnh -> release

  • Phân loại tùy chỉnh -> game

Đảm bảo đặt điều này theo nhu cầu của bạn

CÂU TRẢ LỜI

Ngoài các câu trả lời khác và lỗi đánh máy được chỉ ra bởi @birgire, đây là một cách tiếp cận khác.

Đầu tiên, chúng tôi sẽ đặt tiêu đề là một trường tùy chỉnh ẩn, nhưng trước tiên chúng tôi sẽ xóa các từ như thechúng tôi muốn loại trừ. Trước khi làm điều đó, trước tiên chúng ta cần tạo một hàm trợ giúp để xóa các từ bị cấm khỏi tên thuật ngữ và tiêu đề bài

/**
 * Function get_name_banned_removed()
 *
 * A helper function to handle removing banned words
 * 
 * @param string $tring  String to remove banned words from
 * @param array  $banned Array of banned words to remove
 * @return string $string
 */
function get_name_banned_removed( $string = '', $banned = [] )
{
    // Make sure we have a $string to handle
    if ( !$string )
        return $string;

    // Sanitize the string
    $string = filter_var( $string, FILTER_SANITIZE_STRING );

    // Make sure we have an array of banned words
    if (    !$banned
         || !is_array( $banned )
    )
        return $string; 

    // Make sure that all banned words is lowercase
    $banned = array_map( 'strtolower', $banned );

    // Trim the string and explode into an array, remove banned words and implode
    $text          = trim( $string );
    $text          = strtolower( $text );
    $text_exploded = explode( ' ', $text );

    if ( in_array( $text_exploded[0], $banned ) )
        unset( $text_exploded[0] );

    $text_as_string = implode( ' ', $text_exploded );

    return $string = $text_as_string;
}

Bây giờ chúng ta đã có phần đó, hãy nhìn vào đoạn mã để đặt trường tùy chỉnh của chúng ta. Bạn phải xóa hoàn toàn mã này ngay khi bạn đã tải bất kỳ trang nào một lần. Nếu bạn có một trang web khổng lồ với rất nhiều bài đăng, bạn có thể đặt posts_per_pagethành một cái gì đó 100và chạy các tập lệnh một vài lần cho đến khi tất cả các bài đăng có trường tùy chỉnh đã được đặt thành tất cả các bài đăng

add_action( 'wp', function ()
{
    add_filter( 'posts_fields', function ( $fields, \WP_Query $q ) 
    {
        global $wpdb;

        remove_filter( current_filter(), __FUNCTION__ );

        // Only target a query where the new custom_query parameter is set with a value of custom_meta_1
        if ( 'custom_meta_1' === $q->get( 'custom_query' ) ) {
            // Only get the ID and post title fields to reduce server load
            $fields = "$wpdb->posts.ID, $wpdb->posts.post_title";
        }

        return $fields;
    }, 10, 2);

    $args = [
        'post_type'        => 'release',       // Set according to needs
        'posts_per_page'   => -1,              // Set to execute smaller chucks per page load if necessary
        'suppress_filters' => false,           // Allow the posts_fields filter
        'custom_query'     => 'custom_meta_1', // New parameter to allow that our filter only target this query
        'meta_query'       => [
            [
                'key'      => '_custom_sort_post_title', // Make it a hidden custom field
                'compare'  => 'NOT EXISTS'
            ]
        ]
    ];
    $q = get_posts( $args );

    // Make sure we have posts before we continue, if not, bail
    if ( !$q ) 
        return;

    foreach ( $q as $p ) {
        $new_post_title = strtolower( $p->post_title );

        if ( function_exists( 'get_name_banned_removed' ) )
            $new_post_title = get_name_banned_removed( $new_post_title, ['the'] );

        // Set our custom field value
        add_post_meta( 
            $p->ID,                    // Post ID
            '_custom_sort_post_title', // Custom field name
            $new_post_title            // Custom field value
        );  
    } //endforeach $q
});

Bây giờ các trường tùy chỉnh được đặt cho tất cả các bài đăng và mã ở trên đã bị xóa, chúng tôi cần đảm bảo rằng chúng tôi đặt trường tùy chỉnh này cho tất cả các bài đăng mới hoặc bất cứ khi nào chúng tôi cập nhật tiêu đề bài đăng. Đối với điều này, chúng tôi sẽ sử dụng transition_post_statusmóc. Đoạn mã sau có thể đi vào một plugin ( mà tôi khuyên dùng ) hoặc trongfunctions.php

add_action( 'transition_post_status', function ( $new_status, $old_status, $post )
{
    // Make sure we only run this for the release post type
    if ( 'release' !== $post->post_type )
        return;

    $text = strtolower( $post->post_title );   

    if ( function_exists( 'get_name_banned_removed' ) )
        $text = get_name_banned_removed( $text, ['the'] );

    // Set our custom field value
    update_post_meta( 
        $post->ID,                 // Post ID
        '_custom_sort_post_title', // Custom field name
        $text                      // Custom field value
    );
}, 10, 3 );

NHANH TAY BÀI VIẾT CỦA BẠN

Bạn có thể chạy các truy vấn của mình như bình thường mà không cần bất kỳ bộ lọc tùy chỉnh nào. Bạn có thể truy vấn và sắp xếp bài viết của mình như sau

$args_post = [
    'post_type'      => 'release', 
    'orderby'        => 'meta_value', 
    'meta_key'       => '_custom_sort_post_title',
    'order'          => 'ASC', 
    'posts_per_page' => -1, 
];
$loop = new WP_Query( $args );

Tôi thích cách tiếp cận này (có lẽ nó đủ để loại bỏ từ bị cấm khỏi phần đầu của tiêu đề)
birgire 7/2/2016

@birgire Tôi chỉ đi với điều này vì kiến ​​thức SQL của tôi kém như một con chuột nhà thờ, hahahaha. Cảm ơn vì lỗi đánh máy
Pieter Goosen 7/2/2016

1
Con chuột dí dỏm có thể nhanh nhẹn hơn nhiều so với con voi SQL được mã hóa cứng ;-)
birgire 7/2/2016

0

câu trả lời của birgire hoạt động tốt khi chỉ đặt hàng theo lĩnh vực này. Tôi đã thực hiện một số sửa đổi để làm cho nó hoạt động khi đặt hàng theo nhiều trường (tôi không chắc chắn rằng nó hoạt động chính xác khi thứ tự tiêu đề là chính):

add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
// Do nothing
if( '_custom' !== $q->get( 'orderby' ) && !isset($q->get( 'orderby' )['_custom']) )
    return $orderby;

global $wpdb;

$matches = 'The';   // REGEXP is not case sensitive here

// Custom ordering (SQL)
if (is_array($q->get( 'orderby' ))) {
    return sprintf( 
        " $orderby, 
        CASE 
            WHEN {$wpdb->posts}.post_title REGEXP( '^($matches)[[:space:]]+' )
                THEN TRIM( SUBSTR( {$wpdb->posts}.post_title FROM %d )) 
            ELSE {$wpdb->posts}.post_title 
        END %s
        ",
        strlen( $matches ) + 1,
        'ASC' === strtoupper( $q->get( 'orderby' )['_custom'] ) ? 'ASC' : 'DESC'     
    );
}
else {
    return sprintf( 
        "
        CASE 
            WHEN {$wpdb->posts}.post_title REGEXP( '^($matches)[[:space:]]+' )
                THEN TRIM( SUBSTR( {$wpdb->posts}.post_title FROM %d )) 
            ELSE {$wpdb->posts}.post_title 
        END %s
        ",
        strlen( $matches ) + 1,
        'ASC' === strtoupper( $q->get( 'order' ) ) ? 'ASC' : 'DESC'     
    );
}

}, 10, 2 );
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.