المشكلة / الحاجة

إذا كنت قد قمت بالفعل بتسليم موقع باستخدام Elementor وسألك أحد العملاء "أريد إضافة أداة" العرفولكن بدون تثبيت 12 إضافة، لديك بالفعل رد الفعل الصحيح: قم بمراجعة الكود.

يوفر Elementor (النسخة المجانية/الاحترافية) واجهة برمجة تطبيقات مستقرة بما يكفي من الخطافات والأحداث لتخصيص أداة الإنشاء بشكل صحيح: إضافة فئة من الأدوات، وحفظ أداة مخصصة، وإدخال عناصر التحكم، وفرض القيم الافتراضية، وتحميل البرامج النصية فقط عند الحاجة، وحتى إضافة حقل ديناميكي عبر علامة.

تتمثل الحاجة التجارية النموذجية في تحويل المكونات القابلة لإعادة الاستخدام (مثل عبارات الحث على اتخاذ إجراء، وصناديق المؤلفين، وصفحات المنتجات، والجداول، وشعارات اللائحة العامة لحماية البيانات، وما إلى ذلك) إلى مكونات صناعية مع الحفاظ على تجربة المستخدم الخاصة بـ 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 خيارًا "خطيرًا" للعميل.
  • أنت بحاجة إلى عرض دقيق للواجهة الأمامية دون الاعتماد على إضافة خارجية يمكن أن تتغير دون سابق إنذار.
  • تريد تحسين الأداء: تحميل الأصول فقط عند الضرورة، وعدم وجود "حزمة كبيرة" من الأدوات.
  • تريد دمج بيانات ووردبريس (البيانات الوصفية، الخيارات، ACF/Pods، إلخ) عبر العلامات الديناميكية.

متى لا يجب استخدام هذا الحل

  • الحاجة بصرية بحتة ومؤقتة: أ بسيط قد يكون قالب Elementor وحاوية وقليل من CSS كافياً.
  • ليس لديك سيطرة على الصيانة: استخدام عنصر واجهة مستخدم مخصص يعني اتباع Elementor (وأحيانًا اتباع تعليماته المتعلقة بالإهمال).
  • إذا كنت ترغب في إجراء تعديلات جذرية على Elementor (مثل تغيير سلوك المحرر الداخلي)، فنادراً ما يكون مستقراً. لذا، اختر الإضافات الرسمية، أو تقبّل بعض المشاكل التقنية.
  • يستخدم عميلك بشكل أساسي Gutenberg/blocks: في هذه الحالة، غالبًا ما يكون استخدام كتلة مخصصة (Block API) أكثر ملاءمة. راجع الوثائق الرسمية. دليل محرر الكتلة.

المتطلبات الأساسية / قبل البدء

  • WordPress 6.9.4+ و PHP 8.1+ (ويفضل 8.2/8.3 في عام 2026 إذا كان مزود الاستضافة الخاص بك قادرًا على مواكبة ذلك).
  • تم تثبيت Elementor وتفعيله (النسخة المجانية كافية لهذا المثال). بالنسبة للعلامات الديناميكية المتقدمة، يُستخدم Elementor Pro غالبًا، لكننا نلتزم باستخدام واجهات برمجة التطبيقات العامة كلما أمكن ذلك.
  • يُعدّ إنشاء بيئة تجريبية ونسخ احتياطية أمرًا ضروريًا قبل إجراء أي تغييرات. لقد رأيتُ مرارًا وتكرارًا أجزاءً برمجية تُلصق في بيئة الإنتاج، مما يؤدي إلى حدوث خطأ فادح وتعطيل واجهة الإدارة.
  • إضافة لتسجيل الأحداث (أو على الأقل WP_DEBUG_LOG) لقراءة أخطاء PHP.

مراجع مفيدة في ووردبريس:

النهج الساذج (ولماذا يجب تجنبه)

الأسلوب الكلاسيكي: لصق جزء كبير من التعليمات البرمجية في 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 قام ووردبريس بتحميل الإضافات. يمكننا التحقق مما إذا كانت إضافة Elementor موجودة.
  • elementor/init قام Elementor بتهيئة حاويته الرئيسية. هنا يمكنك إضافة خطافات Elementor الخاصة بك.
  • elementor/widgets/register : ستتلقى مدير الأدوات وستحفظ فئاتك.
  • elementor/elements/categories_registered : تقوم بتحديد فئة مرئية في واجهة المستخدم الخاصة بالمنشئ.

هذا التحليل يتجنب الخطأ الكلاسيكي: "لم يتم العثور على الفئة 'ElementorPlugin'" أو "استدعاء دالة عضوية register() على قيمة فارغة".

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

