htmlcss.fr Des tutos ou tutoriels Wordpress, html, php ou javascript.

Exporter en xlsx depuis wordpress

E

Ce n’est pas vraiment un tutoriel. Disons plutôt que je présente ici un bout de code maison pour exporter au format xlsx des données issues de WordPress. Une façon de partage et de sauvegarder le machin 🙂

Attention : ce n’est pas un plugin mais un petit module que j’installe dans un thème maison ou enfant. Il faudra donc modifier quelques petites choses dans votre thème pour que cela fonctionne.
C’est une class, donc on peut faire plusieurs instances et réutiliser le code très rapidement.

Le module permet d’ajouter deux endroits où l’on peut exporter les données: le dashboard ou une page option dans le menu du post type. Vous avez le choix de l’un ou l’autre ou les deux.

Un peu de contexte

Les données que l’on souhaite exporter viennent d’un post type et sont stockées via ACF.
Par exemple nous avons un post type « Personnage » avec des champs « Nom » et « Prénom ».
Pour générer le xlsx, j’utilise PhpSpreadsheet. Il faudra donc l’installer via un

composer require phpoffice/phpspreadsheet

Comment ça fonctionne

C’est assez simple. Vous devait bien évidement inclure le fichier du module php dans votre functions.php

require_once 'export.php';

Puis initialiser une instance comme dans l’exemple suivant:

if(is_admin()) :
  // je défini les champs à exporter, dans l ordre que je souhaite
  $cpt_fields = [
    [
      'label' => 'Titre',
      'field' => false,
      'method' => 'get_the_title',
    ], [
      'label' => 'Nom',
      'field' => 'nom',
    ], [
      'label' => 'Prénom',
      'field' => 'prenom',
    ], [
      'label' => 'Date',
      'field' => false,
      'method' => 'get_the_date',
    ],
  ];
  // Je fais une instace du module avec les valeurs souhaitées
  $exp = new CustomPostTypeExport('cpt', 'Personnage', $cpt_fields);
endif;

Et voilà, vous pouvez exporter les données de votre CPT.

Le code source

Je vous invite à consulter les variable du constructeur pour avoir le détail des options possibles.

<?php
//////////////////////////////
// INFOS
//////////////////////////////
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

if(is_admin()) :
  // je défini les champs à exporter, dans l ordre que je souhaite
  $cpt_fields = [
    [
      'label' => 'Titre',
      'field' => false,
      'method' => 'get_the_title',
    ], [
      'label' => 'Nom',
      'field' => 'nom',
    ], [
      'label' => 'Prénom',
      'field' => 'prenom',
    ], [
      'label' => 'Date',
      'field' => false,
      'method' => 'get_the_date',
    ],
  ];
  // Je fais une instace du module avec les valeurs souhaitées
  $exp = new CustomPostTypeExport('cpt', 'Personnage', $cpt_fields);
endif;

class CustomPostTypeExport {
  private $PHP_MIN_VERSION;
  private $POST_TYPE;
  private $POST_TYPE_LABEL; // au singulier
  private $POST_TYPE_FIELDS;
  private $plural_letters;
  private $EXPORT_POSITION; // Valuers possibles: dashbord, menu, both
  private $pagenow;

  function __construct($cpt, $cpt_label, $fields, $position = 'both', $plural_letters = 's', $php_min = '7.3.0')
  {
    $this->POST_TYPE = $cpt;
    $this->POST_TYPE_LABEL = $cpt_label;
    $this->POST_TYPE_FIELDS = $fields;
    $this->EXPORT_POSITION = $position;
    $this->PHP_MIN_VERSION = $php_min;
    $this->plural_letters = $plural_letters;
    $this->pagenow = $GLOBALS['pagenow'];

    if(is_admin() && $this->pagenow === "index.php" && ($this->EXPORT_POSITION === 'dashbord' || $this->EXPORT_POSITION === 'both')){
      add_action('admin_notices', [$this, 'notice_check']);
      require_once get_template_directory() . '/php/vendor/autoload.php';
      add_action('wp_dashboard_setup', [$this, 'init_dashboard_widgets']);
    }

    if(is_admin() && ($this->EXPORT_POSITION === 'menu' || $this->EXPORT_POSITION === 'both')){
      require_once get_template_directory() . '/php/vendor/autoload.php';
      add_action('admin_menu', [$this, 'register_submenu_export']);
    }

  }
  //////////////////////////////////////
  // Widget
  //////////////////////////////////////
  public function init_dashboard_widgets()
  {
    if (isset($_POST['export_all'])) {
      if (!wp_verify_nonce($_POST['nonce_all'], 'token_all')) {die('Token non valide');} //securité
      $this->generate_xls();
    }
    wp_add_dashboard_widget('widget_export_'.$this->POST_TYPE, 'Export: '.$this->POST_TYPE_LABEL, [$this, 'render_dashbord_widget']);
  }

  public function render_dashbord_widget($var, $args)
  {
    $count_posts = wp_count_posts($this->POST_TYPE);
    $nb = $count_posts->publish;
    if($nb> 1){ $total = $nb.' '.$this->POST_TYPE_LABEL.$this->plural_letters; }else{ $total = $nb.' '.$this->POST_TYPE_LABEL; }
    ?>
    <div class="wrap theme-options-page">
    <p>Vous pouvez exporter les <?= $this->POST_TYPE_LABEL.$this->plural_letters ?> ici.</p>
    <p>Il y a <strong><?= $total; ?></strong></p>
    <form action="" method="post">
    <input type="hidden" name="nonce_all" value="<?= wp_create_nonce('token_all'); ?>" >
    <p class="submit">
    <input type="submit" name="export_all" class="button-primary autowidth" value="Exporter">
    </p>
    </form>
    </div>
    <?php
  }

