Проблемът / Нуждата

Ако вече сте доставили сайт на Elementor и клиент ви попита „Искам уиджет“ съобразени, но без да инсталирате 12 добавки“, вече имате правилния рефлекс: прегледайте кода.

Elementor (безплатен/професионален) предоставя достатъчно стабилен API от куки и събития, за да персонализира правилно конструктора: добавяне на категория джаджи, запазване на персонализирана джаджа, инжектиране на контроли, налагане на стойности по подразбиране, зареждане на скриптове само когато е необходимо и дори добавяне на динамично поле чрез етикет.

Типичната бизнес нужда: да се индустриализират компоненти за многократна употреба (призиви към действие, полета за авторство, продуктови листове, таблици, GDPR банери и др.), като същевременно се запази потребителското изживяване на Elementor. До края ще знаете как да структурирате чист, съвместим мини-плъгин. WordPress 6.9.4+ и PHP 8.1+ и ще имате база за многократна употреба за вашите проекти.

Бързо обобщение

  • Създайте мини-плъгин WordPress (не е фрагмент за еднократна употреба), който се интегрира с Elementor, без да нарушаваадминистратор.
  • Използвайте правилните куки на Elementor: elementor/init, elementor/widgets/register, elementor/elements/categories_registered, elementor/frontend/after_register_scripts.
  • Добавете персонализиран уиджет „Значка“ (заглавие + текст + цвят + икона) с контроли, безопасно рендиране и стилове.
  • Добавете динамичен етикет (разширена опция), за да инжектирате стойност от мета данни на потребителя (напр. позиция/роля).
  • Зареждайте CSS/JS само ако е наличен уиджет от вашия плъгин (избягва се „зареждане на всичко навсякъде“).

Кога да използвате това решение

  • Искате стабилен, версиониран компонент, който може да се използва повторно в множество сайтове (агенция, фрийлансъри, екип).
  • Трябва да се придържате към стилово ръководство (цветове, типография, разстояние), без да оставяте 50 „опасни“ опции за клиента.
  • Нуждаете се от точно рендериране от предния край, без да разчитате на добавка на трета страна, която може да се промени без предупреждение.
  • Искате да подобрите производителността: ресурси, зареждани само когато е необходимо, без „голям пакет“ от джаджи.
  • Искате да интегрирате данни от WordPress (мета, опции, ACF/Pods и др.) чрез динамични тагове.

Кога НЕ трябва да използвате това решение

  • Необходимостта е чисто визуална и спорадична: a прост Шаблон на Elementor, контейнер и малко CSS може да са достатъчни.
  • Нямате контрол върху поддръжката: персонализираната джаджа предполага следване на Elementor (а понякога и на неговите остарели версии).
  • Ако искате да „закърпите“ Elementor значително (например, да промените вътрешното поведение на редактора), той рядко е стабилен. Изберете официални разширения или приемете известна степен на технически дълг.
  • Вашият клиент използва предимно Gutenberg/blocks: в този случай, персонализиран блок (Block API) често е по-подходящ. Вижте официалната документация: Наръчник за блоков редактор.

Предварителни изисквания / преди започване

  • WordPress 6.9.4+ и PHP 8.1+ (в идеалния случай 8.2/8.3 през 2026 г., ако вашият доставчик на хостинг услуги може да се справи).
  • Elementor е инсталиран и активиран (безплатната версия е достатъчна за този примерен уиджет). За напреднали динамични тагове често се използва Elementor Pro, но ние се придържаме към публични API, когато е възможно.
  • Средата за подготовка и архивирането са от съществено значение, преди да се направят каквито и да е промени. Често съм виждал фрагменти, поставени в продукцията, които предизвикват фатална грешка и заключват администраторския интерфейс.
  • Плъгин за регистрационни файлове (или поне WP_DEBUG_LOG) за четене на PHP грешки.

Полезни препратки към WordPress:

Наивният подход (и защо да го избягваме)

