Il problema / Il bisogno

Se hai già consegnato un sito Elementor e un cliente ti chiede "Voglio un widget" fatto su misura, ma senza installare 12 componenti aggiuntivi”, hai già il riflesso giusto: dai un'occhiata al codice.

Elementor (Free/Pro) espone un'API sufficientemente stabile di hook ed eventi per personalizzare a fondo il builder: aggiungere una categoria di widget, salvare un widget personalizzato, inserire controlli, forzare valori predefiniti, caricare script solo quando necessario e persino aggiungere un campo dinamico tramite un tag.

L'esigenza aziendale tipica: industrializzare componenti riutilizzabili (CTA, box autore, schede prodotto, tabelle, banner GDPR, ecc.) mantenendo l'esperienza utente di Elementor. Al termine, saprai come strutturare un mini-plugin pulito e compatibile. WordPress Richiede 6.9.4 o versioni successive e PHP 8.1 o versioni successive, e disporrà di una base riutilizzabile per i suoi progetti.

Riassunto veloce

  • Crea un mini-plugin WordPress (non uno snippet usa e getta) che si integra con Elementor senza rompere ilAdmin.
  • Utilizza gli hook di Elementor corretti: elementor/init, elementor/widgets/register, elementor/elements/categories_registered, elementor/frontend/after_register_scripts.
  • Aggiungi un widget "Badge" personalizzato (titolo + testo + colore + icona) con controlli, rendering sicuro e stili.
  • Aggiungi un tag dinamico (opzione avanzata) per inserire un valore dai metadati dell'utente (ad esempio, posizione/ruolo).
  • Carica i file CSS/JS solo se è presente un widget del tuo plugin (evitando così di "caricare tutto ovunque").

Quando utilizzare questa soluzione

  • Hai bisogno di un componente stabile e versionato che possa essere riutilizzato su più siti (agenzia, freelance, team).
  • È necessario attenersi a una guida di stile (colori, tipografia, spaziatura) senza lasciare al cliente 50 opzioni "pericolose".
  • Hai bisogno di un rendering front-end accurato senza dover dipendere da un componente aggiuntivo di terze parti che può cambiare senza preavviso.
  • Desideri migliorare le prestazioni: le risorse vengono caricate solo quando necessario, evitando un "pacchetto" di widget di grandi dimensioni.
  • Desideri integrare i dati di WordPress (meta, opzioni, ACF/Pods, ecc.) tramite tag dinamici.

Quando NON utilizzare questa soluzione

  • La necessità è puramente visiva e occasionale: un semplice Un template Elementor, un contenitore e un po' di CSS potrebbero essere sufficienti.
  • Non hai alcun controllo sulla manutenzione: un widget personalizzato implica seguire Elementor (e talvolta le sue deprecazioni).
  • Se intendi "modificare" Elementor in modo sostanziale (ad esempio, modificandone il comportamento interno), tieni presente che raramente il risultato è stabile. Opta per le estensioni ufficiali oppure accetta un certo grado di debito tecnico.
  • Il tuo cliente utilizza principalmente Gutenberg/blocchi: in questo caso, un blocco personalizzato (API dei blocchi) è spesso più appropriato. Consulta la documentazione ufficiale: Manuale dell'editor a blocchi.

Prerequisiti / prima di iniziare

  • WordPress 6.9.4+ e PHP 8.1+ (idealmente 8.2/8.3 nel 2026 se il tuo provider di hosting è in grado di fornire aggiornamenti).
  • Elementor è installato e attivato (la versione gratuita è sufficiente per questo esempio di widget). Per i tag dinamici avanzati, spesso si utilizza Elementor Pro, ma preferiamo utilizzare le API pubbliche ogni volta che è possibile.
  • Un ambiente di staging e un backup sono essenziali prima di apportare qualsiasi modifica. Ho spesso visto frammenti di codice incollati nell'ambiente di produzione che causavano un errore irreversibile e bloccavano l'interfaccia di amministrazione.
  • Un plugin per i log (o almeno WP_DEBUG_LOG) per leggere gli errori PHP.

Riferimenti utili a WordPress:

L'approccio ingenuo (e perché evitarlo)

