Essayez sans attendre l'hébergement proposé par WordPress
-15% sur le premier mois avec le code 2025PRESS15AFF

Essayer maintenant

WordPress 6.9 : Maîtriser les nouvelles API de création de blocs dynamiques

WordPress 6.9 débarque avec une nouvelle classe WP_Block_Builder qui va changer votre façon de créer des blocs dynamiques. Fini les galères avec register_block_type() et ses limitations : cette nouvelle API orientée objet promet des performances boostées, un cache automatique des attributs et une architecture bien plus propre. On va voir ensemble comment maîtriser cette nouveauté qui va révolutionner votre workflow de développement !

Les nouvelles API de création de blocs : ce qui change vraiment

WordPress 6.9 marque un tournant dans la création de blocs dynamiques. Fini les bidouillages avec register_block_type() ! Cette version repense complètement l’architecture pour offrir plus de flexibilité et de performance.

L’architecture repensée des blocs dynamiques

La grande révolution, c’est l’introduction de la classe WP_Block_Builder. Exit l’ancienne méthode où on devait jongler avec plusieurs fonctions :

// Ancienne méthode (WordPress < 6.9)
register_block_type('mon-plugin/bloc-custom', [
    'render_callback' => 'mon_bloc_render',
    'attributes' => [...]
]);

function mon_bloc_render($attributes) {
    // Logique de rendu
}

Maintenant, on utilise une approche orientée objet plus claire :

// Nouvelle méthode (WordPress 6.9+)
class Mon_Bloc_Custom extends WP_Block_Builder {
    public function get_attributes() {
        return ['title' => ['type' => 'string']];
    }
    
    public function render($attributes, $content) {
        // Rendu avec mise en cache automatique
        return $this->build_output($attributes);
    }
}

register_block_builder('mon-plugin/bloc-custom', new Mon_Bloc_Custom());

Cette nouvelle architecture sépare mieux les responsabilités et facilite la maintenance.

Les améliorations de performance côté serveur

Là où ça devient intéressant, c’est au niveau des performances. WordPress 6.9 introduit un système de cache des attributs intelligent :

class Bloc_Liste_Articles extends WP_Block_Builder {
    public function render($attributes, $content) {
        // Cache automatique basé sur les attributs
        return $this->cached_render(function() use ($attributes) {
            $query = new WP_Query([
                'post_type' => $attributes['post_type'],
                'posts_per_page' => $attributes['count']
            ]);
            
            return $this->format_posts($query->posts);
        }, $attributes);
    }
    
    // TTL du cache défini automatiquement
    protected function get_cache_duration() {
        return 300; // 5 minutes
    }
}

La méthode cached_render() gère automatiquement la mise en cache en fonction des attributs du bloc. Plus besoin de gérer manuellement les transients WordPress !

Et côté requêtes, la nouvelle API optimise aussi les appels à la base de données. Les blocs similaires sur une même page partagent maintenant leurs requêtes quand c’est possible.

Compatibilité et migration depuis les versions précédentes

Bonne nouvelle : vos anciens blocs continuent de fonctionner ! WordPress maintient la rétrocompatibilité avec register_block_type(). Néanmoins, pour profiter des nouvelles fonctionnalités, une migration est recommandée.

Voici le processus que j’ai suivi sur mes projets :

  1. Créer la nouvelle classe en étendant WP_Block_Builder
  2. Migrer les attributs vers la méthode get_attributes()
  3. Adapter la logique de rendu dans la méthode render()
  4. Tester en parallèle avec l’ancienne version
  5. Remplacer l’enregistrement par register_block_builder()

Attention cependant : certains hooks spécifiques à l’ancienne API ne fonctionnent plus avec la nouvelle. Il faut adapter le code en conséquence, notamment pour les filtres sur les attributs.

La migration reste optionnelle, mais franchement, les gains en performance et en maintenabilité valent le coup !

