Skip to main content

2.2. Création d'un fichier .docx

Ici, la génération des documents est effectué par service propriétaire afin de se limiter à environ 4000 archives maximum par fichier.

Code PHP principal

/inc/guideRecherche/v2-guide.php
add_action('wp_ajax_SID_create_docx_by_service_proprietaire', 'SID_create_docx_by_service_proprietaire');
function SID_create_docx_by_service_proprietaire(){

// Prevent empty --------------------------------------------------------------------------------
if( empty($_POST['service']) ) $_POST['service'] = '';
if( !isset($_POST['key']) || $_POST['key'] === ''){
return;
}
else{
$key = intval( $_POST['key'] + 1 );
}

// Créer un nouvel objet document Word -----------------------------------------------------------
$phpWord = new \PhpOffice\PhpWord\PhpWord();

// Style du document -----------------------------------------------------------------------------
$linkStyle = [
'bold' => true,
'allCaps' => true,
'underline' => 'single',
'color' => '000000',
];
$phpWord->addTitleStyle( 1,
['bold' => true, 'color' => '8C4A8E', 'size' => 13],
['align' => 'center', 'spaceBefore' => 0, 'spaceAfter' => 600]
);
$phpWord->addTitleStyle(2, [ 'bold' => true, 'color' => '965592', ] );
$phpWord->addTitleStyle(3, [ 'bold' => true, 'color' => 'CA438F', ] );
$phpWord->addTitleStyle(4, [ 'bold' => true, 'color' => 'EE4767', ] );

// Init section document -------------------------------------------------------------------------
$section = $phpWord->addSection();

// SHOW ARCHIVES (by service proprietaire) -------------------------------------------------------
$posts = guideGetArchivesByServicesProprietaires( $_POST['service'] );
docxAddArchivesServiceProprietaire($section, $linkStyle, $posts['grouped_posts'], $key);

// Enregistrer le document Word ------------------------------------------------------------------
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
$upload_dir = wp_upload_dir();
$upload_guide_folder = $upload_dir['basedir'] . '/guide-de-recherche/';
if( !is_dir($upload_guide_folder) ){ mkdir($upload_guide_folder, 0755, true); }
$fileName = 'guide_de_recherche_part_'.intval($key).'.docx';
$fullPath = $upload_guide_folder . $fileName;
$objWriter->save($fullPath);


// Return ----------------------------------------------------------------------
echo json_encode( [
'id' => intval($key),
'service' => $_POST['service'],
'fichier' => $fileName,
'path' => $fullPath,
'nombre_archives' => $posts['nb_posts'],
'archives' => $posts['grouped_posts'],
]);
die;

}

1. Vérifications initiales

  • Le code vérifie d'abord si le paramètre service (le nom du service propriétaire) est fourni via $_POST. S'il est vide, il est défini comme une chaîne vide pour éviter les erreurs.
  • Il vérifie également si une clé (key) est fournie et n'est pas vide. Cette clé est utilisée pour générer un identifiant unique pour le document. Si ces conditions ne sont pas remplies, la fonction s'arrête prématurément.

2. Création d'un document Word

  • Utilise la bibliothèque PHP PhpOffice\PhpWord pour créer un nouvel objet document Word.
  • Définit différents styles de titre pour structurer le document (par exemple, styles pour différents niveaux de titres).

3. Préparation du contenu du document:

  • La fonction guideGetArchivesByServicesProprietaires() est appelée avec le service propriétaire spécifié pour récupérer les archives associées. Elle renvoie un tableau d'archives groupées d'une certaine manière.
    Voir plus de détail via la rubrique Récupération des services propriétaires
  • La fonction docxAddArchivesServiceProprietaire() ajoute ensuite ces archives au document Word en tant que contenu.
    Voir plus de détail via la rubrique Récupération des services propriétaires

4. Sauvegarde du document:

  • Le document est ensuite enregistré dans un dossier spécifique sur le serveur (/guide-de-recherche/ dans le dossier de téléchargement /wp-content/uploads/ de WordPress). Le nom du fichier est basé sur un modèle qui inclut la clé pour garantir l'unicité. Si le dossier de destination n'existe pas, il est créé.

5. Réponse:

  • Enfin, la fonction envoie une réponse JSON contenant plusieurs informations sur le document généré, y compris un identifiant (id), le service propriétaire, le nom du fichier (fichier), le chemin d'accès au fichier (path), le nombre d'archives (nombre_archives) et les archives elles-mêmes (archives). Cette réponse peut être utilisée par le code JavaScript qui a initié l'appel AJAX pour informer l'utilisateur ou effectuer des actions supplémentaires, comme le téléchargement du document.

Récupération des services propriétaires

Ce code définit une fonction guideGetArchivesByServicesProprietaires destinée à récupérer et à organiser des articles WordPress (posts) en fonction d'un service propriétaire spécifique, puis à les trier par thème et sous-thème.

Nous verrons son fonctionnement ci-dessous :

