प्रारंभिक लेखों को अनदेखा करना (जैसे 'a', 'a' या 'the') जब प्रश्नों को छाँटते हैं?


13

मैं वर्तमान में संगीत शीर्षक की एक सूची का उत्पादन करने की कोशिश कर रहा हूं और शीर्षक के प्रारंभिक लेख को छांटना (लेकिन अभी भी प्रदर्शित करना) छांटना चाहता हूं।

उदाहरण के लिए यदि मेरे पास बैंड की सूची थी तो इसे इस तरह से वर्डप्रेस में वर्णानुक्रम में प्रदर्शित किया जाएगा:

  • ब्लैक सब्बाथ
  • लेड जेप्लिन
  • गुलाबी फ्लोयड
  • बीटल्स
  • सनक
  • रोलिंग स्टोन्स
  • थिन लिज़ी

इसके बजाय मैं इस तरह के प्रारंभिक लेख 'द' की अनदेखी करते हुए इसे वर्णानुक्रम में प्रदर्शित करना चाहूंगा:

  • बीटल्स
  • ब्लैक सब्बाथ
  • सनक
  • लेड जेप्लिन
  • गुलाबी फ्लोयड
  • रोलिंग स्टोन्स
  • थिन लिज़ी

मैं पिछले साल से एक ब्लॉग प्रविष्टि में एक समाधान भर आया था , जो निम्नलिखित कोड का सुझाव देता है 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;
}

और फिर क्वेरी को add_filterपहले और remove_filterबाद में लपेटकर ।

मैंने यह कोशिश की है, लेकिन मुझे अपनी साइट पर निम्न त्रुटि मिलती रहती है:

वर्डप्रेस डेटाबेस त्रुटि: [ऑर्डर क्लॉज 'में [अज्ञात कॉलम' title2 ']

Wp_posts का चयन करें। * wp_posts से 1 = 1 और wp_posts.post_type = 'release' AND (wp_posts.post_status = 'प्रकाशित करें या' wp_posts.post_status = 'private') ORDER BY UPPER (title2) को प्रकाशित करें।

मैं झूठ बोलने वाला नहीं हूं, मैं वर्डप्रेस के php भाग के लिए बहुत नया हूं, इसलिए मैं अनिश्चित हूं कि मुझे यह त्रुटि क्यों मिल रही है। मैं देख सकता हूं कि इसे 'title2' कॉलम के साथ कुछ करना है, लेकिन यह मेरी समझ थी कि पहले फ़ंक्शन को ध्यान रखना चाहिए। इसके अलावा, अगर ऐसा करने का कोई होशियार तरीका है तो मैं सभी कानों के पास हूं। मैं इस साइट को चारों ओर से देख रहा हूं और खोज रहा हूं, लेकिन मुझे वास्तव में बहुत सारे समाधान नहीं मिले हैं।

फ़िल्टर का उपयोग करने वाला मेरा कोड ऐसा दिखता है जैसे कि यह कोई मदद हो:

<?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
एक वैकल्पिक समाधान उस शीर्षक को संग्रहीत करने के लिए हो सकता है जिसे आप पोस्ट मेटा डेटा के रूप में क्रमबद्ध करना चाहते हैं और शीर्षक के बजाय उस फ़ील्ड पर ऑर्डर कर सकते हैं।
मिलो

मैं थोड़ा अनिश्चित हूं कि उसके साथ कैसे चलूं। वर्तमान में मुझे जो मिल रहा है, उसके समान एक नए कॉलम परिणाम में त्रुटि नहीं होगी?
rpbtz

1
आप उस कोड का उपयोग नहीं करेंगे, आप मेटा क्वेरी पैरामीटर के साथ पोस्ट मेटा पर क्वेरी और सॉर्ट कर सकते हैं ।
मिलो

जवाबों:


8

समस्या

मुझे लगता है कि वहाँ एक टाइपो है:

फिल्टर का नाम posts_fieldsनहीं है post_fields

यह समझा सकता है कि title2क्षेत्र अज्ञात क्यों है, क्योंकि यह परिभाषा उत्पन्न एसक्यूएल स्ट्रिंग में नहीं जोड़ा गया है।

वैकल्पिक - एकल फिल्टर

हम इसे केवल एक फिल्टर का उपयोग करने के लिए फिर से लिख सकते हैं:

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

अब आप _customऑर्डरबी पैरामीटर के साथ कस्टम ऑर्डरिंग को सक्रिय कर सकते हैं :

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

वैकल्पिक - पुनरावर्ती TRIM()

चलो पास्कल बर्चलर द्वारा पुनरावर्ती विचार को लागू करते हैं , यहां टिप्पणी की गई है :

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

जहाँ हम उदाहरण के लिए पुनरावर्ती कार्य का निर्माण कर सकते हैं:

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

इस का मतलब है कि

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

उत्पन्न करेगा:

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

वैकल्पिक - MariaDB

सामान्य तौर पर मुझे MySQL के बजाय MariaDB का उपयोग करना पसंद है । तब यह बहुत आसान है क्योंकि MariaDB 10.0.5 समर्थन करता है : 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 );