  //////////////////////////////////////
  // Sous menu
  //////////////////////////////////////
  public function register_submenu_export()
  {
    if (isset($_POST['export_all'])) {
      if (!wp_verify_nonce($_POST['nonce_all'], 'token_all')) {die('Token non valide');} //securité
      $this->generate_xls();
    }
    //https://developer.wordpress.org/reference/functions/add_submenu_page/
    add_submenu_page(
    'edit.php?post_type=' . $this->POST_TYPE,
    'Export inscriptions événements',
    'Exporter',
    'edit_posts',
    'export_' . $this->POST_TYPE,
    [$this, 'render_export_page']);
  }

  public function render_export_page()
  {
    // Comme pour le widget, on compte les elements presents
    $count_posts = wp_count_posts($this->POST_TYPE);
    $nb = $count_posts->publish;
    if ($nb > 1) {$total = $nb . ' ' . $this->POST_TYPE_LABEL . $this->plural_letters;} else { $total = $nb . ' ' . $this->POST_TYPE_LABEL;}
    ?>
    <div>
      <h2>Exportation du post type : <?=$this->POST_TYPE?></h2>
      <form method="post" action="">
       <p>Vous pouvez exporter les <?= $this->POST_TYPE_LABEL.$this->plural_letters ?> ici.</p>
       <p>Il y a <strong><?=$total;?></strong></p>
       <input type="hidden" name="nonce_all" value="<?= wp_create_nonce('token_all'); ?>" >
       <p class="submit">
        <input type="submit" name="export_all" class="button-primary autowidth" value="Exporter">
       </p>
      </form>
    </div>
    <?php
  }
  //////////////////////////////////////
  // Function Export
  //////////////////////////////////////
  private function generate_xls()
  {
    $date = current_time('_Y_m_d-H\hi');
    $filename = 'export_'.$this->POST_TYPE.$date;
    $spreadsheet = new Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    $lettre_colonne = 'A';

    // On définit les titres des colonnes
    foreach ($this->POST_TYPE_FIELDS as $field) {
      $sheet->setCellValueExplicit(
        $lettre_colonne . "1",
        $field['label'],
        \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING
      );
      $lettre_colonne++;
    }

    // La boucle du post type
    $args = array(
      'post_type' => $this->POST_TYPE,
      'posts_per_page' => -1,
      // 'orderby' => 'menu_order',
      // 'order' => 'ASC',
    );
    $loop = new WP_Query($args);
    $ligne = 2; // on commence à la deuxieme car on a déjà les titres
    $lettre_colonne = 'A'; // reset $lettre_colonne;
    while ($loop->have_posts()): $loop->the_post();
      $vars = get_fields();
      foreach ($this->POST_TYPE_FIELDS as $key => $field) {
        if(!$field['field']) {
          $value = $field['method']();
        }else{
          $value = $vars[$field['field']];
        }
        $sheet->setCellValueExplicit(
          $lettre_colonne . ($ligne),
          $value,
          \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING
        );
        $lettre_colonne++;
      }
      $lettre_colonne = 'A'; // reset $lettre_colonne;
      $ligne++;
    endwhile;

    $writer = new Xlsx($spreadsheet);
    $writer->setPreCalculateFormulas(false);
    ///////////////////////////////////////////////////
    // Les headers pour le téléchargement (fichier XlSX)
    ///////////////////////////////////////////////////
    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    header('Content-Disposition: attachment;filename="' . $filename . '.xlsx"');
    header('Content-Transfer-Encoding: binary');
    header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Une date dans le passé
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
    header('Cache-Control: cache, must-revalidate');
    header('Pragma: public');

    ob_clean();
    $writer->save('php://output');
    exit();
  }

  //////////////////////////////////////
  // Version check & toolz
  //////////////////////////////////////
  public function notice_check()
  {
    $error_message = '';
    define('PHP_EXTENSIONS', ['zip', 'xml', 'gd']);
    if (version_compare(PHP_VERSION, $this->PHP_MIN_VERSION, '<')) {
      $error_message .= '<p>L\'export nécessite au inimum la version PHP ' . $this->PHP_MIN_VERSION . '.</p>';
    }

    foreach (PHP_EXTENSIONS as $ext) {
      if (!$this->php_extension_check($ext)) {
        $error_message .= '<p>L\'export nécessite que l\'extension PHP php_' . $ext . ' soit installée et activée.</p>';
        $no_error = false;
      }
    }
    if (is_admin() && $error_message) {
      echo '<div class="notice notice-error is-dismissible">' . $error_message . '</div>';
    }
  }

  private function php_extension_check($ext)
  {
    if (extension_loaded($ext)) {
      return true;
    }
    return false;
  }
}

Voilà, je vous souhaite une bonne soirée.

htmlcss.fr Des tutos ou tutoriels Wordpress, html, php ou javascript.

François Riant

Je m’appelle François Riant. Je travail dans les métiers du web depuis 2006. Mon expérience m’a amené à changer plusieurs fois de technologie.

Aujourd’hui je cherche à partager mon expérience et j’y trouve du de plaisir. Je travail actuellement chez W2P Digital.
Je ne prends donc pas de mission en freelance.

Si vous avez une remarque ou une question; vous pouvez me joindre sur francois.riant@gmail.com