/inc/guideRecherche/v2-guide.php
// ************************************************************************************************************************
// Retourne les archives (posts)
// Triés par Services proprietaire > theme > sous-theme
// Return @array
// ************************************************************************************************************************
function guideGetArchivesByServicesProprietaires( $service_proprietaire ){

// Pas de limite de temps.
set_time_limit(0);

// Init ------------------------------------------------------------------------
global $wpdb;
$grouped_posts = array();

// Get posts by service --------------------------------------------------------
$args = array(
'post_type' => 'post',
'post_status' => array('publish', 'draft'),
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'DESC',
'posts_per_page' => -1,
'meta_key' => 'service_proprietaire',
'meta_value' => stripslashes($service_proprietaire),
);
$query = new WP_Query( $args );
$posts = $query->posts;
if( !is_array($posts) ||empty($posts) ) return;
$nb_posts = count($posts);

// Parcours des posts by service proprietaire -----------------------------------
foreach($posts as $key => $post_id) {

// Reinit values
$acf_values = null;
$themes = null;
$parent_theme = null;
$child_theme = null;

// Get post data (ACF)
$acf_values = get_fields($post_id);
$acf_values['post_id'] = $post_id;

// Get post Themes / Sous-themes
$themes = wp_get_post_terms($post_id, 'theme', array('fields' => 'all'));
if ($themes && !is_wp_error($themes)) {
foreach ($themes as $theme) {
if ($theme->parent == 0) {
$parent_theme = $theme;
}
else {
$child_theme = $theme;
}
}
}

// Prevent empty values
if( empty($acf_values['service_proprietaire']) ){
$acf_values['service_proprietaire'] = '999';
}
if( empty($parent_theme) ){
$parent_theme = new stdClass();
$parent_theme->slug = '999999'; // 'Pas de thème'
}
if( empty($child_theme) ) {
$child_theme = new stdClass();
$child_theme->slug = '999999999'; // 'Pas de sous-thème'
}

// Crée un tableau pour le service propriétaire --- si ce n'est pas déjà fait
$acf_values['service_proprietaire'] = mb_convert_case($acf_values['service_proprietaire'], MB_CASE_TITLE, "UTF-8");
if (!isset($grouped_posts[$acf_values['service_proprietaire']])) {
$grouped_posts[$acf_values['service_proprietaire']] = array();
}
// Crée un tableau pour chaque thème / sous-theme, sous le service propriétaire correspondant --- si ce n'est pas déjà fait
if (!isset($grouped_posts[$acf_values['service_proprietaire']][$parent_theme->slug][$child_theme->slug])) {
$grouped_posts[$acf_values['service_proprietaire']][$parent_theme->slug][$child_theme->slug] = array();
}

// SAVE : Ajoute le post au groupe approprié
$grouped_posts[$acf_values['service_proprietaire']][$parent_theme->slug][$child_theme->slug][$post_id] = $acf_values;

}

// Return -----------------------------------------------------------------------
recursive_ksort($grouped_posts);
return [
'grouped_posts' => $grouped_posts,
'nb_posts' => $nb_posts,
];

}

1. Augmentation du Temps d'Exécution :

  • set_time_limit(0); désactive la limite de temps d'exécution du script, permettant à la fonction de s'exécuter aussi longtemps que nécessaire sans être interrompue.

2. Préparation de la Requête :

  • Un tableau $args est préparé avec des critères spécifiques pour interroger les articles : type de poste, statut, ordre de tri, etc. Le plus important est meta_key et meta_value, utilisés pour filtrer les articles par le service propriétaire.

3. Exécution de la Requête :

  • WP_Query est utilisé avec les $args pour récupérer les articles correspondants. Les articles sont renvoyés sous forme d'identifiants.

4. Traitement des Articles :

  • Pour chaque article récupéré, plusieurs actions sont effectuées :
    • Les champs personnalisés (ACF) de l'article sont récupérés via get_fields().
    • Les termes de taxonomie theme sont récupérés pour identifier le thème et le sous-thème de l'article.
    • Des structures conditionnelles et des opérations de chaîne permettent de gérer les valeurs manquantes ou spéciales, attribuant des valeurs par défaut aux cas où le service propriétaire, le thème ou le sous-thème ne sont pas définis.

5. Regroupement des Articles :

  • Les articles sont ensuite regroupés dans un tableau multidimensionnel selon leur service propriétaire, thème et sous-thème. Ce processus crée une structure de données hiérarchique permettant de facilement parcourir et afficher les articles organisés.
Tableau multidimensionnel
├── Services propriétaires
├── Thème
├── Sous-thème

6. Renvoi des Résultats :

La fonction renvoie un tableau contenant deux éléments : le tableau des articles groupés (grouped_posts) et le nombre total d'articles (nb_posts). Cette structure de données peut être utilisée pour générer des rapports, des documents ou pour afficher les informations sur le front-end.

Création d'un document word .docx

Cette fonction, docxAddArchivesServiceProprietaire, est conçue pour organiser et afficher des archives WordPress dans un document Word (.docx), triées par service propriétaire, thème, et sous-thème.

Nous verrons son fonctionnement ci-dessous :