L'approccio classico: incollare un grosso blocco di codice in functions.php del tema (spesso senza un tema figlio), salva gli script ovunque e istanzia le classi di Elementor al caricamento.

Esempio tipico (anti-pattern)

<?php
// ❌ Exemple volontairement mauvais : ne copiez pas tel quel.

add_action('init', function () {
    // ❌ Elementor n'est pas forcément chargé ici, et cette classe peut ne pas exister.
    $widgets_manager = ElementorPlugin::instance()->widgets_manager;

    require_once __DIR__ . '/widgets/badge.php';
    $widgets_manager->register(new My_Badge_Widget());

    // ❌ Charge CSS/JS sur toutes les pages, même si le widget n'est pas utilisé.
    wp_enqueue_style('my-badge', get_stylesheet_directory_uri() . '/badge.css');
});

Perché si rompe (spesso)

  • Sincronizzazione Elementor non aveva ancora terminato l'inizializzazione dei suoi gestori al momento init (a seconda delle versioni/contesti).
  • errore fatale : se Elementor è disabilitato, ElementorPlugin non esiste.
  • Cookie di prestazione : CSS/JS caricati ovunque, anche su pagine che non utilizzano Elementor.
  • Manutenzione : codice perso nel tema, impossibile da versionare correttamente, fragile quando si cambiano i temi.

L'approccio corretto: tutorial passo passo

Realizzeremo un mini-plugin con:

  • un bootstrap che controlla se Elementor è attivo,
  • una categoria dedicata di widget,
  • un widget personalizzato,
  • un carico patrimoniale condizionale,
  • una variante opzionale "Tag dinamico" per illustrare un filtro/registro avanzato.

Passaggio 1: crea il plugin

Crea questa cartella: wp-content/plugins/bpcab-elementor-hooks/

Quindi questo file: wp-content/plugins/bpcab-elementor-hooks/bpcab-elementor-hooks.php

Passaggio 2: verifica di Bootstrap + Elementor

Alleghiamo il nostro codice a plugins_loaded poi aspettiamo elementor/initIl punto chiave: non richiamare mai le classi di Elementor finché il plugin non è pronto.

Passaggio 3: Salva una categoria + un widget

Elementor espone azioni dedicate. In pratica, queste sono rimaste stabili per diverse versioni:

  • elementor/elements/categories_registered per aggiungere una categoria,
  • elementor/widgets/register per salvare un widget.

Insisto: evitate di usare hook "a caso" (come init ou wp_loaded) per toccare Elementor. Il problema raramente deriva dal codice del widget, ma dal momento in cui viene eseguito.

Passaggio 4: Caricare CSS/JS al momento giusto

Le attività vengono registrate tramite elementor/frontend/after_register_styles / elementor/frontend/after_register_scriptspoi noi enqueue solo se il widget viene effettivamente visualizzato.

Passaggio 5 — (Facoltativo) Aggiungi un tag dinamico

Se utilizzi Elementor Pro (o se il tuo stack supporta i tag dinamici), un tag personalizzato è spesso una soluzione più pulita rispetto a uno shortcode. Tu rendi disponibili i dati ed Elementor si occupa di inserirli nei suoi controlli "Dinamici".

Codice completo

Copia e incolla tutto il codice qui sotto. Il plugin è autonomo e potrai aggiungere widget in seguito.

File 1 — bpcab-elementor-hooks.php

<?php
/**
 * Plugin Name: BPCAB - Personnalisation Elementor par hooks
 * Description: Exemple pédagogique : catégorie + widget custom + assets conditionnels + (option) Dynamic Tag.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 * Author: BPCAB
 * License: GPLv2 or later
 */

declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

final class BPCAB_Elementor_Hooks_Plugin {

	public const VERSION = '1.0.0';
	public const SLUG    = 'bpcab-elementor-hooks';

	private static ?self $instance = null;

	public static function instance(): self {
		if (null === self::$instance) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		add_action('plugins_loaded', [$this, 'bootstrap']);
	}