Implémentation pratique : créer son premier bloc dynamique avec les nouvelles API

Allez, on va rentrer dans le vif du sujet ! Créer son premier bloc dynamique avec WordPress 6.9, c’est finalement plus simple qu’on pourrait le croire. L’idée, c’est de construire un bloc qui affiche les derniers articles d’une catégorie spécifique – un classique, mais efficace pour comprendre les mécanismes.

Structure des fichiers et configuration initiale

Première étape : organiser notre structure de fichiers correctement. Dans votre dossier de plugin (ou thème), vous devrez créer cette arborescence :

mon-bloc-dynamique/
├── block.json
├── index.php
├── src/
│   ├── edit.js
│   ├── save.js
│   └── index.js
└── build/ (généré par wp-scripts)

Le fichier block.json est le cœur de votre bloc. C’est lui qui définit toutes les métadonnées :

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "mon-plugin/articles-recents",
  "version": "0.1.0",
  "title": "Articles récents dynamiques",
  "category": "widgets",
  "icon": "list-view",
  "description": "Affiche les derniers articles d'une catégorie",
  "supports": {
    "html": false
  },
  "attributes": {
    "categoryId": {
      "type": "number",
      "default": 0
    },
    "postsToShow": {
      "type": "number",
      "default": 3
    }
  },
  "textdomain": "mon-plugin",
  "editorScript": "file:./build/index.js",
  "render": "file:./render.php"
}

Notez l’attribut render qui pointe vers notre fichier PHP – c’est là que la magie des nouvelles API opère !

Fonctions de rendu côté serveur avec WP_Block_Builder

Dans votre index.php, on va utiliser la nouvelle classe WP_Block_Builder pour gérer le rendu :

<?php
// Enregistrement du bloc
function register_dynamic_posts_block() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'register_dynamic_posts_block' );

// Classe pour gérer le bloc dynamique
class Dynamic_Posts_Block extends WP_Block_Builder {
    
    public function __construct() {
        parent::__construct();
        // Configuration du cache automatique
        $this->enable_cache = true;
        $this->cache_duration = 300; // 5 minutes
    }
    
    protected function get_posts_data( $attributes ) {
        // Extraction des attributs avec valeurs par défaut
        $category_id = isset( $attributes['categoryId'] ) ? intval( $attributes['categoryId'] ) : 0;
        $posts_count = isset( $attributes['postsToShow'] ) ? intval( $attributes['postsToShow'] ) : 3;
        
        // Arguments de la requête
        $args = array(
            'post_type' => 'post',
            'post_status' => 'publish',
            'posts_per_page' => $posts_count,
            'meta_key' => '_thumbnail_id' // On veut uniquement les posts avec image
        );
        
        // Ajout de la catégorie si spécifiée
        if ( $category_id > 0 ) {
            $args['cat'] = $category_id;
        }
        
        return new WP_Query( $args );
    }
    
    public function render_block( $attributes, $content, $block_instance ) {
        $query = $this->get_posts_data( $attributes );
        
        if ( ! $query->have_posts() ) {
            return '<p>Aucun article trouvé pour cette catégorie.</p>';
        }
        
        $output = '<div class="dynamic-posts-block">';
        
        while ( $query->have_posts() ) {
            $query->the_post();
            $output .= '<article class="dynamic-post-item">';
            $output .= '<h3><a href="' . get_permalink() . '">' . get_the_title() . '</a></h3>';
            $output .= '<div class="post-excerpt">' . get_the_excerpt() . '</div>';
            $output .= '</article>';
        }
        
        $output .= '</div>';
        
        wp_reset_postdata();
        return $output;
    }
}

// Instantiation de notre classe
new Dynamic_Posts_Block();

Gestion des attributs et interface d’édition

Côté JavaScript, dans votre fichier edit.js, on va créer l’interface pour configurer le bloc :