/inc/guideRecherche/v2-guide.php
// ************************************************************************************************************************
// Affiche les archives
// Triés par Services proprietaire > theme > sous-theme
// Param : $section (docx) + $linkstyle + ordered posts ( get via guideGetArchivesByServicesProprietaires() )
// ************************************************************************************************************************
function docxAddArchivesServiceProprietaire($section, $linkStyle, $posts, $key){

// Pas de limite de temps.
set_time_limit(0);

// Check Liste de documents
if( !is_array($posts) || empty($posts) ) return;

// Parcours des services proprietaires
foreach ($posts as $service_name => $service) {

// Show title service name
$nb_services = $key;
$service_name_new = str_replace('999', 'Pas de service proprietaire', $service_name);
$service_name_new = mb_strtoupper($service_name_new, 'UTF-8');
$service_name_new = str_replace(array('&', '\\'), array('and', '/'), $service_name_new);
$section->addTitle($nb_services.'. '.$service_name_new, 2);

// Parcours des themes
$nb_themes = 0;
foreach ($service as $theme_slug => $theme) {
$nb_themes ++;

// Show theme name
$term = get_term_by('slug', $theme_slug, 'theme');
if ($term !== false && !is_wp_error($term)) {
$section->addTitle($nb_services.'.'.$nb_themes.'. '.$term->name, 3);
}
else if( $theme_slug == '999999' ){
$section->addTitle($nb_services.'.'.$nb_themes.'. Pas de thème', 3);
}

// Parcours des sous-themes
$nb_sous_themes = 0;
foreach ($theme as $sous_theme_slug => $sous_theme) {
$nb_sous_themes++;

// Show sous-theme name
$term = get_term_by('slug', $sous_theme_slug, 'theme');
if ($term !== false && !is_wp_error($term)) {
$section->addTitle($nb_services.'.'.$nb_themes.'.'.$nb_sous_themes.'. '.$term->name, 4);
}
else if( $sous_theme_slug == '999999999' ){
$section->addTitle($nb_services.'.'.$nb_themes.'.'.$nb_sous_themes.'. Pas de sous-thème', 4);
}
$section->addTextBreak(1);

// Parcours des documents
foreach ($sous_theme as $post_id => $post) {

// Init values
$dossier = " - N° de dossier manquant - ";
$date = "";

// Get title archive
$title = get_the_title($post_id);
$title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
// $title = str_replace(array('&', '\\'), array('and', '/'), $title);

// Get description
$description = get_field('resume_pour_diffusion',$post_id);
$description = iconv(mb_detect_encoding($description, mb_detect_order(), true), "UTF-8", $description);
$description = str_replace(array('&', '\\'), array('and', '/'), $description);
$description = htmlspecialchars($description, ENT_QUOTES, 'UTF-8');
$description = addslashes($description);


// Get dossier (numéro de dossier)
if( !empty($post['n_de_dossier']) ){
// $dossier = '$$$$$-'.$post['n_de_dossier'];
$dossier = $post['n_de_dossier'];
}

// Get dates
if( !empty($post['debut']) && !empty($post['fin']) ){
$date = $post['debut'] . '-' . $post['fin'];
}
else if( !empty($post['debut']) ){
$date = $post['debut'];
}
else if( !empty($post['fin']) ){
$date = $post['fin'];
}


// QUICKFIX PERMALINKS
$postObject = get_post($post_id);

// Show document link
$section->addText( '[URL: https://www.archives.sncf.com/'.$postObject->post_name.' | '.$dossier.']' );

// Show document values
$section->addText( $title );
$section->addText( $description );
$section->addText( $date, array(), array('align' => 'right') );

}
$section->addTextBreak(2);

}

}
}

// Saut de page
$section->addPageBreak();
}

1. Initialisation :

  • Désactive la limite de temps d'exécution pour permettre au script de s'exécuter jusqu'à son achèvement.
  • Vérifie que la liste des posts fournie ($posts) est valide et contient des données.

2. Traitement des Données :

  • Itère sur chaque groupe d'archives, organisé par service propriétaire, puis par thème et sous-thème.
  • Pour chaque niveau d'organisation, un titre est ajouté au document avec des styles spécifiques pour différencier visuellement les niveaux hiérarchiques (service propriétaire, thème, sous-thème).

3. Affichage des Archives :

  • Pour chaque archive, les détails tels que le numéro de dossier, le titre, la description, et les dates sont affichés.
  • Une attention particulière est portée à la manière de représenter le lien vers l'archive et la manipulation de texte pour éviter les problèmes d'encodage.

4. Gestion des Liens :

  • Afin d'éviter des problèmes d'encodage lors de la fusion de multiple .docx, nous utilisons une technique particulière pour insérer des liens dans le document Word : [URL: adresse | dossier]. Celui ci sera transformé en lien hypertexte (lors de la fusion), conduisant à la page de l'archive.

5. Insertion de Sauts de Page :

  • À la fin du traitement de chaque service propriétaire, un saut de page est inséré pour commencer un nouveau service sur une nouvelle page, améliorant ainsi la lisibilité et l'organisation du document.