Класическият подход: поставяне на голямо парче код в functions.php на темата (често без дъщерна тема), запазване на скриптове навсякъде и създаване на инстанции на класове на Elementor при зареждане.

Типичен пример (анти-модел)

<?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');
});

Защо се чупи (често)

  • Синхронизиране Elementor все още не беше завършил инициализацията на своите мениджъри по време на init (в зависимост от версиите/контекста).
  • Фатална грешка : ако Elementor е деактивиран, ElementorPlugin не съществува.
  • Изпълнение CSS/JS се зарежда навсякъде, включително на страници, които не използват Elementor.
  • поддръжка Кодът е изгубен в темата, невъзможно е да се версионира правилно, нестабилно е при смяна на теми.

Правилният подход — стъпка по стъпка урок

Ще направим мини-плъгин с:

  • bootstrap, който проверява дали Elementor е активен,
  • специална категория джаджи,
  • персонализиран уиджет,
  • условно зареждане на активи,
  • опционален вариант на „Динамичен етикет“, илюстриращ усъвършенстван филтър/регистър.

Стъпка 1 — Създайте плъгина

Създайте тази папка: wp-content/plugins/bpcab-elementor-hooks/

След това този файл: wp-content/plugins/bpcab-elementor-hooks/bpcab-elementor-hooks.php

Стъпка 2 — Bootstrap + Elementor проверка

Прикачваме нашия код към plugins_loaded тогава чакаме elementor/initКлючовият момент: никога не извиквайте класове на Elementor, докато плъгинът не е готов.

Стъпка 3 — Запазване на категория + уиджет

Elementor предоставя специални действия. На практика те са стабилни в продължение на няколко версии:

  • elementor/elements/categories_registered за да добавите категория,
  • elementor/widgets/register за да запазите уиджет.

Настоявам: избягвайте да използвате кукички „на случаен принцип“ (като init ou wp_loaded) за да докоснете Elementor. Проблемът рядко идва от кода на уиджета, а от момента, в който той се изпълнява.

Стъпка 4 — Заредете CSS/JS в точния момент

Активите се записват чрез elementor/frontend/after_register_styles / elementor/frontend/after_register_scriptsтогава ние Поставяне в опашката само ако уиджетът действително е рендиран.

Стъпка 5 — (По избор) Добавяне на динамичен етикет

Ако използвате Elementor Pro (или ако вашият стек поддържа динамични тагове), персонализираният тагове често е по-чист от шорткод. Вие предоставяте данните, а Elementor обработва инжектирането в своите „динамични“ контроли.

Пълен код

Копирайте и поставете всичко по-долу. Плъгинът е самостоятелен и можете да добавяте джаджи по-късно.

Файл 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();

Файл 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>';
	}
}

Файл 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);
	}
}

Файл 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;
}

Файл 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.
})();

Обяснение на кода

1) Защо плъгин (а не functions.php)?

Плъгинът ви дава ясен жизнен цикъл, активиране/деактивиране, версии и стабилно местоположение за вашите класове. Често съм виждал сайтове на Avada/Divi да се актуализират и малък фрагмент в темата да изчезва или да става несъвместим.

2) Ключовият момент: времето на закачане на куките

  • plugins_loaded WordPress е заредил плъгините. Можем да проверим дали Elementor е наличен.
  • elementor/init Elementor е инициализирал основния си контейнер. Тук добавяте вашите Elementor hooks-и.
  • elementor/widgets/register : получавате мениджъра на уиджети и запазвате класовете си.
  • elementor/elements/categories_registered : декларирате категория, видима в потребителския интерфейс на конструктора.

Тази разбивка избягва класическия бъг: „Класът 'ElementorPlugin' не е намерен“ или „Извикване на членска функция register() при null“.

3) Условно зареждане на активи

Дуетът get_style_depends() / get_script_depends() е недоизползван. И все пак, това е един от най-чистите начини да заредите вашите ресурси само когато Elementor рендира вашата джаджа.