	public function bootstrap(): void {
		// Elementor définit généralement ELEMENTOR_VERSION quand il est actif.
		if (!defined('ELEMENTOR_VERSION')) {
			// Pas d'Elementor : on ne fait rien. Évitez d'afficher une notice agressive en front.
			add_action('admin_notices', [$this, 'admin_notice_missing_elementor']);
			return;
		}

		// On attend l'initialisation d'Elementor avant d'appeler ses classes/managers.
		add_action('elementor/init', [$this, 'on_elementor_init']);
	}

	public function admin_notice_missing_elementor(): void {
		if (!current_user_can('activate_plugins')) {
			return;
		}

		$plugin_name = esc_html__('BPCAB - Personnalisation Elementor par hooks', 'bpcab');
		$message     = esc_html__('Elementor doit être activé pour utiliser ce plugin.', 'bpcab');

		echo '<div class="notice notice-warning">';
		echo '<p><strong>' . $plugin_name . '</strong> — ' . $message . '</p>';
		echo '</div>';
	}

	public function on_elementor_init(): void {
		// 1) Catégorie de widgets.
		add_action('elementor/elements/categories_registered', [$this, 'register_category']);

		// 2) Widgets.
		add_action('elementor/widgets/register', [$this, 'register_widgets']);

		// 3) Assets : on les enregistre au bon moment côté front.
		add_action('elementor/frontend/after_register_styles', [$this, 'register_frontend_styles']);
		add_action('elementor/frontend/after_register_scripts', [$this, 'register_frontend_scripts']);

		// 4) Option : Dynamic Tag (si l'API est disponible).
		add_action('elementor/dynamic_tags/register', [$this, 'register_dynamic_tags']);
	}

	public function register_category($elements_manager): void {
		// $elements_manager est typiquement une instance de ElementorElements_Manager.
		$elements_manager->add_category(
			'bpcab',
			[
				'title' => esc_html__('BPCAB', 'bpcab'),
				'icon'  => 'fa fa-plug',
			]
		);
	}

	public function register_widgets($widgets_manager): void {
		// Chargement des classes de widgets.
		require_once __DIR__ . '/includes/widgets/class-bpcab-widget-badge.php';

		// Enregistrement.
		$widgets_manager->register(new BPCAB_Widget_Badge());
	}

	public function register_frontend_styles(): void {
		wp_register_style(
			'bpcab-badge',
			plugins_url('assets/css/badge.css', __FILE__),
			[],
			self::VERSION
		);
	}

	public function register_frontend_scripts(): void {
		wp_register_script(
			'bpcab-badge',
			plugins_url('assets/js/badge.js', __FILE__),
			[],
			self::VERSION,
			true
		);
	}

	public function register_dynamic_tags($dynamic_tags_manager): void {
		// Certains sites n'utilisent pas cette feature : on protège le require.
		require_once __DIR__ . '/includes/dynamic-tags/class-bpcab-dynamic-tag-user-position.php';

		// Enregistrement du tag.
		$dynamic_tags_manager->register(new BPCAB_Dynamic_Tag_User_Position());
	}
}

BPCAB_Elementor_Hooks_Plugin::instance();

File 2 — includes/widgets/class-bpcab-widget-badge.php

<?php
declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

use ElementorWidget_Base;
use ElementorControls_Manager;
use ElementorIcons_Manager;

final class BPCAB_Widget_Badge extends Widget_Base {

	public function get_name(): string {
		return 'bpcab_badge';
	}

	public function get_title(): string {
		return esc_html__('Badge (BPCAB)', 'bpcab');
	}

	public function get_icon(): string {
		return 'eicon-badge';
	}

	public function get_categories(): array {
		return ['bpcab'];
	}

	public function get_keywords(): array {
		return ['badge', 'label', 'cta', 'bpcab'];
	}

	public function get_style_depends(): array {
		// Elementor enqueuera ce style seulement si le widget est présent.
		return ['bpcab-badge'];
	}

	public function get_script_depends(): array {
		// Idem pour le script.
		return ['bpcab-badge'];
	}