मुझे लगता है कि इस मुद्दे को मेरे समाधान से बेहतर हल करना चाहिए
पीटर गोएन्स

आप बिलकुल सही थे - पोस्ट_फिल्ड को पोस्ट_फिल्ड में बदलना इस मुद्दे को तय कर दिया और अब यह ठीक उसी तरह से छँट रहा है जैसा मैं चाहता हूँ। धन्यवाद! मैं अब थोड़ा बेवकूफ महसूस कर रहा हूं क्योंकि यह मुद्दा था। मुझे 4AM पर कोडिंग के लिए यही मिलता है, अनुमान लगाओ। मैं एकल फिल्टर समाधान में भी देखूंगा। एक बहुत अच्छा विचार लगता है। एक बार फिर धन्यवाद।
rpbtz

मैं इसे सही उत्तर के रूप में चिह्नित करूंगा क्योंकि यह वह है जो मेरे प्रारंभिक प्रश्नों से सबसे अधिक निकटता से संबंधित है, हालांकि जहां तक ​​मैं बता सकता हूं कि अन्य उत्तर भी मान्य समाधान हैं।
rpbtz

सिंगल फ़िल्टर विकल्प ने एक आकर्षण की तरह काम किया। मैं अब फ़िल्टर कोड को अंदर रख सकता हूं functions.phpऔर orderbyजब आवश्यकता हो तब इसे कॉल कर सकता हूं। महान समाधान - धन्यवाद :-)
rpbtz

1
खुशी है कि यह आपके लिए काम कर रहा है - मैंने पुनरावर्ती विधि को जोड़ा। @rpbtz
birgire

12

एक आसान तरीका हो सकता है कि उन पोस्ट्स पर जाएं और उन पोस्ट्स पर पर्मलिंक स्लग को बदल दें, जिनकी आवश्यकता है (पोस्ट लेखन स्क्रीन पर शीर्षक के तहत) और फिर शीर्षक के बजाय केवल ऑर्डर करने के लिए इसका उपयोग करें।

अर्थात। सॉर्टिंग के लिए उपयोग post_nameनहीं post_title...

इसका मतलब यह भी होगा कि यदि आप अपने पेरीलिंक संरचना में% पोस्टनाम% का उपयोग करते हैं, तो आपका पर्मलिंक अलग हो सकता है, जो एक अतिरिक्त बोनस हो सकता है।

जैसे। http://example.com/rolling-stones/ नहीं देता हैhttp://example.com/the-rolling-stones/

संपादित करें : मौजूदा स्लग को अपडेट करने के लिए कोड, post_nameकॉलम से अवांछित उपसर्गों को हटाकर ...

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

महान समाधान - छँटाई के लिए बहुत सरल और कुशल।
बिलके

@Birgire से टाइपो समाधान ने एक आकर्षण की तरह काम किया, लेकिन यह एक सभ्य विकल्प की तरह लगता है। मैं दूसरे के साथ अभी के लिए जाऊँगा क्योंकि प्रारंभिक लेख के साथ काफी कुछ चौकाने वाले पोस्ट हैं और सभी पर्मलिंक स्लग को बदलने में कुछ समय लग सकता है। मुझे इस समाधान की सादगी पसंद है, हालांकि। धन्यवाद :-)
rpbtz

