私は現在音楽のタイトルのリストを出力しようとしています、そしてソートがタイトルの最初の記事を無視するようにしたいです(しかしそれでも表示します)。
例えば、私がバンドのリストを持っていたなら、それはこのようにWordPressでアルファベット順に表示されるでしょう:
代わりに、このように最初の記事「The」を無視してアルファベット順に表示したいと思います。
私は 昨年のブログ記事 の中で解決策を見つけました。それは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;
}
次に、クエリをbefore add_filter
およびremove_filter
afterでラップします。
私はこれを試しましたが、私は私のサイトで以下のエラーを得続けています:
WordPressデータベースエラー:['order句'の不明な列 'title2']
SELECT wp_posts。* from wp_posts WHERE 1 = 1 AND wp_posts.post_type = 'release' AND(wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')ORDER BY UPPER(title2)ASC
私はうそをつくつもりはありません、私はWordPressの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();
?>
誤字があると思います。
フィルタの名前はposts_fields
ではなくpost_fields
です。
これは、title2
フィールドが不明である理由を説明するためのものです。定義が生成されたSQL文字列に追加されていないためです。
単一のフィルタのみを使用するように書き直すことができます。
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
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();
TRIM()
再帰的なアイデアを Pascal Birchler 、 ここではコメント付き :)で実装しましょう。
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)
) )
) )
) )
一般的に私はMySQLの代わりにMariaDBを使うのが好きです。 MariaDB 10.0.5supportsREGEXP_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 );
もっと簡単な方法は、それを必要とする投稿(投稿書き込み画面のタイトルの下)のパーマリンクスラッグを調べて変更し、それをタイトルの代わりに順序付けに使用することです。
すなわち。ソートにはpost_name
ではなくpost_title
を使用してください...
これはまた、パーマリンク構造で%postname%を使用すると、パーマリンクが異なる可能性があることを意味します。これは追加のボーナスになる可能性があります。
例えば。 http://example.com/rolling-stones/
ではなくhttp://example.com/the-rolling-stones/
を与える
_ edit _ :既存のスラッグを更新し、不要なプレフィックスを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);
}
}
}
}
コードを少し改良しました。それに応じてすべてのコードブロックが更新されます。ちょっと注意してくださいORIGINAL ANSWERの更新に飛び込む前に、私は次のコードで動作するようにコードを設定しました。
カスタム投稿タイプ - > release
カスタム分類法 - > game
あなたの必要性に従ってこれを設定することを忘れないでいなさい
@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;
}
これでカバーしたので、カスタムフィールドを設定するためのコードを見てみましょう。ページを一度ロードしたらすぐに、このコードを完全にremoveする必要があります。巨大なサイトで大量の投稿がある場合は、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 );
birgireの回答は、このフィールドのみで注文した場合に有効です。複数のフィールドで注文するときに機能するように修正を加えました(タイトルの順序が主な場合に正しく機能するかどうかわかりません)。
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 );