web-dev-qa-db-fra.com

Désactiver les requêtes de médias lents?

Lorsqu'un utilisateur crée/met à jour une publication dans l'administrateur, cette opération prend entre 15 et 30 secondes.

Le coupable semble être cette requête lente:

SELECT ID
FROM wp_posts
WHERE post_type = 'attachment'
AND post_mime_type LIKE 'video%'
LIMIT 1

Il s'agit d'un bug connu et l'équipe principale y travaille, mais entre-temps, j'aimerais pouvoir désactiver cette requête. Puis-je faire cela dans mon fichier functions.php en utilisant quelque chose comme le filtre pre_get_posts?

3
bigmike7801

Solution pour les versions de WordPress> = 4.7.4 (4.8)

Ticket # 31071 introduit des correctifs avec de nouveaux filtres pour remplacer trois requêtes de média lentes possibles, dans la fonction wp_enqueue_media():

  • media_library_show_audio_playlist (@param bool | null)

    Depuis le document en ligne: Indique si le bouton doit être affiché ou null afin de décider en fonction de l'existence de fichiers audio dans la bibliothèque multimédia.

  • media_library_show_video_playlist (@param bool | null)

    Dans le document en ligne: Indique si le bouton doit être affiché ou null pour décider si des fichiers vidéo existent dans la bibliothèque multimédia.

  • media_library_months_with_files (@param array | null)

    Dans le document en ligne: Un tableau d'objets avec les propriétés month et year, ou null (ou toute autre valeur autre qu'un tableau) pour le comportement par défaut.

Exemple:

Voici un plugin de démonstration:

<?php
/**
  * Plugin Name:  Override Possible Slow Media Queries
  * Plugin URI:   https://wordpress.stackexchange.com/a/200383/26350
  */

// Always show audio button
add_filter( 'media_library_show_audio_playlist', '__return_true' );

// Always show video button
add_filter( 'media_library_show_video_playlist', '__return_true' );

// Cache media library file months with the transients API
add_filter( 'media_library_months_with_files', function( $months )
{
    // Generate file months when it's not cached or the transient has expired
    if ( false === ( $months = get_transient( 'wpse_media_library_months_with_files' ) ) )
    {
        global $wpdb;

        /**
         * Note that we want to avoid returning non-array file months,  
         * to avoid running the slow query twice.
         *
         * From the Codex for wpdb::get_results( $query, $output_type ):
         *
         * "If no matching rows are found, or if there is a 
         *  database error, the return value will be an empty array.
         *  If your $query string is empty, or you pass an invalid 
         *  $output_type, NULL will be returned."
         *
         * So it looks like we're covered, as we're not dealing with 
         * empty query or a wrong return type.
         */
        $months = $wpdb->get_results( $wpdb->prepare( "
            SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
            FROM $wpdb->posts
            WHERE post_type = %s
            ORDER BY post_date DESC
         ", 'attachment' ) );

        // Cache the results
        set_transient(
            'wpse_media_library_months_with_files',
                $months,
                12 * HOUR_IN_SECONDS   // <-- Override to your needs!
             );
    }
    return $months;
} );

Notez que nous pourrions également sélectionner manuellement le fichier mois avec, par exemple:

$months = [
    (object) [ 'year' => 2017, 'month' => 2 ],
    (object) [ 'year' => 2017, 'month' => 1 ],
    (object) [ 'year' => 2016, 'month' => 12 ],
];

en utilisant le filtre media_library_months_with_files.

Réponse précédente

Ces requêtes sont dans la fonction wp_enqueue_media():

$has_audio = $wpdb->get_var( "
            SELECT ID
            FROM $wpdb->posts
            WHERE post_type = 'attachment'
            AND post_mime_type LIKE 'audio%'
            LIMIT 1
    " );
 $has_video = $wpdb->get_var( "
            SELECT ID
            FROM $wpdb->posts
            WHERE post_type = 'attachment'
            AND post_mime_type LIKE 'video%'
            LIMIT 1
    " );
 $months = $wpdb->get_results( $wpdb->prepare( "
            SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
            FROM $wpdb->posts
            WHERE post_type = %s
            ORDER BY post_date DESC
    ", 'attachment' ) );

Voici un moyen de modifier ces requêtes lentes potentielles:

/**
 * Modify the potential slow $has_audio, $has_video and $months queries
 *
 * @link http://wordpress.stackexchange.com/a/200383/26350
 */
add_filter( 'media_upload_tabs', function( $tabs )
{
    add_filter( 'query', 'wpse_replace_months_sql' );
    add_filter( 'query', 'wpse_replace_audio_video_sql' );
    return $tabs;
} );

add_filter( 'media_view_settings', function( $settings )
{
    remove_filter( 'query', 'wpse_replace_months_sql' );
    remove_filter( 'query', 'wpse_replace_audio_video_sql' );
    return $settings;
} );

où (PHP 5.4+):

/**
 * Use "SELECT false" for the $has_audio and $has_video queries
 */
function wpse_replace_audio_video_sql( $sql )
{
   global $wpdb;
   foreach( [ 'audio', 'video' ] as $type )
   {
      $find = "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'attachment' 
          AND post_mime_type LIKE '{$type}%' LIMIT 1";
      if( trim( preg_replace('/\s+/', ' ', $sql) ) == trim( preg_replace('/\s+/', ' ', $find) ) )
          return "SELECT false"; // <-- We could also use true here if needed
   }
   return $sql;
} 

et

/**
 * Replace the available months query with the current month
 */
function wpse_replace_months_sql( $sql )
{
    global $wpdb;
    $find = "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
        FROM {$wpdb->posts} WHERE post_type = 'attachment' ORDER BY post_date DESC";
    if( trim( preg_replace('/\s+/', ' ', $sql) ) == trim( preg_replace('/\s+/', ' ', $find) ) )
         $sql = "SELECT YEAR( CURDATE() ) as year, MONTH( CURDATE() ) as month";
    return $sql;
}

Nous pourrions essayer d’affiner la situation en créant des compteurs has_audio et has_video dans la table d’options et en le mettant à jour chaque fois que nous téléchargeons/supprimons un fichier audio ou vidéo.

Dans le ticket de traçabilité mentionné dans la question, il y a un index proposé :

ALTER TABLE $wpdb->posts ADD INDEX type_mime(post_type,post_mime_type)

cela pourrait donner un coup de pouce.

@ Denis-de-Bernardy donne également un exemple de requêtes alternatives pour la partie mois.

4
birgire