J'essaie actuellement de sortir une liste de titres musicaux et j'aimerais que le tri ignore (mais continue d'afficher) l'article initial du titre.
Par exemple, si j'avais une liste de groupes, elle serait affichée alphabétiquement dans WordPress comme ceci:
Au lieu de cela, j'aimerais qu'il soit affiché par ordre alphabétique tout en ignorant l'article initial 'The', comme ceci:
Je suis tombé sur une solution dans une entrée de blog de l'année dernière , qui suggère le code suivant dans 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;
}
et ensuite encapsuler la requête avec add_filter
avant et remove_filter
après.
J'ai essayé cela, mais je continue à avoir l'erreur suivante sur mon site:
Erreur de base de données WordPress: [Colonne inconnue 'title2' dans 'clause de commande']
SELECT wp_posts. * FROM wp_posts WHERE 1 = 1 AND wp_posts.post_type = 'release' AND (wp_posts.post_status = 'publier' OR wp_posts.post_status = 'private') ORDER BY UPPER (title2) ASC
Je ne vais pas mentir, je suis assez nouveau dans la partie php de WordPress, alors je ne sais pas pourquoi je reçois cette erreur. Je peux voir que cela a quelque chose à voir avec la colonne 'title2', mais je croyais comprendre que la première fonction devrait en tenir compte. Aussi, s'il y a une façon plus intelligente de faire cela, je suis tout ouïe. J'ai cherché sur ce site sur Google, mais je n'ai pas vraiment trouvé beaucoup de solutions.
Mon code utilisant les filtres ressemble à ceci si c'est une aide:
<?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();
?>
Je pense qu'il y a une faute de frappe là-dedans:
Le nom du filtre est posts_fields
et non post_fields
.
Cela pourrait expliquer pourquoi le champ title2
est inconnu, car sa définition n'est pas ajoutée à la chaîne SQL générée.
Nous pouvons le réécrire pour n’utiliser qu’un seul filtre:
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 );
où vous pouvez maintenant activer la commande personnalisée avec le paramètre _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()
Implémentons l'idée récursive par Pascal Birchler , commenté ici :
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 );
où nous pouvons par exemple construire la fonction récursive comme:
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 );
}
Cela signifie que
$matches = [ 'the ', 'an ', 'a ' ];
echo wpse_sql( $matches, " LOWER( {$wpdb->posts}.post_title) " );
va générer:
TRIM( LEADING 'a ' FROM (
TRIM( LEADING 'an ' FROM (
TRIM( LEADING 'the ' FROM (
LOWER( wp_posts.post_title)
) )
) )
) )
En général, j'aime bien utiliser MariaDB au lieu de MySQL. Alors c'est beaucoup plus facile car MariaDB 10.0.5supporteREGEXP_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 );
Un moyen plus simple consiste à modifier le slug de permalien des publications qui en ont besoin (sous le titre sur l'écran de rédaction de publication), puis à l'utiliser simplement pour passer commande à la place du titre.
c'est à dire. utilisez post_name
pas post_title
pour le tri ...
Cela signifierait également que votre lien permanent peut être différent si vous utilisez% postname% dans votre structure de lien permanent, ce qui pourrait constituer un bonus supplémentaire.
par exemple. donne http://example.com/rolling-stones/
pas http://example.com/the-rolling-stones/
EDIT: code pour mettre à jour les slugs existants, en supprimant les préfixes indésirables de la colonne 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);
}
}
}
}
J'ai un peu amélioré le code. Tous les blocs de code sont mis à jour en conséquence. Juste une note cependant avant de sauter dans les mises à jour de la REPONSE ORIGINALE , j'ai configuré le code pour qu'il fonctionne avec les éléments suivants
Type de message personnalisé ->release
Taxonomie personnalisée ->game
Assurez-vous de régler ceci en fonction de vos besoins
Outre les autres réponses et la typo signalée par @birgire, voici une autre approche.
Tout d'abord, nous allons définir le titre en tant que champ personnalisé masqué, mais nous allons d'abord supprimer les mots tels que the
que nous voudrions exclure. Avant de faire cela, nous devons d'abord créer une fonction d'assistance afin de supprimer les mots interdits des noms de terme et les titres de publication.
/**
* 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;
}
Maintenant que nous avons couvert cela, regardons le morceau de code pour définir notre champ personnalisé. Vous devez supprimer ce code complètement dès que vous avez chargé une page une fois. Si vous avez un site immense avec une tonne de publications, vous pouvez définir posts_per_page
sur quelque chose à 100
et exécuter les scripts plusieurs fois jusqu'à ce que toutes les publications du champ personnalisé soient définies sur toutes les publications.
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
});
Maintenant que les champs personnalisés sont définis sur toutes les publications et que le code ci-dessus est supprimé, nous devons nous assurer de définir le champ personnalisé sur toutes les nouvelles publications ou chaque fois que nous mettons à jour le titre de la publication. Pour cela, nous utiliserons le hook transition_post_status
. Le code suivant peut aller dans un plugin ( que je recommande ) ou dans votre 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 );
Vous pouvez exécuter vos requêtes normalement, sans filtres personnalisés. Vous pouvez interroger et trier vos messages comme suit
$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 );
les réponses de birgire fonctionnent bien lorsque vous commandez uniquement par ce champ. J'ai apporté quelques modifications pour que cela fonctionne lors de la commande par plusieurs champs (je ne suis pas sûr que cela fonctionne correctement lorsque l'ordre des titres est le premier):
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 );