Зад кулисите: Elementor събира зависимостите на джаджите на страницата и запитва съответните манипулатори. Вие просто... wp_register_style() / wp_register_script() в правилното време.

4) Сигурно рендиране: санитизация + екраниране

  • влизане Elementor съхранява стойности в базата данни. Все още е необходимо да ги почиствате в зависимост от контекста.
  • вилазка : esc_html() за текст, wp_kses_post() ако разрешите ограничен HTML.

Капанът, който виждам най-често: да излизам директно навън $settings['text'] без wp_kses_post() „Защото е администраторът.“ В сайт с множество автори това се превръща в XSS риск.

5) Динамичен етикет: защо е полезен

Динамичният таг елиминира нуждата от шорткодове в полетата на Elementor. Вие предоставяте данни (потребителски мета данни, опция, ACF поле) и потребителят избира тага в потребителския интерфейс. Това е по-лесно за поддръжка от шорткод, поставен в 30 уиджета.

Варианти и случаи на употреба

Вариант 1 — Принудително „заключени“ стойности (по-малко опции за клиента)

Ако искате да попречите на клиента да променя определени опции, можете да:

  • не излагайте контрола (не add_control),
  • или да се покаже затворен списък (SELECT),
  • или да наложите стойност в render().

Пример: налагане на CSS клас въз основа на „тип“:

// 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>';

Вариант 2 — Добавяне на контрола за URL адрес и създаване на чиста връзка

Призив към действие с кликаема значка се появява през цялото време. Elementor предоставя контрол за URL адреси с опции „отваряне в нов раздел“ и „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;
}

Забележка: Ако отворите връзка в нов раздел, запазете noopener (охрана).

Вариант 3 — Зареждане на ресурс само на определени страници (още по-строго)

Ако имате сложен скрипт, можете да комбинирате зависимостта на уиджета с условие на WordPress. Например: само на страници (не на членове):

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');
	}
}

Рядко го използвам: може да е изненадващо, ако даден уиджет се използва в шаблон, който се показва другаде. Тествайте го старателно.

Съвместимост с Divi 5 / Elementor / Avada

Elementor

  • Горният плъгин се интегрира „в“ Elementor, без да зависи от тема.
  • Ако използвате шаблони на Elementor (Theme Builder), джаджата остава достъпна навсякъде.
  • Активите са условни: добър момент за много натоварени сайтове.

Диви 5

Divi 5 не използва Elementor API. Вашият уиджет няма да се показва в Divi и това е нормално.

Ако целта ви е да използвате повторно един и същ компонент на Divi страници, препоръчвам стратегия „независимо от конструктора“:

  • създайте шорткод за WordPress (или по-добре: блок на Gutenberg),
  • след това го вмъкнете в Divi чрез модул за код/кратък код,
  • и запазете Elementor като „UI overlay“, когато е наличен.

Според моя опит, това е единственият подход, който работи, ако имате парк с множество строители.

Авада (Fusion Builder)

Същата логика важи: Avada няма да използва уиджети на Elementor. Вашият плъгин обаче остава полезен, ако сайтът използва Elementor на някои страници.

За Avada, най-чистият модел е също: шорткод или блок, след това елемент „Кодов блок“ / „Шорткод“ във Fusion Builder.

Проверки след инсталацията

  1. Активирайте плъгина в Разширения.
  2. Отворете страница с Elementor.
  3. В панела с джаджи потърсете категорията БПКАБ след това джаджата Значка (BPCAB).
  4. Поставете го на страницата, редактирайте заглавието/текста/цветовете и публикувайте.
  5. На лицевата страна проверете страницата: трябва да видите bpcab-badge.css заредени (а не на страници, които не използват уиджета).

Бърза диагностична диаграма