الخيار الثاني - أضف عنصر تحكم عنوان URL وقم بإنشاء رابط نظيف

يظهر زر دعوة للعمل قابل للنقر باستمرار. يوفر Elementor عنصر تحكم عنوان URL مع خياري "فتح في علامة تبويب جديدة" و"عدم المتابعة".

// 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 (حماية).

الخيار الثالث - تحميل أحد الأصول فقط على صفحات معينة (أكثر صرامة)

إذا كان لديك نص برمجي معقد، يمكنك دمج تبعية الأداة مع شرط ووردبريس. على سبيل المثال: فقط على الصفحات (وليس على مقالات):

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 (منشئ القوالب)، فستظل الأداة متاحة في كل مكان.
  • الأصول مشروطة: وهي نقطة جيدة في المواقع ذات الحركة المرورية العالية.

ديفي 5

لا يستخدم Divi 5 واجهة برمجة تطبيقات Elementor. لن يظهر عنصر واجهة المستخدم الخاص بك في Divi، وهذا أمر طبيعي.

إذا كان هدفك هو إعادة استخدام نفس المكون في صفحات Divi، فأنا أوصي باستراتيجية "غير مرتبطة بأداة البناء":

  • أنشئ رمزًا مختصرًا في ووردبريس (أو الأفضل: كتلة غوتنبرغ)،
  • ثم قم بإدراجه في Divi عبر وحدة Code/Shortcode،
  • واحتفظ بـ Elementor كـ "طبقة واجهة مستخدم" عند وجودها.

بحسب تجربتي، هذا هو النهج الوحيد الذي ينجح إذا كان لديك مجمع سكني يضم عدة شركات بناء.

أفادا (منشئ الاندماج)

ينطبق المنطق نفسه: لن يستخدم Avada أدوات Elementor. ومع ذلك، يظل الملحق الخاص بك مفيدًا إذا كان الموقع يستخدم Elementor في بعض الصفحات.

بالنسبة لـ Avada، فإن النمط الأكثر وضوحًا هو أيضًا: رمز مختصر أو كتلة، ثم عنصر "كتلة التعليمات البرمجية" / "الرمز المختصر" في Fusion Builder.

فحوصات ما بعد التثبيت

  1. قم بتفعيل الملحق في ملحقات.
  2. افتح صفحة باستخدام Elementor.
  3. في لوحة الأدوات، ابحث عن الفئة BPCAB ثم الأداة شارة (BPCAB).
  4. ضعها على الصفحة، وعدّل العنوان/النص/الألوان، ثم انشرها.
  5. في المقدمة، تفحص الصفحة: يجب أن ترى bpcab-badge.css تم تحميلها (وليس على الصفحات التي لا تستخدم الأداة).

مخطط التشخيص السريع

عرض السبب المحتمل التحقق الحلول
لا تظهر فئة BPCAB لم يتم تنفيذ الخطاف (لم يتم تحميل Elementor) تأكد من ذلك ELEMENTOR_VERSION تم تعريفها، وElementor نشط قم بتفعيل Elementor، وتحقق من وجود تعارضات/إضافات متعددة
خطأ فادح: "لم يتم العثور على الفئة ElementorWidget_Base" تم تحميل ملف الأداة مبكرًا جدًا / Elementor غير نشط عرض سجل PHP + تتبع المكدس لا يتطلب الأمر سوى الأداة في elementor/widgets/register بعد elementor/init
لم يتم تحميل ملفات CSS/JS مقابض غير مسجلة أو خطاف تسجيل خاطئ فحص wp_head / wp_footer + وحدة التحكم متحقق after_register_styles/scripts et get_style_depends()
تظهر الأداة، لكن الأنماط معطلة. التخزين المؤقت (الملحق/شبكة توصيل المحتوى) أو التصغير المكثف قم بتعطيل ذاكرة التخزين المؤقت، وامسح شبكة توصيل المحتوى (CDN)، واختبر في وضع التصفح الخاص. استبعاد الملفات من التخزين المؤقت/التصغير، وتحديث الإصدار
لم يتم العثور على الوسم الديناميكي الميزة غير متوفرة (حسب الإعدادات/الإصدار الاحترافي) أو لم يتم تفعيل الخطاف تحقق مما إذا كانت لوحة "ديناميكي" موجودة في حقل نصي قم بتثبيت/تفعيل Elementor Pro إذا لزم الأمر، أو قم بإزالة قسم الوسوم.

