Rechercher

Synchroniser deux champs ACF relationship

Placeholder image

Dans un projet WordPress, nous avions besoin d’utiliser deux types de contenu personnalisé (CPT) qui doivent être liés entre eux. L’objectif est d’établir une relation bidirectionnelle entre ces deux CPT. Cela signifie que lorsqu’un utilisateur associe un élément du premier CPT à un autre élément dans le second CPT via un champ de relation, cette association doit être répercutée dans l’autre sens.

Exemple :

  • Si tu as deux CPT : « Professeurs » et « Cours ».
  • Si tu associes un professeur à un cours, il faut que le cours soit aussi automatiquement lié au professeur.

Explication des parties importantes du code :

  1. Détection de l’enregistrement automatique : La première condition empêche l’exécution de la fonction lors d’une sauvegarde automatique pour éviter les mises à jour inutiles ou non souhaitées.
  2. Récupération du type de post : get_post_type($post_id) et get_the_terms($post_id, 'format') sont utilisés pour identifier le type de post et le format associé (si applicable).
  3. Premier bloc de synchronisation :
    • Si le post appartient au type custom_post_type_1 (nom générique), il récupère les relations stockées dans related_field_1 (premier champ relationnel).
    • Pour chaque relation, il vérifie si le post lié a bien l’ID du post actuel dans son propre champ relationnel (related_field_2).
    • Si ce n’est pas le cas, il ajoute cette relation et met à jour le champ avec update_field().
  4. Deuxième bloc de synchronisation :
    • Ce bloc fait exactement la même chose, mais dans l’autre sens : s’il s’agit d’un post de type custom_post_type_2, il synchronise les relations du champ related_field_2 avec related_field_1 dans le post lié.
  5. Attachement au hook acf/save_post : La fonction est exécutée à chaque fois qu’un post est sauvegardé via ACF, en utilisant le hook avec une priorité de 20 pour s’assurer que cela se passe après les autres opérations.

Noms génériques :

  • custom_post_type_1 et custom_post_type_2 représentent les deux types de contenu personnalisé (CPT).
  • related_field_1 et related_field_2 représentent les champs de relation entre ces deux types de CPT.
function sync_acf_relationship_fields($post_id) {
  // Empêcher l'exécution du code pendant une sauvegarde automatique
  if (is_admin() && defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
    return;
  }

  // Récupérer le type de contenu personnalisé (CPT) du post actuel
  $post_type = get_post_type($post_id);

  // Récupérer les termes associés à la taxonomie 'format' du post actuel
  $post_format = get_the_terms($post_id, 'format');

  // Vérifier si le post appartient au premier type de CPT ('cpt_slug')
  // et que le format correspond (si applicable)
  if ($post_type == 'custom_post_type_1' && $post_format[0]->slug == 'format_slug') {
    // Récupérer les relations à synchroniser pour le premier champ personnalisé
    $related_posts_1 = get_field('related_field_1', $post_id);
    
    if ($related_posts_1) {
      // Parcourir chaque relation pour les synchroniser dans l'autre sens
      foreach ($related_posts_1 as $related_post_id) {
        // Récupérer les relations existantes dans le second champ du post lié
        $existing_related_posts = get_field('related_field_2', $related_post_id);
        if (!$existing_related_posts) {
          $existing_related_posts = array(); // Initialiser si vide
        }

        // Si le post actuel n'est pas déjà dans les relations, l'ajouter
        if (!in_array($post_id, $existing_related_posts)) {
          $existing_related_posts[] = $post_id;
          // Mettre à jour le champ relationnel dans le post lié
          update_field('related_field_2', $existing_related_posts, $related_post_id);
        }
      }
    }
  }

  // Vérifier si le post appartient au second type de CPT ('cpt_slug_2')
  if ($post_type == 'custom_post_type_2') {
    // Récupérer les relations à synchroniser pour le second champ personnalisé
    $related_posts_2 = get_field('related_field_2', $post_id);

    if ($related_posts_2) {
      // Parcourir chaque relation pour les synchroniser dans l'autre sens
      foreach ($related_posts_2 as $related_post_id) {
        // Récupérer les relations existantes dans le premier champ du post lié
        $existing_related_posts = get_field('related_field_1', $related_post_id);
        if (!$existing_related_posts) {
          $existing_related_posts = array(); // Initialiser si vide
        }

        // Si le post actuel n'est pas déjà dans les relations, l'ajouter
        if (!in_array($post_id, $existing_related_posts)) {
          $existing_related_posts[] = $post_id;
          // Mettre à jour le champ relationnel dans le post lié
          update_field('related_field_1', $existing_related_posts, $related_post_id);
        }
      }
    }
  }
}

