Sorguları sıralarken ilk makaleleri ('a', 'an' veya 'the' gibi) yoksayıyor musunuz?


13

Şu anda müzik başlıklarının bir listesini çıkarmak için çalışıyorum ve sıralama (ama yine de başlık) ilk makalesini görmezden istiyorum.

Örneğin, bir grup listesi vardı, WordPress alfabetik olarak şu şekilde görüntülenir:

  • Black Sabbath
  • Led Zeplin
  • Pink Floyd
  • The Beatles
  • Kinks
  • Yuvarlanan taşlar
  • Minik lizy

Bunun yerine, 'The' ilk makalesini göz ardı ederken alfabetik olarak görüntülenmesini istiyorum, şöyle:

  • The Beatles
  • Black Sabbath
  • Kinks
  • Led Zeplin
  • Pink Floyd
  • Yuvarlanan taşlar
  • Minik lizy

Geçen yıl blog girişinde bir çözümle karşılaştım , bu da aşağıdaki kodu önerir 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;
}

ve ardından sorguyu add_filterönce veremove_filter sonra .

Bunu denedim, ancak sitemde aşağıdaki hatayı almaya devam ediyorum:

WordPress veritabanı hatası: ['sipariş yan tümcesinde' Bilinmeyen sütun 'başlık2']

Wp_posts SEÇİN. * Wp_posts'TEN NEREDE 1 = 1 AND wp_posts.post_type = 'release' AND (wp_posts.post_status = 'yayınla' VEYA wp_posts.post_status = 'private') SİPARİŞ TARAFINDAN (başlık2) ASC

Yalan söylemeyeceğim, WordPress'in php bölümünde oldukça yeniyim, bu yüzden bu hatayı neden aldığımdan emin değilim. 'Title2' sütunu ile ilgili bir şey olduğunu görebiliyorum, ancak ilk fonksiyonun bununla ilgilenmesi gerektiğine inanıyorum. Ayrıca, bunu yapmanın daha akıllı bir yolu varsa, ben tamamen kulaklarım. Etrafta dolaşıyorum ve bu siteyi araştırıyorum, ancak gerçekten çok fazla çözüm bulamadım.

Herhangi bir yardım varsa filtreleri kullanarak kodum şöyle görünür:

<?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
alternatif bir çözüm, sıralamak istediğiniz başlığı post meta verileri olarak depolamak ve başlık yerine bu alanda sıralamak olabilir.
Milo

Bununla nasıl devam edeceğim konusunda biraz emin değilim. Bunu yeni bir sütunda depolamak, şu anda aldığım hataya benzer bir hataya neden olmaz mı?
rpbtz

1
bu kodun hiçbirini kullanmazsanız, meta meta parametrelerini kullanarak meta metaları sorgulayabilir ve sıralayabilirsiniz .
Milo

Yanıtlar:


8

Sorun

Bence burada bir yazım hatası var:

Filtrenin adı posts_fieldsdeğil post_fields.

Bu, title2alanın neden bilinmediğini açıklayabilir , çünkü tanımı oluşturulan SQL dizesine eklenmez.

Alternatif - Tek filtre

Yalnızca tek bir filtre kullanmak için yeniden yazabiliriz:

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 );

artık özel siparişi _customorderby parametresiyle etkinleştirebilirsiniz :

$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();

Alternatif - Özyinelemeli TRIM()

Pascal Birchler tarafından tekrarlanan fikri uygulayalım , yorumladı :

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 );

burada özyinelemeli işlevi şu şekilde oluşturabiliriz:

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 );
}

Bunun anlamı şudur ki

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

üretecek:

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

Alternatif - MariaDB

Genel olarak MySQL yerine MariaDB kullanmayı seviyorum . O zaman çok daha kolay çünkü MariaDB 10.0.5 şunları destekler : 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 );

Bunun sorunu benim
çözümümden

Kesinlikle haklıydınız - post_field'leri posts_fields olarak değiştirmek sorunu çözdü ve şimdi tam olarak istediğim şekilde sıralanıyor. Teşekkür ederim! Sorun olduğum için şimdi biraz aptal hissediyorum. Saat 4'te kodlamak için aldığım şey, sanırım. Tek bir filtre çözümüne de bakacağım. Gerçekten iyi bir fikir gibi görünüyor. Tekrar teşekkürler.
rpbtz

Bunu doğru cevap olarak işaretleyeceğim, çünkü bu benim ilk sorularımla en yakından ilişkili olanıdır, ancak diğer cevapların da geçerli çözümler olduğunu söyleyebilirim.
rpbtz

