ปัญหา / ความต้องการ

หากคุณส่งมอบเว็บไซต์ Elementor ไปแล้ว และลูกค้าถามคุณว่า “ฉันต้องการวิดเจ็ต” ทำเพื่อวัดแต่ถ้าไม่ติดตั้งส่วนเสริม 12 ตัว คุณก็มีปฏิกิริยาตอบสนองที่ถูกต้องแล้ว นั่นคือ ตรวจสอบโค้ด

Elementor (เวอร์ชันฟรี/โปร) มี API ที่เสถียรเพียงพอสำหรับการปรับแต่งตัวสร้างเว็บไซต์ เช่น การเพิ่มหมวดหมู่ของวิดเจ็ต การบันทึกวิดเจ็ตที่กำหนดเอง การแทรกคอนโทรล การกำหนดค่าเริ่มต้น การโหลดสคริปต์เฉพาะเมื่อจำเป็น และแม้แต่การเพิ่มฟิลด์แบบไดนามิกผ่านแท็ก

ความต้องการทางธุรกิจทั่วไปคือ การสร้างส่วนประกอบที่นำกลับมาใช้ซ้ำได้ (เช่น ปุ่มกระตุ้นการดำเนินการ (CTA), กล่องข้อมูลผู้เขียน, เอกสารข้อมูลผลิตภัณฑ์, ตาราง, แบนเนอร์ GDPR เป็นต้น) ในระดับอุตสาหกรรม โดยยังคงรักษาประสบการณ์ผู้ใช้ (UX) ของ 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 เป็นหลัก ในกรณีนี้ การใช้ custom block (Block API) มักจะเหมาะสมกว่า โปรดดูเอกสารอย่างเป็นทางการ: คู่มือการแก้ไขบล็อก.

ข้อกำหนดเบื้องต้น / ก่อนเริ่มต้น

  • WordPress เวอร์ชัน 6.9.4 ขึ้นไป และ PHP เวอร์ชัน 8.1 ขึ้นไป (หรือเวอร์ชัน 8.2/8.3 ในปี 2026 หากผู้ให้บริการโฮสติ้งของคุณสามารถรองรับได้)
  • Elementor ได้รับการติดตั้งและเปิดใช้งานแล้ว (เวอร์ชันฟรีก็เพียงพอสำหรับตัวอย่างวิดเจ็ตนี้) สำหรับ Dynamic Tags ขั้นสูง มักจะใช้ 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 ด้วย
  • ซ่อมบำรุง : โค้ดหายไปในธีม ไม่สามารถจัดการเวอร์ชันได้อย่างถูกต้อง และมีความเปราะบางเมื่อเปลี่ยนธีม

แนวทางที่ถูกต้อง — คู่มือทีละขั้นตอน

เราจะสร้างปลั๊กอินขนาดเล็ก โดยมีส่วนประกอบดังนี้:

  • สคริปต์บูตสแตรปที่ตรวจสอบว่า 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 เพื่อบันทึกวิดเจ็ต

ผมขอย้ำอีกครั้ง: หลีกเลี่ยงการใช้ hooks "แบบสุ่ม" (เช่น init ou wp_loaded) เพื่อแก้ไข Elementor ปัญหาไม่ค่อยเกิดจากโค้ดของวิดเจ็ต แต่เกิดจากช่วงเวลาที่วิดเจ็ตเริ่มทำงาน

ขั้นตอนที่ 4 — โหลด CSS/JS ในเวลาที่เหมาะสม

สินทรัพย์จะถูกบันทึกผ่าน elementor/frontend/after_register_styles / elementor/frontend/after_register_scriptsแล้วเรา enqueue เฉพาะในกรณีที่วิดเจ็ตแสดงผลจริงเท่านั้น

ขั้นตอนที่ 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 เข้าไป
  • 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'] Sans wp_kses_post() “เพราะเป็นผู้ดูแลระบบ” ในเว็บไซต์ที่มีผู้เขียนหลายคน นี่จึงกลายเป็นความเสี่ยงต่อการโจมตี XSS

5) แท็กแบบไดนามิก: เหตุใดจึงมีประโยชน์