import { __ } from '@wordpress/i18n';
import { 
    PanelBody, 
    SelectControl, 
    RangeControl 
} from '@wordpress/components';
import { 
    InspectorControls, 
    useBlockProps 
} from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { ServerSideRender } from '@wordpress/server-side-render';

export default function Edit({ attributes, setAttributes }) {
    const { categoryId, postsToShow } = attributes;
    
    // Récupération des catégories via l'API WordPress
    const categories = useSelect((select) => {
        return select('core').getEntityRecords('taxonomy', 'category', {
            per_page: -1,
        });
    }, []);
    
    const blockProps = useBlockProps();
    
    // Options pour le sélecteur de catégorie
    const categoryOptions = [
        { label: 'Toutes les catégories', value: 0 },
        ...(categories || []).map((category) => ({
            label: category.name,
            value: category.id,
        })),
    ];
    
    return (
        <div {...blockProps}>
            <InspectorControls>
                <PanelBody title={__('Paramètres du bloc', 'mon-plugin')}>
                    <SelectControl
                        label={__('Catégorie', 'mon-plugin')}
                        value={categoryId}
                        options={categoryOptions}
                        onChange={(value) => setAttributes({ categoryId: parseInt(value) })}
                    />
                    <RangeControl
                        label={__('Nombre d\'articles', 'mon-plugin')}
                        value={postsToShow}
                        onChange={(value) => setAttributes({ postsToShow: value })}
                        min={1}
                        max={10}
                    />
                </PanelBody>
            </InspectorControls>
            
            <ServerSideRender
                block="mon-plugin/articles-recents"
                attributes={attributes}
                LoadingResponsePlaceholder={() => (
                    <div>Chargement des articles...</div>
                )}
                ErrorResponsePlaceholder={() => (
                    <div>Erreur lors du chargement des articles</div>
                )}
            />
        </div>
    );
}

Et pour le fichier save.js, c’est simple : on retourne null puisque le rendu est géré côté serveur !

export default function save() {
    return null; // Rendu dynamique côté serveur
}

États réactifs et interaction avec la base de données

L’un des points forts des nouvelles API, c’est la gestion automatique du cache et des états réactifs. Quand un attribut change dans l’éditeur, le ServerSideRender se met à jour automatiquement.

Pour aller plus loin, on peut ajouter une gestion d’état plus sophistiquée :

import { useState, useEffect } from '@wordpress/element';

export default function Edit({ attributes, setAttributes }) {
    const [isLoading, setIsLoading] = useState(false);
    const [hasError, setHasError] = useState(false);
    
    // Mise à jour de l'état quand les attributs changent
    useEffect(() => {
        setIsLoading(true);
        setHasError(false);
        
        // Simulation d'un délai de chargement
        const timer = setTimeout(() => {
            setIsLoading(false);
        }, 500);
        
        return () => clearTimeout(timer);
    }, [attributes.categoryId, attributes.postsToShow]);
    
    // ... reste du code
}

Bon, j’avoue que c’est déjà pas mal pour un premier bloc ! Dans l’éditeur Gutenberg, vous verrez maintenant votre bloc apparaître dans la catégorie « Widgets », avec un panneau de configuration sur la droite permettant de choisir la catégorie et le nombre d’articles à afficher.

Le rendu côté serveur se met à jour en temps réel grâce au ServerSideRender, et le cache automatique de WP_Block_Builder optimise les performances. Plutôt élégant, non ?

Cas d’usage avancés et optimisations

Maintenant qu’on maîtrise les bases des blocs dynamiques WordPress 6.9, il est temps d’aborder les aspects plus pointus. Car soyons honnêtes : dans la vraie vie, on ne se contente pas d’afficher quelques articles… On a des besoins spécifiques, des performances à optimiser, et des données sensibles à protéger !

Intégration avec les Custom Post Types