1
जब से आपको पसंद आया, कुछ कोड जोड़े जो चाहिए / जरूरत होने पर सभी स्लग को बदलना चाहिए। :-)
मेजिक

6

संपादित करें

मैंने कोड में थोड़ा सुधार किया है। सभी कोड ब्लॉक तदनुसार अपडेट किए जाते हैं। केवल एक नोट, हालांकि मूल उत्तर में अपडेट में कूदने से पहले , मैंने निम्नलिखित के साथ काम करने के लिए कोड निर्धारित किया है

  • कस्टम पोस्ट प्रकार -> release

  • कस्टम टैक्सोनॉमी -> game

अपनी आवश्यकताओं के अनुसार इसे सेट करना सुनिश्चित करें

मूल ANSWER

अन्य उत्तरों और टाइपो के अलावा, @birgire द्वारा बताया गया है, यहां एक और दृष्टिकोण है।

सबसे पहले, हम शीर्षक को एक छिपे हुए कस्टम फ़ील्ड के रूप में सेट करेंगे, लेकिन हम पहले उन शब्दों को हटा देंगे जैसे theहम बाहर करना चाहते हैं। इससे पहले कि हम ऐसा करें, हमें पहले नाम और पद के शीर्षक से प्रतिबंधित शब्दों को हटाने के लिए एक सहायक फ़ंक्शन बनाना होगा

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

अब हमारे पास वह कवर है, जो हमारे कस्टम फ़ील्ड को सेट करने के लिए कोड के टुकड़े को देखता है। जैसे ही आपने किसी पृष्ठ को एक बार लोड किया है, आपको इस कोड को पूरी तरह से हटा देना चाहिए । आप पदों की एक टन के साथ एक बड़ी साइट है, तो आप सेट कर सकते हैं posts_per_pageके लिए कुछ करने के लिए 100और स्क्रिप्ट एक दो बार चलाने के लिए जब तक सभी पोस्ट कस्टम फ़ील्ड के सभी पोस्ट करने के लिए सेट किया गया है

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

अब जब कस्टम फ़ील्ड सभी पोस्ट पर सेट हो गए हैं और ऊपर का कोड हटा दिया गया है, तो हमें यह सुनिश्चित करने की आवश्यकता है कि हम इस कस्टम फ़ील्ड को सभी नई पोस्ट पर सेट करें या जब भी हम पोस्ट शीर्षक को अपडेट करें। इसके लिए हम transition_post_statusहुक का उपयोग करेंगे । निम्न कोड एक प्लगइन में जा सकता है ( जो मैं सुझाता हूं ) या आपकेfunctions.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 );

अपने पोस्ट को पूरा करना

आप बिना किसी कस्टम फ़िल्टर के अपने प्रश्नों को सामान्य रूप से चला सकते हैं। आप अपने पोस्ट को अनुसरण के अनुसार क्वेरी और सॉर्ट कर सकते हैं

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

मुझे यह दृष्टिकोण पसंद है (हो सकता है कि शीर्षक की शुरुआत से प्रतिबंधित शब्द को हटाने के लिए पर्याप्त हो)
911 पर बर्कगेयर

@ राहगीर मैं इसके साथ केवल इसलिए गया क्योंकि मेरा एसक्यूएल ज्ञान एक चर्च माउस, हाहाहा के रूप में खराब है। टाइपो के लिए धन्यवाद
Pieter Goosen

1
मजाकिया माउस
हार्डककोड

0

केवल इस क्षेत्र द्वारा आदेश देने पर बिरगायर के उत्तर अच्छे हैं। मैंने कई क्षेत्रों द्वारा आदेश देने पर इसे काम करने के लिए कुछ संशोधन किए (मुझे यकीन नहीं है कि यह सही ढंग से काम करता है जब शीर्षक आदेश प्राथमिक होता है):

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 );
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.