แท็กแบบไดนามิกช่วยลดความจำเป็นในการใช้ shortcode ในฟิลด์ของ Elementor คุณเปิดเผยข้อมูล (เมตาข้อมูลผู้ใช้ ตัวเลือก ฟิลด์ ACF) และผู้ใช้เลือกแท็กใน UI ซึ่งดูแลรักษาง่ายกว่าการวาง shortcode ลงในวิดเจ็ต 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 และสร้างลิงก์ที่ดูเรียบร้อย

จะมีปุ่มคลิกเพื่อดำเนินการ (CTA) ปรากฏอยู่ตลอดเวลา 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');
	}
}

ฉันไม่ค่อยได้ใช้มันเท่าไหร่: มันอาจทำให้เกิดความประหลาดใจได้หากมีการใช้ widget ในเทมเพลตที่แสดงผลที่อื่น ทดสอบอย่างละเอียดถี่ถ้วนก่อน

ความเข้ากันได้กับ Divi 5 / Elementor / Avada

Elementor

  • ปลั๊กอินข้างต้นผสานรวมเข้ากับ Elementor โดยไม่ขึ้นอยู่กับธีมใดๆ
  • หากคุณใช้เทมเพลต Elementor (ตัวสร้างธีม) วิดเจ็ตจะยังคงใช้งานได้ทุกที่
  • สินทรัพย์มีเงื่อนไข: นี่เป็นข้อดีสำหรับเว็บไซต์ที่มีการใช้งานสูงมาก

Divi 5

Divi 5 ไม่ได้ใช้ API ของ Elementor ดังนั้นวิดเจ็ตของคุณจะไม่ปรากฏใน Divi ซึ่งเป็นเรื่องปกติ

หากเป้าหมายของคุณคือการนำส่วนประกอบเดียวกันไปใช้ซ้ำในหน้าเว็บ Divi ผมขอแนะนำกลยุทธ์ "ไม่ขึ้นกับตัวสร้างหน้าเว็บ" (builder-agnostic):

  • สร้าง shortcode ของ WordPress (หรือจะดีกว่านั้นคือ บล็อก Gutenberg)
  • จากนั้นแทรกเข้าไปใน Divi ผ่านโมดูลโค้ด/ชอร์ตโค้ด
  • และให้คง Elementor ไว้เป็น "ส่วนเสริม UI" เมื่อมีการใช้งาน

จากประสบการณ์ของผม นี่เป็นวิธีเดียวที่ได้ผลหากคุณมีนิคมอุตสาหกรรมที่มีผู้สร้างหลายราย

อวาดา (ฟิวชั่น บิลเดอร์)

หลักการเดียวกันนี้ใช้ได้เช่นกัน: Avada จะไม่ใช้ Widget ของ Elementor อย่างไรก็ตาม ปลั๊กอินของคุณยังคงมีประโยชน์หากเว็บไซต์นั้นใช้ Elementor ในบางหน้า

สำหรับ Avada รูปแบบที่สะอาดที่สุดก็คือ: ใช้ shortcode หรือ block จากนั้นใช้ element “Code Block” / “Shortcode” ใน Fusion Builder

การตรวจสอบหลังการติดตั้ง

  1. เปิดใช้งานปลั๊กอินใน ส่วนขยาย.
  2. เปิดหน้าเว็บด้วย Elementor
  3. ในแผงวิดเจ็ต ให้มองหาหมวดหมู่ บีพีซีเอบี จากนั้นวิดเจ็ต ตราสัญลักษณ์ (BPCAB).
  4. วางลงบนหน้าเว็บ แก้ไขชื่อเรื่อง/ข้อความ/สี แล้วเผยแพร่
  5. ตรวจสอบหน้ากระดาษด้านหน้า คุณควรจะเห็น... bpcab-badge.css โหลดแล้ว (และไม่ใช่ในหน้าเว็บที่ไม่ใช้ widget)

แผนภูมิการวินิจฉัยอย่างรวดเร็ว