// Attacher la fonction à l'action 'acf/save_post' pour synchroniser après la sauvegarde d'un post
add_action('acf/save_post', 'sync_acf_relationship_fields', 20);

Authoriser les SVG dans les champs ACF

Placeholder image
/**
 * ACF SVG filter to allow raw SVG code.
 * 
 * https://www.advancedcustomfields.com/resources/html-escaping/
 * 
 */
add_filter( 'wp_kses_allowed_html', 'acf_add_allowed_svg_tag', 10, 2 );

function acf_add_allowed_svg_tag( $tags, $context ) {
    if ( $context === 'acf' ) {
      $tags['svg']  = array(
          'xmlns'				=> true,
          'width'				=> true,
          'height'				=> true,
          'preserveAspectRatio'	=> true,
          'fill'					=> true,
          'viewbox'			=> true,
          'role'				=> true,
          'aria-hidden'			=> true,
          'focusable'			=> true,
      );
      $tags['path'] = array(
          'd'    => true,
          'fill' => true,
      );
    }
    return $tags;
}

Selectionnez automatiquement une option de variation si elle est la seule disponible dans un produit Woocommerce

Placeholder image

Ce script permet de selectionner automatiquement une option de variation d’un produit dans woocommerce si elle est la seule disponible. Par exemple, si vous choisissez un t-shirt en S et que la seule couleur disponible pour cette taille est bleu, cette option sera automatiquement selectionnée.



add_action( 'wp_footer', 'auto_select_variation_option' );
function auto_select_variation_option() {
    if ( ! is_product() ) {
        return;
    }
    ?>
    <script type="text/javascript">
        jQuery(document).ready(function($){

            // Fonction pour sélectionner automatiquement l'attribut avec une seule option disponible
            function autoSelectSingleOption() {
                var changeTriggered = false; // Drapeau pour suivre si un changement a été déclenché

                $('.variations_form .variations select').each(function(){
                    if (changeTriggered) {
                        return false; // Sortir de la boucle si un changement a été déclenché
                    }

                    var select = $(this);
                    var selectedVal = select.val(); // Vérifier si une valeur est déjà sélectionnée
                    var options = select.find('option').not(':disabled'); // Obtenir les options disponibles

                    // Vérifier s'il n'y a qu'une seule option sélectionnable et aucune sélection faite
                    if (options.length === 2 && !selectedVal) { 
                        var optionToSelect = options.last().val(); // La seule option disponible

                        // Vérifier si cette option est déjà sélectionnée
                        if (select.val() !== optionToSelect) {
                            select.val(optionToSelect).trigger('change'); // Sélectionner et déclencher WooCommerce
                            changeTriggered = true; // Marquer qu'un changement a été déclenché
                        }
                    }
                });
            }

            // Lorsque la page est chargée et que la sélection change, on vérifie si une sélection automatique doit être faite
            $('.variations_form').on('woocommerce_variation_select_change', function() {
                if (!$('.variations_form').hasClass('processing')) { // Éviter de lancer si Ajax est déjà en cours
                    setTimeout(() => {
                      autoSelectSingleOption(); // Exécuter lors du changement de variation
                    }, 160);
                }
            });

        });
    </script>
    <?php
}

Un sommaire auto en JS

Placeholder image

Créer un sommaire automatique pour vos pages en JS :

  • Mettre une div pour recevoir le code généré par le JS
  • Choisir dans quelle div vous souhaitez chercher vos titres
function generateSlug(text) {
    return text.normalize("NFD")
                .replace(/[\u0300-\u036f]/g, "")
                .toLowerCase()
                .replace(/[^\w\s]/g, '')
                .replace(/\s+/g, '-');
}