إذا لم ينجح الأمر

  1. تأكد من الإصدارات ووردبريس 6.9.4+، PHP 8.1+، إليمنتور محدّث. إصدار 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. تعطيل مؤقتا إضافات Snippet. لقد رأيت بالفعل مقتطفًا "من برنامج تعليمي قديم لـ Elementor" يُعلن عن فئة بنفس الاسم ويتسبب في تعارض كارثي.
  5. امسح ذاكرة التخزين المؤقت ذاكرة التخزين المؤقت للملحقات، وذاكرة التخزين المؤقت للخادم، وشبكة توصيل المحتوى (CDN)، وذاكرة التخزين المؤقت للمتصفح. في Elementor، يمكن للتخزين المؤقت المكثف الاحتفاظ بالملفات المفقودة أيضًا.
  6. إعادة إنشاء CSS الخاص بـ Elementor (إذا كان موقعك يستخدم خيار إنشاء CSS). في Elementor، عادةً ما يكون لديك خيار إعادة الإنشاء في إعدادات الأدوات/الأداء.
  7. جربها مع سمة محايدة (مؤقتًا): قالب Twenty Twenty-* أو قالب خفيف. يمكن للقالب إلغاء تسجيل البرامج النصية/الأنماط.

الأخطاء الشائعة والمزالق

خطأ سبب الحلول
تم لصق الكود في الملف الخطأ تمت الإضافة functions.php بدلاً من إضافة أنشئ إضافة، وقم بإصدارها، وقم بتفعيلها/إلغاء تفعيلها بشكل صحيح.
"خطأ في التحليل: خطأ في بناء الجملة" فاصلة منقوطة مفقودة، قوس معقوف زائد، نسخ ولصق غير مكتمل راجع السطر المشار إليه في السجل، واستخدم بيئة تطوير متكاملة (IDE) تدعم تنسيق PHP.
عنصر الخطاف غير مناسب استخدام init / wp_loaded لحفظ عنصر واجهة المستخدم استعمال elementor/init ثم elementor/widgets/register
"لم يتم العثور على فئة ElementorPlugin" تم تعطيل أو تحميل Elementor بعد تنفيذ التعليمات البرمجية الخاصة بك متحقق defined('ELEMENTOR_VERSION') ولا تتصل بـ Elementor من قبل elementor/init
لم يتم تحميل ملفات CSS/JS مقبض سيئ، أو خطاف سيئ، أو ذاكرة تخزين مؤقتة/تصغير احفظ عبر after_register_styles/scriptsقم بتعريف التبعيات عبر get_*_depends()مسح ذاكرة التخزين المؤقت
تعارض في اسم الفئة يُعلن مكونان إضافيان BPCAB_Widget_Badge (أو جهاز تحميل تلقائي غير مُهيأ بشكل صحيح) استخدم دائمًا البادئات ومساحات الأسماء إذا كنت تقوم بالتصنيع.
الخلط بين الفعل والمرشح أنت تحاول "العودة" إلى سهم الإجراءات: آثار جانبية. المرشحات: تُرجع قيمة. راجع الخطاف المستخدم.
الاختبار المباشر في الإنتاج لا يوجد تجهيز مسبق، ولا يوجد نسخ احتياطي خطة التجهيز + النسخ الاحتياطي + التراجع (إلغاء تفعيل الإضافة عبر بروتوكول نقل الملفات إذا لزم الأمر)
روابط دائمة/قوالب غير متناسقة أنت تختبر على قالب مختلف عن القالب الذي تم عرضه (منشئ القوالب). تحقق من قالب Elementor الذي تم تطبيقه فعليًا، وقم بمسح ذاكرة التخزين المؤقت.

نصائح السلامة والأداء والصيانة

أمن

  • الهروب المنهجي يجب تهريب كل ما يتم إخراجه في HTML وفقًا للسياق (esc_html, esc_attr, esc_url, wp_kses_post). مرجع : ووردبريس: التحقق من صحة البيانات.
  • لا توجد خيارات "HTML مجانية" بالنسبة للأدوار غير الإدارية. في المواقع متعددة المؤلفين، يُعد هذا ثغرة أمنية من نوع XSS.
  • لا يتم تنفيذ PHP عبر الأداة (يبدو الأمر واضحاً، لكنني رأيت بالفعل بعض "أدوات البرمجة" المجمعة بشكل عشوائي).