อาการ สาเหตุที่เป็นไปได้ การตรวจสอบ Solution
หมวดหมู่ BPCAB ไม่ปรากฏ Hook ไม่ทำงาน (Elementor โหลดไม่สำเร็จ) ตรวจสอบดู ELEMENTOR_VERSION ได้ถูกกำหนดไว้แล้ว และ Elementor ก็ทำงานอยู่ เปิดใช้งาน Elementor และตรวจสอบข้อขัดแย้ง/ปลั๊กอิน mu
ข้อผิดพลาดร้ายแรง “ไม่พบคลาส ElementorWidget_Base” ไฟล์วิดเจ็ตถูกโหลดเร็วเกินไป / Elementor ไม่ทำงาน ดูบันทึก PHP + ข้อผิดพลาดในการแสดงผล ต้องการเพียงวิดเจ็ตใน elementor/widgets/register หลังจากที่ elementor/init
CSS/JS โหลดไม่สำเร็จ แฮนด์เดิลที่ไม่ได้ลงทะเบียนหรือฮุกการลงทะเบียนที่ไม่ถูกต้อง ตรวจ wp_head / wp_footer + คอนโซล Verifier after_register_styles/scripts et get_style_depends()
วิดเจ็ตแสดงผล แต่รูปแบบการแสดงผลผิดเพี้ยน การแคช (ปลั๊กอิน/CDN) หรือการลดขนาดไฟล์อย่างเข้มงวด ปิดใช้งานแคช ล้าง CDN ทดสอบในโหมดการเรียกดูแบบส่วนตัว ไม่รวมไฟล์จากแคช/ย่อขนาด เพิ่มเวอร์ชัน
ไม่พบแท็กแบบไดนามิก ฟังก์ชันนี้ไม่พร้อมใช้งาน (ขึ้นอยู่กับการตั้งค่า/เวอร์ชัน Pro) หรือไม่มีการเรียกใช้งานฮุก ตรวจสอบว่ามีแผง "ไดนามิก" อยู่ในช่องข้อความหรือไม่ ติดตั้ง/เปิดใช้งาน Elementor Pro หากจำเป็น หรือลบส่วนแท็กออก

ถ้าวิธีนั้นไม่ได้ผล

  1. ตรวจสอบเวอร์ชัน WordPress เวอร์ชัน 6.9.4 ขึ้นไป, PHP เวอร์ชัน 8.1 ขึ้นไป, Elementor อัปเดตล่าสุดแล้ว แต่เวอร์ชัน PHP ที่ล้าสมัยทำให้เกิดข้อผิดพลาด declare(strict_types=1) หรือประเภทต่างๆ ?self.
  2. เปิดใช้งานการบันทึก dans 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-* หรือธีมที่มีน้ำหนักเบา ธีมสามารถยกเลิกการลงทะเบียนสคริปต์/สไตล์ได้

ข้อผิดพลาดและปัญหาที่พบบ่อย

ความผิดพลาด ก่อให้เกิด Solution
โค้ดถูกวางลงในไฟล์ผิด เพิ่มใน functions.php แทนที่จะเป็นปลั๊กอิน สร้างปลั๊กอิน กำหนดเวอร์ชัน และเปิด/ปิดใช้งานอย่างถูกต้อง
“ข้อผิดพลาดในการวิเคราะห์: ข้อผิดพลาดทางไวยากรณ์” ขาดเครื่องหมายเซมิโคลอน มีวงเล็บปีกกาเกินมา การคัดลอกวางไม่สมบูรณ์ ตรวจสอบบรรทัดที่ระบุในบันทึก โดยใช้ IDE ที่มีฟังก์ชันจัดรูปแบบ PHP
ฮุค เอเลเมนเตอร์ ไม่เหมาะสม การใช้ init / wp_loaded เพื่อบันทึกวิดเจ็ต ใช้ elementor/init แล้วก็ elementor/widgets/register
“ไม่พบคลาส ElementorPlugin” Elementor ถูกปิดใช้งานหรือโหลดหลังจากโค้ดของคุณ Verifier defined('ELEMENTOR_VERSION') และห้ามโทรหา Elementor ก่อนเด็ดขาด elementor/init
CSS/JS โหลดไม่สำเร็จ ด้ามจับไม่ดี ตะขอไม่ดี หรือแคช/การย่อขนาดไม่ดี บันทึกผ่าน after_register_styles/scriptsประกาศการพึ่งพาผ่าน get_*_depends()ล้างแคช
ความขัดแย้งของชื่อคลาส ปลั๊กอินสองตัวประกาศ BPCAB_Widget_Badge (หรือตัวโหลดอัตโนมัติที่ตั้งค่าไม่ถูกต้อง) หากคุณกำลังพัฒนาระบบในระดับอุตสาหกรรม ควรใช้คำนำหน้าและเนมสเปซเสมอ
ความสับสนระหว่างแอ็กชันและฟิลเตอร์ คุณกำลังพยายาม "กลับเข้าสู่" หุ้นตัวนั้น การกระทำ: ผลข้างเคียง ตัวกรอง: ส่งคืนค่า ตรวจสอบฮุกที่ใช้
การทดสอบโดยตรงในขั้นตอนการผลิต ไม่มีการเตรียมการ ไม่มีระบบสำรองข้อมูล แผนการเตรียมการ + การสำรองข้อมูล + การย้อนกลับ (รวมถึงการปิดใช้งานปลั๊กอินผ่าน FTP หากจำเป็น)
ลิงก์ถาวร/เทมเพลตไม่สอดคล้องกัน คุณกำลังทดสอบบนเทมเพลตที่แตกต่างจากเทมเพลตที่แสดงผล (ตัวสร้างธีม) ตรวจสอบว่าเทมเพลต Elementor ใดถูกใช้งานอยู่จริง และล้างแคช