симптом Вероятна причина проверка Решение
Категорията BPCAB не се показва Куката никога не се изпълнява (Елементорът не е зареден) Проверете това ELEMENTOR_VERSION е дефиниран и Elementor е активен Активирайте Elementor, проверете за конфликти/mu-плъгини
Фатална грешка „Класът ElementorWidget_Base не е намерен“ Файлът с уиджета е зареден твърде рано / Elementor е неактивен Преглед на PHP лога + стека Изисква се само джаджата в elementor/widgets/register après elementor/init
CSS/JS не е зареден Нерегистрирани манипулатори или лоша кука за регистрация инспектира wp_head / wp_footer + конзола Проверяващият after_register_styles/scripts et get_style_depends()
Джаджата се показва, но стиловете са счупени. Кеширане (плъгин/CDN) или агресивно минифициране Деактивиране на кеша, изчистване на CDN, тестване в режим на частно сърфиране Изключване на файлове от кеша/минимизиране, bump версия
Динамичният етикет не е намерен Функцията не е налична (в зависимост от конфигурацията/Pro) или куката не е задействана Проверете дали панелът „Динамичен“ съществува в текстово поле Инсталирайте/активирайте Elementor Pro, ако е необходимо, или премахнете секцията с етикети

Ако това не проработи

  1. Потвърдете версиите WordPress 6.9.4+, PHP 8.1+, Elementor са актуални. Остаряла версия на PHP причинява грешки на declare(strict_types=1) или видовете ?self.
  2. Активиране на регистриране в wp-config.php (в етап на подготовка):

    define('WP_DEBUG', true);

    define('WP_DEBUG_LOG', true);

    define('WP_DEBUG_DISPLAY', false);
  3. отворено wp-content/debug.log и потърсете „BPCAB“ или „Elementor“.
  4. Временно деактивиране Плъгини за фрагменти. Вече съм виждал фрагмент „от стар урок за Elementor“, който декларира клас със същото име и причинява фатален конфликт.
  5. Изчистване на кешовете : кеш на плъгини, кеш на сървъра, CDN, кеш на браузъра. В Elementor, агресивното кеширане може също да запази липсващи ресурси.
  6. Регенерирайте CSS кода на Elementor (ако вашият сайт използва опцията за генериране на CSS). В Elementor обикновено имате действие за регенериране в настройките за инструменти/производителност.
  7. Опитайте с неутрална тема (временно): Twenty Twenty-* или лека тема. Темата може да отписва скриптове/стилове.

Често срещани капани и грешки

Грешка Причина Решение
Кодът е поставен в грешен файл Добавено в functions.php вместо плъгин Създайте плъгин, актуализирайте го и го активирайте/деактивирайте правилно.
„Грешка при синтактичен анализ: синтактична грешка“ Липсваща точка и запетая, допълнителна къдрава скоба, непълно копиране и поставяне Прегледайте реда, посочен в лога, използвайте IDE с PHP форматиране
Неподходящ елемент Hook Използването на init / wp_loaded за запазване на уиджет употреба elementor/init puis elementor/widgets/register
„Класът ElementorPlugin не е намерен“ Elementor е деактивиран или зареден след вашия код Проверяващият defined('ELEMENTOR_VERSION') и никога преди не се обаждайте на Elementor elementor/init
CSS/JS не е зареден Лош дескриптор, лоша кука или кеш/минификация Запазване чрез after_register_styles/scripts, декларирайте зависимости чрез get_*_depends()изчистване на кеша
Конфликт с имената на класовете Два плъгина декларират BPCAB_Widget_Badge (или неправилно конфигуриран автозареждащ механизъм) Винаги използвайте префикси и именни пространства, ако индустриализирате.
Объркване между действие и филтър Опитвате се да се „върнете“ към акция Действия: странични ефекти. Филтри: връщат стойност. Прегледайте използвания hook.
Директно тестване в производство Без подготовка, без резервно копие План за подготовка + архивиране + връщане към предишни версии (деактивиране на плъгина чрез FTP, ако е необходимо)
Непоследователни постоянни връзки/шаблони Тествате върху различен шаблон от този, който се рендира (Theme Builder). Проверете кой шаблон на Elementor е действително приложен и изчистете кеша.