	protected function register_controls(): void {

		$this->start_controls_section(
			'section_content',
			[
				'label' => esc_html__('Contenu', 'bpcab'),
				'tab'   => Controls_Manager::TAB_CONTENT,
			]
		);

		$this->add_control(
			'title',
			[
				'label'       => esc_html__('Titre', 'bpcab'),
				'type'        => Controls_Manager::TEXT,
				'default'     => esc_html__('Nouveau', 'bpcab'),
				'placeholder' => esc_html__('Ex: Nouveau', 'bpcab'),
				'label_block' => true,
			]
		);

		$this->add_control(
			'text',
			[
				'label'       => esc_html__('Texte', 'bpcab'),
				'type'        => Controls_Manager::TEXTAREA,
				'default'     => esc_html__('Offre limitée', 'bpcab'),
				'placeholder' => esc_html__('Ex: Offre limitée', 'bpcab'),
				'rows'        => 3,
			]
		);

		$this->add_control(
			'icon',
			[
				'label'   => esc_html__('Icône', 'bpcab'),
				'type'    => Controls_Manager::ICONS,
				'default' => [
					'value'   => 'fas fa-star',
					'library' => 'fa-solid',
				],
			]
		);

		$this->end_controls_section();

		$this->start_controls_section(
			'section_style',
			[
				'label' => esc_html__('Style', 'bpcab'),
				'tab'   => Controls_Manager::TAB_STYLE,
			]
		);

		$this->add_control(
			'bg_color',
			[
				'label'     => esc_html__('Couleur de fond', 'bpcab'),
				'type'      => Controls_Manager::COLOR,
				'default'   => '#111827',
				'selectors' => [
					'{{WRAPPER}} .bpcab-badge' => 'background-color: {{VALUE}};',
				],
			]
		);

		$this->add_control(
			'text_color',
			[
				'label'     => esc_html__('Couleur du texte', 'bpcab'),
				'type'      => Controls_Manager::COLOR,
				'default'   => '#ffffff',
				'selectors' => [
					'{{WRAPPER}} .bpcab-badge' => 'color: {{VALUE}};',
				],
			]
		);

		$this->add_responsive_control(
			'padding',
			[
				'label'      => esc_html__('Padding', 'bpcab'),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => ['px', 'em', 'rem'],
				'default'    => [
					'top'    => 12,
					'right'  => 14,
					'bottom' => 12,
					'left'   => 14,
					'unit'   => 'px',
				],
				'selectors'  => [
					'{{WRAPPER}} .bpcab-badge' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				],
			]
		);

		$this->end_controls_section();
	}

	protected function render(): void {
		$settings = $this->get_settings_for_display();

		// Sanitization/escaping : Elementor stocke des valeurs, mais vous devez sortir du HTML propre.
		$title = isset($settings['title']) ? sanitize_text_field((string) $settings['title']) : '';
		$text  = isset($settings['text']) ? wp_kses_post((string) $settings['text']) : '';

		// Icône : Elementor fournit Icons_Manager pour rendre proprement.
		$icon = $settings['icon'] ?? null;

		echo '<div class="bpcab-badge" role="note">';
		echo '<div class="bpcab-badge__head">';

		if (!empty($icon) && is_array($icon)) {
			echo '<span class="bpcab-badge__icon" aria-hidden="true">';
			Icons_Manager::render_icon($icon, ['aria-hidden' => 'true']);
			echo '</span>';
		}

		if ($title !== '') {
			echo '<strong class="bpcab-badge__title">' . esc_html($title) . '</strong>';
		}

		echo '</div>';

		if ($text !== '') {
			// wp_kses_post permet un sous-ensemble HTML (liens, strong, em, etc.).
			echo '<div class="bpcab-badge__text">' . $text . '</div>';
		}

		echo '</div>';
	}
}

File 3 — includes/dynamic-tags/class-bpcab-dynamic-tag-user-position.php

<?php
declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

use ElementorCoreDynamicTagsTag;

final class BPCAB_Dynamic_Tag_User_Position extends Tag {

	public function get_name(): string {
		return 'bpcab-user-position';
	}

	public function get_title(): string {
		return esc_html__('Utilisateur : Poste (BPCAB)', 'bpcab');
	}

	public function get_group(): string {
		// Groupe "Site" ou "User" selon votre organisation.
		return 'site';
	}

	public function get_categories(): array {
		// Catégorie TEXT pour insertion dans des champs texte.
		return [ElementorModulesDynamicTagsModule::TEXT_CATEGORY];
	}