L’un des gros avantages des blocs dynamiques, c’est leur capacité à interagir avec vos Custom Post Types. Avec WordPress 6.9, cette intégration devient encore plus fluide grâce aux nouvelles méthodes de WP_Block_Builder.

Voici comment créer un bloc qui affiche vos produits personnalisés :

class Product_Grid_Block extends WP_Block_Builder {
    protected function get_posts_data($attributes) {
        $args = [
            'post_type' => 'product',
            'post_status' => 'publish',
            'posts_per_page' => $attributes['count'] ?? 6,
            'meta_query' => [
                [
                    'key' => 'featured_product',
                    'value' => $attributes['featured_only'] ? '1' : '',
                    'compare' => $attributes['featured_only'] ? '=' : '!='
                ]
            ]
        ];
        
        return get_posts($args);
    }
}

Attention cependant : pensez toujours à vérifier que votre CPT existe avant de l’utiliser. Un simple post_type_exists('product') peut vous éviter bien des erreurs !

Gestion des requêtes complexes et mise en cache

Quand on travaille avec des blocs dynamiques, les performances deviennent rapidement critiques. WordPress 6.9 intègre un système de cache intelligent, mais on peut (et on doit) l’optimiser davantage.

Pour les requêtes complexes, j’utilise souvent cette approche :

protected function get_cached_results($cache_key, $query_args) {
    $cached = wp_cache_get($cache_key, 'dynamic_blocks');
    
    if (false === $cached) {
        $results = new WP_Query($query_args);
        wp_cache_set($cache_key, $results->posts, 'dynamic_blocks', 3600);
        return $results->posts;
    }
    
    return $cached;
}

Bon conseil : utilisez des clés de cache spécifiques qui incluent les paramètres de votre requête. Quelque chose comme "product_grid_{$category_id}_{$count}_{$featured}" par exemple.

Pour les sites à fort trafic, pensez également aux solutions de cache avancé comme Redis ou Memcached. Et n’oubliez pas Query Monitor pour identifier les requêtes problématiques !

Bonnes pratiques de sécurité et validation des données

La sécurité, c’est LE point qu’on ne peut pas négliger. Avec les blocs dynamiques, on manipule souvent des données utilisateur, et là… attention aux failles !

Première règle : validez TOUT ce qui vient de l’extérieur :

protected function sanitize_attributes($attributes) {
    return [
        'count' => absint($attributes['count'] ?? 6),
        'category' => sanitize_text_field($attributes['category'] ?? ''),
        'show_excerpt' => (bool) ($attributes['show_excerpt'] ?? false),
        'custom_query' => wp_kses($attributes['custom_query'] ?? '', [])
    ];
}

Pour les champs HTML, utilisez wp_kses() avec une whitelist stricte. Et pour les requêtes SQL personnalisées (si vous en avez), $wpdb->prepare() est votre meilleur ami.

Dernière chose importante : n’exposez jamais d’informations sensibles dans les attributs des blocs. Ces données sont stockées en base et potentiellement visibles côté client !

Tests et débogage des blocs dynamiques

Le débogage des blocs dynamiques peut vite devenir un casse-tête. Heureusement, on a quelques outils qui peuvent nous sauver la mise !

Query Monitor reste l’extension incontournable pour analyser les performances. Elle vous montrera exactement quelles requêtes sont lancées par vos blocs, leur temps d’exécution, et les éventuels problèmes de cache.

Pour le debug en local, j’aime bien cette petite fonction :

protected function debug_block_data($data, $context = '') {
    if (defined('WP_DEBUG') && WP_DEBUG) {
        error_log(sprintf(
            '[Block Debug] %s: %s',
            $context,
            print_r($data, true)
        ));
    }
}

Et pour les tests automatisés ? PHPUnit reste la référence, mais pensez aussi à tester vos blocs dans l’éditeur Gutenberg. Parfois, ce qui fonctionne en PHP plante côté JavaScript… Et croyez-moi, c’est toujours au pire moment qu’on s’en aperçoit !