คำแนะนำด้านความปลอดภัย ประสิทธิภาพ และการบำรุงรักษา

ความปลอดภัย

  • การหลบหนีอย่างเป็นระบบ : ทุกสิ่งที่แสดงผลในรูปแบบ HTML จะต้องถูกแปลงให้สอดคล้องกับบริบท (esc_html, esc_attr, esc_url, wp_kses_post). อ้างอิง : WordPress: การตรวจสอบความถูกต้องของข้อมูล.
  • ไม่มีตัวเลือก “HTML ฟรี” สำหรับบทบาทที่ไม่ใช่ผู้ดูแลระบบ ในเว็บไซต์ที่มีผู้เขียนหลายคน นี่เป็นช่องโหว่ XSS
  • ไม่มีการเรียกใช้งาน PHP ผ่านวิดเจ็ต (ดูเหมือนจะเป็นเรื่องชัดเจน แต่ผมก็เคยเห็น "วิดเจ็ตโค้ด" ที่ถูกสร้างขึ้นแบบลวกๆ มาบ้างแล้ว)

ประสิทธิภาพ

  • สินทรัพย์ที่มีเงื่อนไข ผ่านทาง get_style_depends() / get_script_depends() : เป็นอัตราส่วนความพยายามต่อผลตอบแทนที่ดีที่สุด
  • หลีกเลี่ยงการวนซ้ำคำขอ dans render()หากคุณต้องการโหลดข้อมูล (โพสต์ เมตา) ให้แคชข้อมูลนั้นไว้ (แคชชั่วคราว/แคชอ็อบเจ็กต์) หรือเตรียมข้อมูลนั้นผ่านการสืบค้นที่ได้รับการปรับให้เหมาะสม
  • CSS ขั้นต่ำ : วิดเจ็ต = ไฟล์ขนาดเล็ก ถ้ามี 20 อัน ให้จัดกลุ่มอย่างชาญฉลาด (แต่ยังคงเงื่อนไขไว้)

ซ่อมบำรุง

  • เวอร์ชั่น ใช้ปลั๊กอิน (Git) และติดแท็กให้กับเวอร์ชันที่เผยแพร่ เมื่อ Elementor เปลี่ยนแปลง API คุณจะรู้ว่าต้องปรับใช้เวอร์ชันใด
  • หลีกเลี่ยงบทช่วยสอนแบบ "เก่า" ซึ่งใช้ hook ที่ล้าสมัย หากคุณนำโค้ดตัวอย่างจากปี 2021-2023 มาใช้ซ้ำ โปรดตรวจสอบให้แน่ใจว่าสามารถใช้งานร่วมกับ Elementor และ WordPress เวอร์ชัน 6.9.4 ปัจจุบันได้
  • เตรียมแผนสำรองไว้ หาก Elementor ถูกปิดใช้งาน ปลั๊กอินของคุณควรจะ "ไม่ทำอะไรเลย" โดยไม่ทำให้เกิดข้อผิดพลาดร้ายแรง

ทรัพยากร

คำถามที่พบบ่อย

โค้ดนี้ใช้งานได้กับ WordPress เวอร์ชัน 6.9.4 หรือไม่?

ใช่ ปลั๊กอินนี้ใช้หลักการมาตรฐานของ WordPress (hooks, enqueue) และรองรับ PHP 8.1 ขึ้นไป ปัญหาความเข้ากันได้หลักยังคงอยู่ที่เวอร์ชันของ Elementor (โปรดอัปเดตอยู่เสมอ)