	protected function register_controls(): void {
		// Exemple : choisir une meta key (simple). En prod, vous pourriez proposer une liste.
		$this->add_control(
			'meta_key',
			[
				'label'       => esc_html__('Meta key utilisateur', 'bpcab'),
				'type'        => ElementorControls_Manager::TEXT,
				'default'     => 'position',
				'placeholder' => 'position',
			]
		);
	}

	public function render(): void {
		$user_id = get_current_user_id();
		if (!$user_id) {
			return;
		}

		$settings = $this->get_settings();
		$meta_key = isset($settings['meta_key']) ? sanitize_key((string) $settings['meta_key']) : 'position';

		$value = get_user_meta($user_id, $meta_key, true);
		if (!is_scalar($value) || $value === '') {
			return;
		}

		echo esc_html((string) $value);
	}
}

File 4 — assets/css/badge.css

.bpcab-badge{
	display:block;
	border-radius:12px;
	line-height:1.35;
}
.bpcab-badge__head{
	display:flex;
	gap:10px;
	align-items:center;
}
.bpcab-badge__icon{
	display:inline-flex;
}
.bpcab-badge__title{
	font-weight:700;
}
.bpcab-badge__text{
	margin-top:8px;
	opacity:.95;
}

File 5 — assets/js/badge.js

(function () {
	// Script minimal : exemple de point d'accroche.
	// Ici, on ne fait rien de critique. Gardez vos widgets robustes sans JS si possible.
})();

Codice Spiegazione

1) Perché un plugin (e non functions.php)?

Un plugin ti offre un ciclo di vita chiaro, attivazione/disattivazione, versioning e una posizione stabile per le tue classi. Ho spesso visto siti Avada/Divi aggiornati, in cui un piccolo frammento di codice nel tema scompare o diventa incompatibile.

2) Il punto chiave: la tempistica degli agganci

  • plugins_loaded WordPress ha caricato i plugin. Possiamo verificare se Elementor è presente.
  • elementor/init Elementor ha inizializzato il suo contenitore principale. È qui che puoi aggiungere i tuoi hook di Elementor.
  • elementor/widgets/register : ricevi il gestore dei widget e salvi le tue classi.
  • elementor/elements/categories_registered : dichiari una categoria visibile nell'interfaccia utente del builder.

Questa analisi evita il classico bug: "Classe 'ElementorPlugin' non trovata" o "Chiamata al metodo register() su un valore nullo".

3) Caricamento condizionale delle attività

Il duo get_style_depends() / get_script_depends() è sottoutilizzato. Eppure, è uno dei modi più puliti per caricare le risorse solo quando Elementor esegue il rendering del widget.

Dietro le quinte: Elementor raccoglie le dipendenze dei widget presenti nella pagina e interroga i relativi handle. Tu devi solo... wp_register_style() / wp_register_script() al momento giusto.

4) Rendering sicuro: sanificazione + escape

  • Entrée Elementor memorizza i valori nel database. È comunque necessario ripulirli a seconda del contesto.
  • Produzione : esc_html() per il testo, wp_kses_post() se si consente l'HTML limitato.

La trappola che vedo più spesso: andare dritto $settings['text'] sans wp_kses_post() "Perché è l'amministratore." Su un sito con più autori, questo si traduce in un rischio di XSS.

5) Tag dinamico: perché è utile

Un tag dinamico elimina la necessità di shortcode nei campi di Elementor. Si espongono i dati (meta utente, opzioni, campi ACF) e l'utente seleziona il tag nell'interfaccia utente. Questo rende il codice più facile da gestire rispetto a uno shortcode incollato in 30 widget.

Varianti e casi d'uso

Variante 1 — Forza valori “bloccati” (meno opzioni per il cliente)

Se desideri impedire al cliente di modificare determinate opzioni, puoi:

  • non esporre il controllo (no add_control),
  • o esporre un elenco chiuso (SELECT),
  • o imporre un valore in render().

Esempio: imporre una classe CSS in base a un “tipo”:

// Dans register_controls()
$this->add_control(
	'type',
	[
		'label'   => esc_html__('Type', 'bpcab'),
		'type'    => Controls_Manager::SELECT,
		'default' => 'info',
		'options' => [
			'info'    => esc_html__('Info', 'bpcab'),
			'warning' => esc_html__('Alerte', 'bpcab'),
		],
	]
);

