web-dev-qa-db-fra.com

Manière plus rapide de wp_insert_post & add_post_meta en masse

Je veux insérer un fichier csv composé d'environ 1 500 lignes et 97 colonnes. Il faut environ 2 à 3 heures pour effectuer une importation complète et j'aimerais l'améliorer s'il y a un moyen. Actuellement, pour chaque ligne, je fais un $ post_id = wp_insert_post puis un add_post_meta pour les 97 colonnes associées à chaque ligne. C'est assez inefficace ...

Existe-t-il un meilleur moyen de s’y prendre pour qu’un post_id conserve la relation entre post et ses valeurs post_meta?

En ce moment, j'essaye ceci sur ma machine locale avec wamp mais je vais le faire tourner sur un VPS

13
Corey Rowell

J'ai eu des problèmes similaires il y a quelque temps avec une importation CSV personnalisée, mais j'ai fini par utiliser un code SQL personnalisé pour l'insertion en bloc. Mais je n'avais pas encore vu cette réponse:

Optimiser post insertion et suppression pour les opérations en vrac?

utiliser wp_defer_term_counting() pour activer ou désactiver le comptage de termes.

De plus, si vous consultez le répertoire source du plug-in d'importateur WordPress, vous verrez ces fonctions juste avant l'importation en bloc:

wp_defer_term_counting( true );
wp_defer_comment_counting( true );

puis après l'insertion en vrac:

wp_defer_term_counting( false );
wp_defer_comment_counting( false );

Donc, cela pourrait être quelque chose à essayer ;-)

Importer des publications en tant que brouillon au lieu de publier, accélérera également les choses, car le processus lent de recherche d'un slug unique pour chacun d'entre eux est ignoré. On pourrait par exemple publiez-les plus tard en petites étapes, mais notez que ce type d'approche devrait marquer les publications importées d'une manière ou d'une autre, de sorte que nous ne publions pas de brouillon plus tard! Cela nécessiterait une planification minutieuse et très probablement un codage personnalisé.

S'il y a par exemple Beaucoup de titres d'articles similaires (même post_name) doivent être importés, alors wp_unique_post_slug() peut devenir lent, en raison de l'itération de requête en boucle pour trouver un slug disponible. Cela peut éventuellement générer un grand nombre de requêtes de base de données.

Depuis WordPress 5.1, le filtre pre_wp_unique_post_slug est disponible pour éviter l'itération de la boucle pour le slug. Voir ticket principal # 21112 . Voici un exemple:

add_filter( 'pre_wp_unique_post_slug', 
    function( $override_slug, $slug, $post_id, $post_status, $post_type, $post_parent ) {
        // Set a unique slug value to shortcircuit the slug iteration loop.
        // $override_slug = ...

        return $override_slug;
    }, 10, 6
);

Si on essaie, par exemple $override_slug = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix" avec $suffix en tant que $post_id, nous noterions alors que $post_id est toujours 0 pour les nouvelles publications, comme prévu. Il existe différentes manières de générer des nombres uniques en PHP, comme uniqid( '', true ). Mais utilisez ce filtre avec soin pour vous assurer que vous avez des limaces uniques. Nous pourrions par exemple Exécutez ensuite une requête sur le nombre de groupes sur post_name pour en être sûr.

Une autre option serait d’utiliser WP-CLI pour éviter le timeout. Voir par exemple ma réponse posté pour Création de 20 000 articles ou pages à l’aide d’un fichier .csv?

Ensuite, nous pouvons exécuter notre script personnalisé PHP import import.php avec la commande WP-CLI:

wp eval-file import.php

Évitez également d'importer un grand nombre de types de publication hiérarchiques, car l'interface utilisateur actuelle de wp-admin ne la gère pas correctement. Voir par exemple Type de message personnalisé - Liste des messages - Écran blanc de la mort

Voici le bon conseil de @otto:

Avant insertions en bloc, désactivez explicitement le mode autocommit:

$wpdb->query( 'SET autocommit = 0;' );

Après les insertions en vrac, exécutez:

$wpdb->query( 'COMMIT;' );

Je pense aussi que ce serait une bonne idée de faire un peu de ménage comme:

$wpdb->query( 'SET autocommit = 1;' );

Je n'ai pas testé cela sur MyISAM, mais cela devrait fonctionner sur InnoDB.

Comme mentionné par @kovshenin, cette astuce ne fonctionnerait pas pour MyISAM.

18
birgire

Vous devrez insérer le message pour obtenir votre identifiant, mais la table $wpdb->postmeta a une structure très simple. Vous pourriez probablement utiliser une instruction INSERT INTO pure, comme celle-ci dans la documentation MySQL: INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

Dans ton cas...

$ID = 1; // from your wp_insert_post
$values = '($ID,2,3),($ID,5,6),($ID,8,9)'; // build from your 97 columns; I'd use a loop of some kind
$wpdb->query("INSERT INTO {$wpdb->postmeta} (post_id,meta_key,meta_value) VALUES {$values}");

Cela ne traitera pas l'encodage, la sérialisation, l'échappement, la vérification des erreurs, les duplications ou toute autre chose, mais je m'attendrais à ce que ce soit plus rapide (bien que je ne l'aie pas encore essayé).

Je ne le ferais pas sur un site de production sans des tests approfondis, et si je devais le faire une ou deux fois seulement, j'utiliserais les fonctions principales et prendrais un long déjeuner pendant que les choses importaient.

5
s_ha_dum

Je devais ajouter ceci:

    remove_action('do_pings', 'do_all_pings', 10, 1);

Gardez à l'esprit que ceci ignorera do_all_pings, qui traite les pingbacks, les enclos, les rétroliens et autres ping (link: https://developer.wordpress.org/reference/functions/do_all_pings/ ). D'après ce que j'ai compris en regardant le code, les pingbacks/rétroliens/enclos en attente seront toujours traités après la suppression de cette ligne remove_action, mais je ne suis pas tout à fait sûr.

Mise à jour: j'ai aussi ajouté

    define( 'WP_IMPORTING', true );

Au-delà, j'utilise:

    ini_set("memory_limit",-1);
    set_time_limit(0);
    ignore_user_abort(true);

    wp_defer_term_counting( true );
    wp_defer_comment_counting( true );
    $wpdb->query( 'SET autocommit = 0;' );

    /* Inserting 100,000 posts at a time
       including assigning a taxonomy term and adding meta keys
       (i.e. a `foreach` loop with each loop containing:
       `wp_insert_post`, `wp_set_object_terms`, `add_post_meta`.)
    */

    $wpdb->query( 'COMMIT;' );
    wp_defer_term_counting( false );
    wp_defer_comment_counting( false );
3
firasd

Note importante à propos de 'SET autocommit = 0;'

après avoir défini autocommit = 0 si le script arrête l'exécution (pour une raison quelconque, telle que exit, erreur fatale ou etc ...), vos modifications ne seront pas enregistrées dans la base de données!

$wpdb->query( 'SET autocommit = 0;' );

update_option("something", "value");     

exit; //lets say, here happens error or anything...

$wpdb->query( 'COMMIT;' );

Dans ce cas, update_option ne sera pas enregistré dans la base de données!

Donc, le meilleur conseil est d’inscrire COMMIT dans shutdown comme fonction de précaturation (au cas où une sortie inattendue se produirait).

register_shutdown_function( function(){
    $GLOBALS['wpdb']->query( 'COMMIT;' );
} );
0
T.Todua