Tek filtre alternatif de bir cazibe gibi çalıştı. Şimdi filtre kodunu saklayabilir functions.phpve orderbyihtiyacım olduğunda arayabilirim . Harika bir çözüm - teşekkür ederim :-)
rpbtz

1
İşinize yaradığını duyduğuma sevindim - özyinelemeli yöntemi ekledim. @rpbtz
birgire

12

Daha kolay bir yol, ihtiyaç duyulan yazılarda (yazı yazma ekranındaki başlığın altında) kalıcı bağlantı bilgisini değiştirmek ve bunu başlık yerine sipariş vermek için kullanmak olabilir.

yani. kullanmak post_namedeğil post_titleSıralama için ...

Bu, kalıcı bağlantı yapınızda% postname% kullanırsanız kalıcı bağlantınızın farklı olabileceği anlamına gelir; bu da ek bir bonus olabilir.

Örneğin. verir http://example.com/rolling-stones/ değilhttp://example.com/the-rolling-stones/

DÜZENLEME : mevcut sümüklü böcekleri güncellemek için kod, istenmeyen önekleri post_namesütundan kaldırmak ...

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);
            }
        }
    }
}

Mükemmel çözüm - sıralama için çok basit ve verimli.
BillK

@Birgire'ın yazım hatası çözümü bir cazibe gibi çalıştı, ancak bu iyi bir alternatif gibi görünüyor. Şimdilik diğeriyle birlikte gideceğim çünkü başlangıçtaki bir makaleye sahip birkaç sorgulanan mesaj var ve tüm kalıcı sümüklü böcekleri değiştirmek biraz zaman alabilir. Yine de bu çözümün basitliğini seviyorum. Teşekkürler :-)
rpbtz

1
Beğendiğinizden, istenirse / gerekirse tüm sümüklü böcekleri değiştirmesi gereken bir kod ekledi. :-)
majick

6

DÜZENLE

Kodu biraz geliştirdim. Tüm kod bloğu buna göre güncellenir. Sadece bir not olsa da, ORİJİNAL CEVAP güncellemelerine atlamadan önce , aşağıdakilerle çalışmak için kodu ayarladım

  • Özel gönderi türü -> release

  • Özel sınıflandırma -> game

Bunu ihtiyaçlarınıza göre ayarladığınızdan emin olun

ORİJİNAL CEVAP

Diğer yanıtlara ve @birgire tarafından belirtilen yazım hatalarına ek olarak, işte başka bir yaklaşım daha var.

İlk olarak, başlığı gizli bir özel alan olarak ayarlayacağız, ancak önce thehariç tutmak istediğimiz kelimeleri kaldıracağız . Bunu yapmadan önce, yasaklanmış kelimeleri terim adlarından ve yazı başlıklarından kaldırmak için önce bir yardımcı işlev oluşturmamız gerekir.

/**
 * 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;
}

Şimdi bunu ele aldığımıza göre, özel alanımızı ayarlamak için kod parçasına bakalım. Herhangi bir sayfayı bir kez yükler yüklemez bu kodu tamamen kaldırmanız gerekir . Tonlarca yayını olan büyük bir siteniz varsa, tüm yayınlar özel alan tüm yayınlara ayarlanana kadar komut dosyalarına bir posts_per_pageşeyler ayarlayabilir 100ve komut dosyalarını birkaç kez çalıştırabilirsiniz.

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
});

Artık özel alanlar tüm yayınlara ayarlandığından ve yukarıdaki kod kaldırıldığından, bu özel alanı tüm yeni yayınlara ayarladığımızdan veya yayın başlığını güncellediğimizden emin olmamız gerekir. Bunun için transition_post_statuskancayı kullanacağız . Aşağıdaki kod bir eklentiye ( önerdiğim ) veyafunctions.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 );

YAZILARINIZI SORGULAMA

Özel filtreler olmadan sorgularınızı normal şekilde çalıştırabilirsiniz. Yayınlarınızı aşağıdaki gibi sorgulayabilir ve sıralayabilirsiniz

$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 );

Bu yaklaşımı beğendim (belki yasaklı kelimeyi başlığın başından kaldırmak yeterlidir)
Birgire

@birgire Sadece bununla gittim çünkü SQL bilgim bir kilise faresi kadar zayıf, hahahaha. Yazım hatası için teşekkürler
Pieter Goosen

1
Esprili fare sabit kodlu SQL
filinden

0

birgire'ın cevapları sadece bu alana göre sipariş verirken iyi çalışır. Birden fazla alana göre sipariş verirken çalışması için bazı değişiklikler yaptım (başlık siparişi birincil olduğunda doğru çalıştığından emin değilim):

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 );
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.