Съвети за безопасност, производителност и поддръжка

Сигурност

  • Систематично бягство всичко, което се извежда в HTML, трябва да бъде екранирано според контекста (esc_html, esc_attr, esc_url, wp_kses_post). Референция: WordPress: Валидиране на данни.
  • Няма опции за „безплатен HTML“ за роли, които не са администратори. В сайтове с множество автори това е XSS вектор.
  • Няма изпълнение на PHP чрез уиджет (Изглежда очевидно, но вече съм виждал някои сглобени „кодови джаджи“).

Изпълнение

  • Условни активи от get_style_depends() / get_script_depends() : това е най-доброто съотношение усилия/печалба.
  • Избягвайте циклични заявки в render()Ако трябва да заредите данни (публикации, мета данни), кеширайте ги (tranzients/object cache) или ги подгответе чрез оптимизирана заявка.
  • Минимален CSS : уиджет = малък файл. Ако имате 20, групирайте ги интелигентно (но запазете условността).

поддръжка

  • Версия Използвайте плъгина (Git) и маркирайте изданията си. Когато Elementor промени API, ще знаете какво да внедрите.
  • Избягвайте „стари“ уроци които използват остарели куки. Ако използвате повторно фрагмент от 2021-2023 г., уверете се, че е съвместим с текущите Elementor и WordPress 6.9.4.
  • Подгответе резервна стратегия : ако Elementor е деактивиран, вашият плъгин не би трябвало да „прави нищо“, без да е фатален.

ресурси

ЧЗВ

Този код работи ли с WordPress 6.9.4?

Да: плъгинът следва стандартните практики на WordPress (hooks, queue) и е насочен към PHP 8.1+. Основният проблем със съвместимостта остава версията на Elementor (поддържайте я актуална).

Защо да не използвате плъгин за фрагменти?

За бърз тест е добре. За многократно използваем уиджет на Elementor, истинският плъгин е по-надежден: контролирано зареждане, организирани файлове, версии и чисто деактивиране, ако се повреди.

Моят уиджет се показва, но не е в правилната категория.

Провери това get_categories() обърни се ['bpcab']и че категорията е регистрирана чрез elementor/elements/categories_registered.

Как да добавя няколко джаджи?

Добавете още файлове към includes/widgets/ и ги запазете в register_widgets()Един файл = един клас.

Как мога да избегна зареждането на JS, ако не ми е нужен?

Изтрий get_script_depends() или върнете празен масив. Поддържайте джаджата функционална без JS, доколкото е възможно.

Можем ли да използваме автозареждащ се (Composer)?

Да, особено ако имате 10+ джаджи. В контекста на WordPress, внимавайте да не насилвате Composer на крайния сайт. Често срещан подход е да се включи PSR-4 автозареждащ механизъм в плъгина.

Защо да използвам wp_kses_post() За текста?

Защото текстовото поле може да съдържа HTML, ако Elementor го позволява (или ако потребителят постави съдържание). wp_kses_post() позволява безопасно подмножество, за разлика от сурово ехо.

Динамичният етикет не се показва: това нормално ли е?

Зависи от конфигурацията на вашия Elementor. Проверете дали потребителският интерфейс „Dynamic“ е наличен за вашите полета. Ако сайтът ви не поддържа динамични тагове, премахнете частта elementor/dynamic_tags/register и свързания файл.

Как мога да тествам правилно, без да повредя редактора?

Тествайте в режим на подготовка, активирайте логване и започнете с минимален уиджет (рендериран + един контрол). Добавяйте контролите един по един. Грешките в Elementor често са безшумни от страна на потребителския интерфейс, но видими в конзолата и PHP лога.

Съвместимо ли е с детска тема Divi/Avada?

Да, защото е плъгин. Уиджетът обаче ще се показва само в Elementor. За Divi/Avada използвайте шорткод или блок, ако искате компонент, който може да се споделя между конструктори.