ทำไมไม่ลองใช้ปลั๊กอินสำหรับสร้างโค้ดตัวอย่างล่ะ?

สำหรับการทดสอบอย่างรวดเร็วก็ใช้ได้ แต่สำหรับวิดเจ็ต Elementor ที่นำกลับมาใช้ใหม่ได้นั้น ปลั๊กอินที่มีคุณภาพดีกว่าจะน่าเชื่อถือกว่า เพราะมีการควบคุมการโหลด การจัดการไฟล์ การกำหนดเวอร์ชัน และการปิดใช้งานอย่างราบรื่นหากเกิดปัญหา

วิดเจ็ตของฉันปรากฏขึ้น แต่ไม่ได้อยู่ในหมวดหมู่ที่ถูกต้อง

ตรวจสอบว่า get_categories() หันกลับ ['bpcab']และหมวดหมู่ดังกล่าวได้รับการลงทะเบียนผ่านทาง elementor/elements/categories_registered.

ฉันจะเพิ่มวิดเจ็ตหลายรายการได้อย่างไร?

เพิ่มไฟล์เพิ่มเติมลงใน includes/widgets/ และบันทึกไว้ใน register_widgets()เก็บไฟล์หนึ่งไฟล์เท่ากับหนึ่งคลาส

ฉันจะหลีกเลี่ยงการโหลด JavaScript หากไม่จำเป็นได้อย่างไร?

ลบ get_script_depends() หรือส่งคืนอาร์เรย์ว่างเปล่า พยายามทำให้วิดเจ็ตใช้งานได้โดยไม่ต้องใช้ JavaScript ให้มากที่สุด

สามารถใช้โปรแกรมโหลดอัตโนมัติ (Composer) ได้หรือไม่?

ใช่ โดยเฉพาะอย่างยิ่งถ้าคุณมีวิดเจ็ตมากกว่า 10 ตัว ในบริบทของ WordPress โปรดระวังอย่าบังคับใช้ Composer ในเว็บไซต์สุดท้าย วิธีที่นิยมใช้คือการรวม autoloader ของ PSR-4 ไว้ในปลั๊กอิน

ทำไมต้องใช้ wp_kses_post() สำหรับข้อความใช่ไหม?

เนื่องจากช่องข้อความสามารถมีโค้ด HTML ได้ หาก Elementor อนุญาต (หรือหากผู้ใช้คัดลอกและวางเนื้อหา) wp_kses_post() ช่วยให้สามารถเลือกส่วนย่อยที่ปลอดภัยได้ ซึ่งแตกต่างจากการสะท้อนแบบดิบๆ

แท็กแบบไดนามิกไม่แสดงผล: เป็นเรื่องปกติหรือไม่?

ขึ้นอยู่กับการตั้งค่า Elementor ของคุณ ตรวจสอบว่า UI แบบ "ไดนามิก" พร้อมใช้งานสำหรับฟิลด์ของคุณหรือไม่ หากเว็บไซต์ของคุณไม่รองรับแท็กไดนามิก ให้ลบส่วนนั้นออก elementor/dynamic_tags/register และไฟล์ที่เกี่ยวข้อง

ฉันจะทดสอบอย่างถูกต้องโดยไม่ทำให้โปรแกรมแก้ไขเสียหายได้อย่างไร?

ทดสอบในโหมดทดสอบ (staging mode) เปิดใช้งานการบันทึก (logging) และเริ่มต้นด้วยวิดเจ็ตขนาดเล็ก (แสดงผล + ตัวควบคุมหนึ่งตัว) เพิ่มตัวควบคุมทีละตัว ข้อผิดพลาดของ Elementor มักจะไม่แสดงบนหน้าจอผู้ใช้ แต่จะมองเห็นได้ในคอนโซลและบันทึก PHP

สามารถใช้งานร่วมกับธีมลูกของ Divi/Avada ได้หรือไม่?

ใช่ เพราะมันเป็นปลั๊กอิน แต่ว่าวิดเจ็ตจะปรากฏเฉพาะใน Elementor เท่านั้น สำหรับ Divi/Avada ให้ใช้ shortcode หรือ block ถ้าคุณต้องการคอมโพเนนต์ที่สามารถแชร์ระหว่าง builder ต่างๆ ได้