هاملت

  • الأصول المشروطة بواسطة get_style_depends() / get_script_depends() إنها أفضل نسبة بين الجهد المبذول والمكسب.
  • تجنب الطلبات المتكررة في render()إذا كنت بحاجة إلى تحميل البيانات (المنشورات، البيانات الوصفية)، فقم بتخزينها مؤقتًا (البيانات المؤقتة/ذاكرة التخزين المؤقت للكائنات) أو قم بإعدادها عبر استعلام مُحسَّن.
  • الحد الأدنى من CSS : عنصر واجهة المستخدم = ملف صغير. إذا كان لديك 20 عنصرًا، فقم بتجميعها بشكل ذكي (مع الحفاظ على الشرط).

الدورية

  • إصدار استخدم الإضافة (Git) وقم بتصنيف إصداراتك. عندما يُجري Elementor تغييرات على واجهة برمجة التطبيقات (API)، ستعرف ما يجب نشره.
  • تجنب الدروس التعليمية "القديمة". والتي تستخدم خطافات قديمة. إذا كنت تعيد استخدام جزء من التعليمات البرمجية من عام 2021-2023، فتأكد من توافقه مع Elementor وWordPress 6.9.4 الحاليين.
  • قم بإعداد استراتيجية احتياطية إذا تم تعطيل Elementor، فيجب ألا يقوم المكون الإضافي الخاص بك بأي شيء دون أن يكون ذلك كارثيًا.

الموارد

الأسئلة الشائعة

هل يعمل هذا الكود مع ووردبريس 6.9.4؟

نعم: يتبع الملحق ممارسات ووردبريس القياسية (الخطافات، وقائمة الانتظار) ويستهدف PHP 8.1 والإصدارات الأحدث. تبقى مشكلة التوافق الرئيسية هي إصدار Elementor (احرص على تحديثه باستمرار).

لماذا لا تستخدم إضافة مقتطفات التعليمات البرمجية؟

لإجراء اختبار سريع، لا بأس. أما بالنسبة لأداة Elementor قابلة لإعادة الاستخدام، فإن استخدام إضافة حقيقية أكثر موثوقية: تحميل مُتحكم به، ملفات مُنظمة، نظام إدارة الإصدارات، وإمكانية تعطيلها بشكل صحيح في حال حدوث عطل.

تظهر الأداة الخاصة بي، لكنها ليست في الفئة الصحيحة.

تحقق من ذلك get_categories() التف حوله ['bpcab']وأن الفئة مسجلة عبر elementor/elements/categories_registered.

كيف أضيف عدة أدوات؟

أضف المزيد من الملفات إلى includes/widgets/ واحفظها في register_widgets()احتفظ بملف واحد = فئة واحدة.

كيف يمكنني تجنب تحميل جافا سكريبت إذا لم أكن بحاجة إليها؟

حذف get_script_depends() أو قم بإرجاع مصفوفة فارغة. حافظ على وظيفة الأداة بدون استخدام جافا سكريبت قدر الإمكان.

هل يمكن استخدام برنامج التحميل التلقائي (Composer)؟

نعم، خاصةً إذا كان لديك أكثر من 10 أدوات. في بيئة ووردبريس، احرص على عدم فرض استخدام Composer على الموقع النهائي. من الطرق الشائعة تضمين مُحمِّل PSR-4 تلقائي داخل الإضافة.

لماذا استخدام wp_kses_post() بالنسبة للنص؟

لأن منطقة النص يمكن أن تحتوي على HTML إذا سمح بذلك Elementor (أو إذا قام المستخدم بلصق المحتوى). wp_kses_post() يسمح بمجموعة فرعية آمنة، على عكس الصدى الخام.

لا يتم عرض الوسم الديناميكي: هل هذا طبيعي؟

يعتمد ذلك على إعدادات Elementor لديك. تأكد من توفر واجهة المستخدم "الديناميكية" لحقولك. إذا كان موقعك لا يدعم العلامات الديناميكية، فاحذف الجزء. elementor/dynamic_tags/register والملف المرتبط به.

كيف يمكنني إجراء الاختبار بشكل صحيح دون إتلاف المحرر؟

اختبر في وضع التجريب، فعّل التسجيل، وابدأ بأداة بسيطة (مُعرَضة + عنصر تحكم واحد). أضف عناصر التحكم واحدًا تلو الآخر. غالبًا ما تكون أخطاء Elementor غير ظاهرة في واجهة المستخدم، ولكنها تظهر في وحدة التحكم وسجل PHP.

هل هو متوافق مع قالب فرعي من نوع Divi/Avada؟

نعم، لأنه إضافة. مع ذلك، لن يظهر العنصر إلا في Elementor. أما في Divi/Avada، فاستخدم رمزًا مختصرًا أو كتلة إذا كنت ترغب في عنصر قابل للمشاركة بين أدوات الإنشاء.