// Dans render()
$type = isset($settings['type']) ? sanitize_key((string) $settings['type']) : 'info';
$type_class = in_array($type, ['info', 'warning'], true) ? 'is-' . $type : 'is-info';
echo '<div class="bpcab-badge ' . esc_attr($type_class) . '">...</div>';

Variante 2: aggiungi un controllo URL e crea un collegamento pulito

Un badge CTA cliccabile appare sempre. Elementor fornisce un controllo URL con le opzioni "apri in una nuova scheda" e "nofollow".

// Contrôle URL
$this->add_control(
	'link',
	[
		'label' => esc_html__('Lien', 'bpcab'),
		'type'  => Controls_Manager::URL,
		'options' => ['url', 'is_external', 'nofollow'],
		'default' => [
			'url' => '',
		],
	]
);

// Dans render()
$link = $settings['link'] ?? [];
$url  = isset($link['url']) ? esc_url((string) $link['url']) : '';

if ($url) {
	$target = !empty($link['is_external']) ? ' target="_blank"' : '';
	$rel    = !empty($link['nofollow']) ? ' rel="nofollow noopener"' : ' rel="noopener"';

	echo '<a class="bpcab-badge" href="' . $url . '"' . $target . $rel . '>...</a>';
	return;
}

Nota: se apri un collegamento in una nuova scheda, mantieni noopener (sicurezza).

Variante 3: Carica una risorsa solo su determinate pagine (ancora più restrittiva)

Se hai uno script complesso, puoi combinare la dipendenza del widget con una condizione di WordPress. Ad esempio: solo sulle pagine (non il news):

public function register_frontend_scripts(): void {
	wp_register_script(
		'bpcab-badge',
		plugins_url('assets/js/badge.js', __FILE__),
		[],
		self::VERSION,
		true
	);

	// ⚠️ Ne faites pas wp_enqueue_script ici : Elementor gère l'enqueue via get_script_depends().
	// Si vous voulez vraiment empêcher le chargement sur certains contextes, vous pouvez deregister :
	if (!is_page()) {
		wp_deregister_script('bpcab-badge');
	}
}

Lo uso raramente: può essere sorprendente se un widget viene utilizzato in un template che viene visualizzato altrove. Testalo accuratamente.

Compatibilità con Divi 5, Elementor e Avada

Elementor

  • Il plugin sopra descritto si integra "in" Elementor, senza dipendere da un tema.
  • Se utilizzi i template di Elementor (Theme Builder), il widget rimane disponibile ovunque.
  • Le risorse sono condizionali: un aspetto positivo per i siti molto frequentati.

Divi 5

Divi 5 non utilizza l'API di Elementor. Il tuo widget non verrà visualizzato in Divi, ed è normale.

Se il tuo obiettivo è riutilizzare lo stesso componente nelle pagine Divi, ti consiglio una strategia "indipendente dal costruttore":

  • crea uno shortcode di WordPress (o meglio: un blocco Gutenberg),
  • quindi inseriscilo in Divi tramite un modulo Codice/Shortcode,
  • e mantieni Elementor come "sovrapposizione dell'interfaccia utente" quando è presente.

In base alla mia esperienza, questo è l'unico approccio che funziona se si ha un parco con più costruttori.

Avada (Fusion Builder)

Vale la stessa logica: Avada non utilizzerà i widget di Elementor. Tuttavia, il tuo plugin rimane utile se il sito utilizza Elementor in alcune pagine.

Per Avada, il modello più pulito è anche il seguente: shortcode o blocco, quindi elemento "Blocco di codice" / "Shortcode" in Fusion Builder.

Controlli post-installazione

  1. Attiva il plugin in Estensioni.
  2. Apri una pagina con Elementor.
  3. Nel pannello dei widget, cerca la categoria BPCAB quindi il widget Distintivo (BPCAB).
  4. Inseriscilo nella pagina, modifica il titolo, il testo e i colori, e pubblica.
  5. Sul fronte, ispeziona la pagina: dovresti vedere bpcab-badge.css caricato (e non su pagine che non utilizzano il widget).

Tabella diagnostica rapida