// Fonction pour créer la table des matières
function createTableOfContents() {
    // Sélectionner tous les titres de niveau 2 dans la div page-body
    const pageBody = document.querySelector('.page-body');
    const headings = pageBody.querySelectorAll('h2');

    // Sélectionner la div #toc et la liste UL où insérer les liens
    const toc = document.querySelector('#toc ul');

    // Parcourir tous les <h2>
    headings.forEach((heading, index) => {
        // Générer une slug (ID) à partir du texte du titre
        const headingText = heading.textContent;
        const slug = generateSlug(headingText);

        // Ajouter une ID au titre <h2> avec la slug générée
        heading.id = slug;

        // Créer un élément <li> pour la table des matières
        const listItem = document.createElement('li');
        
        // Créer un lien <a> qui pointe vers le titre avec l'ID
        const link = document.createElement('a');
        link.href = `#${slug}`;
        link.textContent = headingText;

        // Ajouter le lien à l'élément <li>
        listItem.appendChild(link);

        // Ajouter l'élément <li> à la table des matières (dans l'ul)
        toc.appendChild(listItem);
    });
}

// Appeler la fonction pour créer la table des matières
createTableOfContents();

Ajouter un style pour l’editeur interne Gutenberg

Placeholder image

Pour designer les blocks dans l’admin aussi !

function mytheme_enqueue_block_editor_assets() {
    // Charger le fichier CSS personnalisé dans l'éditeur Gutenberg
    wp_enqueue_style(
        'mytheme-editor-styles', // Nom du handle
        get_theme_file_uri('/editor-style.css'), // Chemin vers le fichier CSS
        false, // Pas de dépendances
        '1.0', // Version du fichier
        'all' // Media
    );
}
add_action('enqueue_block_editor_assets', 'mytheme_enqueue_block_editor_assets');
// Custom style Editor only
function custom_gutenberg_editor_style() {
    echo '<style>
        .interface-complementary-area__fill,
        .interface-complementary-area__fill > div {
            width: 480px !important;
            min-width: 480px;
        }
    </style>';
}
add_action('admin_head', 'custom_gutenberg_editor_style');

Ajouter un style par défault à un bloc Gutenberg existant

Placeholder image
function mytheme_register_block_styles() {
    // Ajouter un style personnalisé "not" au bloc Groupe
    register_block_style(
        'core/group',
        array(
            'name'  => 'note',
            'label' => __('Note Style', 'mytheme'),
            'isDefault' => false, // Optionnel : false pour ne pas le définir comme style par défaut
        )
    );
}
add_action('init', 'mytheme_register_block_styles');
.wp-block-group.is-style-note {
    background-color: #f0f0f0;
    border: 2px dashed #ff0000;
    padding: 20px;
    color: #333;
}

Dates en français en PHP

Placeholder image

C’est juste compliqué, donc voilà la version courte.

Pour plus de formats : Formatting Dates and Times | ICU Documentation (unicode-org.github.io)

$date = new DateTime($time[0]['start']['date']);

// Créer le format de date avec le fuseau horaire français
$fmt = datefmt_create(
  'fr_FR',
  IntlDateFormatter::FULL, // Style complet (lundi 25 septembre 2023)
  IntlDateFormatter::FULL,
  'Europe/Paris', // Utilisation du fuseau horaire français
  IntlDateFormatter::GREGORIAN,
  'd MMMM yyyy' // Format : jour mois année (par exemple : 25 septembre 2023)
);

// Affichage de la date formatée
echo datefmt_format($fmt, $date);

Déterminer le sens du scroll (up or down)

Placeholder image
var scrollText = document.getElementById('scroll');
var lastScrollTop = 0;
var ticking = false;

function handleScroll() {
    var st = window.scrollY || document.documentElement.scrollTop;
    if (st > lastScrollTop) {
        scrollText.innerText = 'Scroll Down';
    } else {
        scrollText.innerText = 'Scroll Up';
    }
    lastScrollTop = Math.max(st, 0); // Éviter des valeurs négatives pour le scroll
}

window.addEventListener("scroll", function() {
    if (!ticking) {
        window.requestAnimationFrame(function() {
            handleScroll();
            ticking = false;
        });
        ticking = true;
    }
});

Rendre une liste horizontale scrollable au touch

Placeholder image
.scrollable-container {
    padding: 20px;
    display: flex;
    gap: 20px;
    /* REAL CODE */
    overflow-x: scroll;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    overflow: -moz-scrollbars-none;
    -ms-overflow-style: none;
}
.scrollable-container::-webkit-scrollbar {
    display: none;
}
.scrollable-container > div {
    padding: 20px;
    min-width: 200px;
    box-shadow: 0 0 8px rgba(0,0,0,0.1);
}