sintomo Probabile causa Verifica Soluzione
La categoria BPCAB non compare Il hook non è mai stato eseguito (Elementor non caricato) Controlla che ELEMENTOR_VERSION è definito ed Elementor è attivo Attiva Elementor e verifica la presenza di conflitti/plugin mu.
Errore irreversibile "Classe ElementorWidget_Base non trovata" Il file del widget è stato caricato troppo presto / Elementor è inattivo Visualizza il log PHP e la traccia dello stack È necessario solo il widget in elementor/widgets/register après elementor/init
CSS/JS non caricati Handle non registrati o hook di registrazione errato ispezionare wp_head / wp_footer + console Verifier after_register_styles/scripts et get_style_depends()
Il widget viene visualizzato, ma gli stili sono danneggiati. Memorizzazione nella cache (plugin/CDN) o minimizzazione aggressiva Disabilita la cache, cancella la CDN, prova in modalità di navigazione privata Escludi i file dalla cache/minimizza, aggiorna la versione
Tag dinamico non trovato Funzionalità non disponibile (a seconda della configurazione/versione Pro) o hook non attivato. Verifica se il pannello "Dinamico" è presente in un campo di testo Installa/attiva Elementor Pro se necessario, oppure rimuovi la sezione tag

Se non funziona

  1. Conferma le versioni WordPress 6.9.4+, PHP 8.1+, Elementor aggiornato. Una versione obsoleta di PHP sta causando errori su declare(strict_types=1) o i tipi ?self.
  2. Abilita la registrazione in wp-config.php (in fase di allestimento):

    define('WP_DEBUG', true);

    define('WP_DEBUG_LOG', true);

    define('WP_DEBUG_DISPLAY', false);
  3. aperto wp-content/debug.log e cerca “BPCAB” o “Elementor”.
  4. Disattivare temporaneamente Plugin per snippet. Ho già visto uno snippet "proveniente da un vecchio tutorial di Elementor" dichiarare una classe con lo stesso nome e causare un conflitto fatale.
  5. Svuota la cache : cache dei plugin, cache del server, CDN, cache del browser. Su Elementor, una cache aggressiva può anche conservare le risorse mancanti.
  6. Rigenera il CSS di Elementor (se il tuo sito utilizza l'opzione di generazione CSS). In Elementor, di solito è presente un'azione di rigenerazione nelle impostazioni Strumenti/Prestazioni.
  7. Prova con un tema neutro (temporaneo): Twenty Twenty-* o un tema leggero. Un tema può annullare la registrazione di script/stili.

Errori e trappole comuni

errore Causare Soluzione
Codice incollato nel file sbagliato Aggiunto in functions.php invece di un plugin Crea un plugin, gestisci le versioni e attivalo/disattivalo correttamente.
"Errore di analisi: errore di sintassi" Punto e virgola mancante, parentesi graffa in più, copia-incolla incompleta Esamina la riga indicata nel log, usa un IDE con formattazione PHP
Hook Elementor non adatto L'uso di init / wp_loaded per salvare un widget utiliser elementor/init puis elementor/widgets/register
"Classe ElementorPlugin non trovata" Elementor disabilitato o caricato dopo il tuo codice Verifier defined('ELEMENTOR_VERSION') e non chiamare mai Elementor prima elementor/init
CSS/JS non caricati Maniglia errata, gancio errato o cache/minimizzazione errata Salva tramite after_register_styles/scripts, dichiara le dipendenze tramite get_*_depends()svuota la cache
conflitto di nomi di classe Due plugin dichiarano BPCAB_Widget_Badge (oppure un caricatore automatico configurato in modo errato) Utilizzate sempre prefissi e namespace se state industrializzando il sistema.
Confusione tra azione e filtro Stai cercando di "tornare" a un titolo Azioni: effetti collaterali. Filtri: restituiscono un valore. Esamina l'hook utilizzato.
Test diretti in produzione Nessuna messa in scena, nessun backup Pianificazione di staging, backup e ripristino (disattivazione dei plugin tramite FTP, se necessario).
Permalink/modelli incoerenti Stai eseguendo il test su un modello diverso da quello renderizzato (Theme Builder). Verifica quale template Elementor è effettivamente applicato e svuota la cache.

Consigli su sicurezza, prestazioni e manutenzione

sicurezza

  • Fuga sistematica : tutto ciò che viene visualizzato in HTML deve essere codificato in base al contesto (esc_html, esc_attr, esc_url, wp_kses_post). Riferimento : WordPress: Validazione dei dati.
  • Nessuna opzione “HTML libero” per ruoli non amministrativi. Nei siti con più autori, questo rappresenta una vulnerabilità XSS.
  • Esecuzione NoPH tramite widget (Sembra ovvio, ma ho già visto alcuni "widget di codice" assemblati alla buona).

Cookie di prestazione

  • Attività condizionali via get_style_depends() / get_script_depends() : è il miglior rapporto sforzo/beneficio.
  • Evitare richieste ripetitive in render()Se devi caricare i dati (post, meta), memorizzali nella cache (transient/object cache) o preparali tramite una query ottimizzata.
  • CSS minimo : un widget = un piccolo file. Se ne hai 20, raggruppali in modo intelligente (ma mantieni la condizionalità).

Manutenzione

  • Versione Utilizza il plugin (Git) e assegna un tag alle tue release. Quando Elementor modifica un'API, saprai cosa distribuire.
  • Evita i tutorial “vecchi” che utilizzano hook obsoleti. Se riutilizzi uno snippet del periodo 2021-2023, assicurati che sia compatibile con le versioni attuali di Elementor e WordPress 6.9.4.
  • Preparare una strategia di riserva : se Elementor è disabilitato, il tuo plugin non dovrebbe "fare nulla" senza causare errori irreversibili.

Risorse

FAQ

Questo codice funziona con WordPress 6.9.4?

Sì: il plugin segue le pratiche standard di WordPress (hook, enqueue) ed è compatibile con PHP 8.1 e versioni successive. Il principale problema di compatibilità rimane la versione di Elementor (assicurati di mantenerla aggiornata).

Perché non utilizzare un plugin per gli snippet?

Per un test rapido va bene. Per un widget Elementor riutilizzabile, un vero plugin è più affidabile: caricamento controllato, file organizzati, gestione delle versioni e disattivazione pulita in caso di problemi.

Il mio widget viene visualizzato, ma non si trova nella categoria corretta.

Controllalo get_categories() girarsi ['bpcab']e che la categoria è registrata tramite elementor/elements/categories_registered.

Come faccio ad aggiungere più widget?

Aggiungi altri file a includes/widgets/ e salvarli in register_widgets()Un file equivale a una classe.

Come posso evitare di caricare JavaScript se non mi serve?

Elimina get_script_depends() oppure restituisci un array vuoto. Mantieni il widget funzionale senza JavaScript il più possibile.

Possiamo utilizzare un autoloader (Composer)?

Sì, soprattutto se hai più di 10 widget. In un contesto WordPress, fai attenzione a non forzare Composer sul sito finale. Un approccio comune è quello di includere un autoloader PSR-4 all'interno del plugin.

Perché usare wp_kses_post() Per il testo?

Perché un'area di testo può contenere codice HTML se Elementor lo consente (oppure se l'utente incolla del contenuto). wp_kses_post() consente un sottoinsieme sicuro, a differenza di un eco grezzo.

Il tag dinamico non viene visualizzato: è normale?

Dipende dalla configurazione di Elementor. Verifica che l'interfaccia utente "Dinamica" sia disponibile per i tuoi campi. Se il tuo sito non supporta i tag dinamici, rimuovi la parte elementor/dynamic_tags/register e il file associato.

Come posso eseguire i test correttamente senza compromettere l'editor?

Esegui i test in modalità di staging, abilita la registrazione dei log e inizia con un widget minimale (renderizzato + un controllo). Aggiungi i controlli uno alla volta. Gli errori di Elementor spesso non vengono visualizzati nell'interfaccia utente, ma sono visibili nella console e nel log PHP.

È compatibile con un tema figlio di Divi/Avada?

Sì, perché è un plugin. Tuttavia, il widget apparirà solo in Elementor. Per Divi/Avada, usa uno shortcode o un blocco se desideri un componente che possa essere condiviso tra i diversi sviluppatori.