Ak vás klient požiada o „a blok Elementor, ktorý zobrazuje autorské pole s odkazom na ich najnovšie články, vám ponúka dve možnosti: vytvoriť shortcode alebo vytvoriť Skutočný widget Elementoru, správne konfigurovateľný a ľahko udržiavateľný. Shortcode funguje… až do dňa, keď budete potrebovať pridať ovládací prvok štýlu, spravovať záložný kód alebo sa vyhnúť načítaniu CSS všade.
Problém / Potreba
Chcete vlastný, opakovane použiteľný widget Elementoru, ktorý sa integruje do editora ako akýkoľvek natívny widget. A čo je najdôležitejšie: chcete, aby bol konfigurovateľný (obsah + štýly), bezpečný (sanitizácia/escaping) a aby neovplyvňoval výkon stránky.
Táto príručka je určená pre mierne pokročilých používateľov (ovládajúcich PHP a hooky) pracujúcich na WordPresse 6.9.4 (apríl 2026) a PHP 8.1+. Na konci budete vedieť:
- Vytvorte si mini-zapojiť ktorý registruje widget prostredníctvom rozhrania API widgetov Elementoru.
- Pridajte ovládacie prvky (text, URL, selektor používateľa, počet článkov, prepínanie možností).
- Vygenerujte bezpečné (escapingové) a robustné (fallbackové) vykresľovanie HTML.
- Načíta CSS/JS iba vtedy, keď sa používa widget.
Rýchle zhrnutie
- Vytvoríme „mu“ alebo klasický plugin, ktorý sa pripojí k
elementor/widgets/register. - Definujeme triedu widgetu, ktorá rozširuje
ElementorWidget_Base. - Ovládacie prvky pridávame prostredníctvom
Controls_Manager(obsah + štýl). - Widget vykresľujeme pomocou
render()(vpredu) acontent_template()(Náhľad editora, voliteľné). - Ukladáme dáta (CSS/JS) a načítavame ich cez
get_style_depends()/get_script_depends().
Kedy použiť toto riešenie
- Máte blok špecifický pre daný projekt (napr. „Vloženie autora“, „Výzva na akciu domovskej stránky“, „Odporúčané produkty“), ktorý musia byť vaši editori schopní vizuálne nakonfigurovať.
- Musíte sprístupniť ovládacie prvky štýlu Elementoru (typografia, farby, medzery) bez toho, aby ste museli písať zložitú sadu CSS tried.
- Chcete sa vyhnúť „magickým“ krátkym kódom, ktoré narušia rozloženie pri zmene tém alebo nástrojov na tvorbu.
- Ak spravujete viacero webových stránok: widget zabalený ako plugin sa ľahšie verzuje a nasadzuje.
Kedy toto riešenie NEPOUŽÍVAŤ
- Ak chcete vložiť iba malý kúsok statického HTML kódu: použite widget „HTML“ alebo šablónu Elementor.
- Potrebujete 100 % dynamické vykresľovanie na strane servera, ale bez zložitého používateľského rozhrania: postačí vám shortcode (ktorý sa dá použiť v Elementore prostredníctvom widgetu „Shortcode“).
- Ak hľadáte opakovane použiteľný komponent pre rôzne nástroje, uprednostnite Gutenbergov blok (Editor blokov) a/alebo vzor. Elementor je špecifický ekosystém.
- Nemáte kontrolu nad kódom (klientská stránka je uzamknutá): plugin „snippets“ môže pomôcť, ale zriedkakedy je čistý pre načítanie tried/príslušenstva.
Predpoklady / pred začatím
Predtým, ako sa dotknete kódu:
- Práca na testovacom/lokálnom prostredí (LocalWP, DevKinsta, Docker…).
- Zálohujte databázu a súbory (aspoň
wp-content). - Skontrolujte verzie: WordPress 6.9.4, PHP 8.1+, Elementor aktuálny.
- umožniť
WP_DEBUGetWP_DEBUG_LOGna staging, aby sa videli chyby.
Užitočné pripomienky:
- Referenčná príručka k pluginu WP: developer.wordpress.org/plugins
- Dobré bezpečnostné postupy (validácia/escaping): developer.wordpress.org/apis/security
- PHP 8.1: php.net/releases/8.1
Bezpečnostné opatrenie: Widget Elementoru môže zobrazovať údaje z databázy (používateľov, príspevky). Ak výstup správne neuniknete, otvoríte dvere uloženým zraniteľnostiam XSS. Tento scenár som už videl na stránkach s viacerými autormi, kde sa do atribútu HTML vložil zle filtrovaný „display_name“.
Naivný prístup (a prečo sa mu vyhnúť)
Čo často vidím: krátky kód, ktorý načíta možnosti prostredníctvom $_GET alebo nefiltrované atribúty, potom vypíše surový HTML. Typický príklad (nepoužívajte):
<?php
// ❌ Exemple volontairement mauvais : pas de sanitization, pas d'escaping, requête non bornée.
add_shortcode('author_box', function($atts) {
$atts = shortcode_atts([
'user' => 1,
'count' => 5,
'title' => 'Auteur'
], $atts);
$user = get_user_by('id', $atts['user']);
echo '<div class="author-box">';
echo '<h3>' . $atts['title'] . '</h3>';
echo '<p>' . $user->display_name . '</p>';
echo '</div>';
});
Konkrétne problémy:
- Zabezpečenie :
$atts['title']et$user->display_nameodchádzajú bezesc_html(). - perf žiadna vyrovnávacia pamäť, žiadne prísne hranice a riskujete načítanie opakovaných požiadaviek na stránke Elementoru.
- UX : v Elementore editor nevidí natívne ovládacie prvky (žiadna typografia/farby bez vlastného CSS).
- údržba : nakoniec získate 12 krátkych kódov, každý s vlastnou logikou a celkovým CSS.
Správny prístup – podrobný návod
Krok 1 – Vytvorte minimálny doplnok
Vytvorte priečinok: wp-content/plugins/bpcab-elementor-widgets
Vytvorte hlavný súbor: wp-content/plugins/bpcab-elementor-widgets/bpcab-elementor-widgets.php
Krok 2 – Overte, či je Elementor načítaný (a v správnom čase)
Klasické úskalie: príliš skoré uloženie widgetu (napr. na init) a dostanete chybu typu Trieda „ElementorWidget_Base“ sa nenašlaPripájame sa k špecializovaným háčikom Elementor.
Krok 3 – Vytvorte widget „Panel autorov + Najnovšie príspevky“
Napíšeme widget, ktorý:
- Vyberie používateľa (autora) pomocou ovládacieho prvku.
- Zobrazuje avatar + meno + životopis (voliteľné).
- Zoznam jeho najnovších článkov (počet je konfigurovateľný).
- Zobrazuje možnosti štýlu (farby, typografia, medzery).
Krok 4 – Načítajte CSS/JS iba v prípade potreby
Elementor umožňuje deklarovať závislosti prostredníctvom get_style_depends() et get_script_depends()Podľa mojej skúsenosti je to veľká výhoda: vyhnete sa načítaniu globálneho súboru CSS na všetkých stránkach.
Krok 5 – Pridajte ovládacie prvky štýlu Elementoru
Na generovanie CSS s rozsahom widgetu sa použijú „selektory“ Elementoru. Tým sa zabráni kódovaniu desiatok tried a znížia sa konflikty s Avadou/Divi.
Krok 6 – Bezpečné a robustné vykresľovanie
Kľúčové body :
- Nastavenia dezinfekcie:
absint,sanitize_text_field,esc_url_rawak je to potrebné. - Únik na strane východu:
esc_html,esc_attr,esc_url. - Záložné možnosti: autor sa nenašiel, žiadne články, prázdny životopis.
Úplný kód
Skopírujte a vložte tak, ako je. Tento plugin ukladá 1 widget Elementoru. Je zámerne kompaktný, ale kompletný a pripravený na testovanie.
1) Hlavný súbor pluginu
<?php
/**
* Plugin Name: BPCAB - Widgets Elementor (Exemple)
* Description: Exemple pédagogique : widget Elementor personnalisé (encart auteur + derniers articles).
* Version: 1.0.0
* Requires at least: 6.9
* Requires PHP: 8.1
* Author: BPCAB
*
* Sécurité : ce plugin est un exemple. Testez en staging avant production.
*/
declare(strict_types=1);
if (!defined('ABSPATH')) {
exit;
}
final class BPCAB_Elementor_Widgets_Plugin {
private const MIN_PHP = '8.1';
public static function init(): void {
add_action('plugins_loaded', [__CLASS__, 'bootstrap']);
}
public static function bootstrap(): void {
// Vérif PHP (utile si le site est downgradé par erreur).
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
add_action('admin_notices', [__CLASS__, 'notice_php_version']);
return;
}
// Ne rien faire si Elementor n'est pas actif.
if (!did_action('elementor/loaded')) {
add_action('admin_notices', [__CLASS__, 'notice_elementor_missing']);
return;
}
// Enregistrer le widget au bon hook.
add_action('elementor/widgets/register', [__CLASS__, 'register_widgets']);
// Enregistrer les assets (CSS/JS) utilisables par les widgets.
add_action('wp_enqueue_scripts', [__CLASS__, 'register_front_assets']);
}
public static function register_front_assets(): void {
$ver = '1.0.0';
wp_register_style(
'bpcab-author-box',
plugins_url('assets/author-box.css', __FILE__),
[],
$ver
);
// JS optionnel : ici on ne fait rien de critique, mais c'est prêt si vous en avez besoin.
wp_register_script(
'bpcab-author-box',
plugins_url('assets/author-box.js', __FILE__),
[],
$ver,
true
);
}
public static function register_widgets($widgets_manager): void {
// Charger la classe du widget.
require_once __DIR__ . '/widgets/class-bpcab-author-box-widget.php';
// Elementor 3.x+ : register() existe sur le manager.
$widgets_manager->register(new BPCAB_Author_Box_Widget());
}
public static function notice_elementor_missing(): void {
if (!current_user_can('activate_plugins')) {
return;
}
$plugin_page = admin_url('plugins.php');
echo '<div class="notice notice-warning"><p>';
echo esc_html__('BPCAB - Widgets Elementor : Elementor n’est pas actif. Activez Elementor pour charger le widget.', 'bpcab');
echo ' ';
echo '<a href="' . esc_url($plugin_page) . '">' . esc_html__('Aller aux extensions', 'bpcab') . '</a>';
echo '</p></div>';
}
public static function notice_php_version(): void {
if (!current_user_can('manage_options')) {
return;
}
echo '<div class="notice notice-error"><p>';
echo esc_html__('BPCAB - Widgets Elementor : PHP 8.1+ est requis.', 'bpcab');
echo '</p></div>';
}
}
BPCAB_Elementor_Widgets_Plugin::init();
2) Trieda widgetov
Vytvoriť: wp-content/plugins/bpcab-elementor-widgets/widgets/class-bpcab-author-box-widget.php
<?php
declare(strict_types=1);
if (!defined('ABSPATH')) {
exit;
}
use ElementorWidget_Base;
use ElementorControls_Manager;
use ElementorGroup_Control_Typography;
use ElementorGroup_Control_Border;
use ElementorGroup_Control_Box_Shadow;
final class BPCAB_Author_Box_Widget extends Widget_Base {
public function get_name(): string {
return 'bpcab_author_box';
}
public function get_title(): string {
return esc_html__('Encart Auteur (BPCAB)', 'bpcab');
}
public function get_icon(): string {
// Icône Elementor (dashicons-like). Vous pouvez la changer.
return 'eicon-user-circle-o';
}
public function get_categories(): array {
// Catégorie standard. Vous pouvez créer votre propre catégorie si besoin.
return ['general'];
}
public function get_keywords(): array {
return ['author', 'auteur', 'bio', 'posts', 'bpcab'];
}
public function get_style_depends(): array {
return ['bpcab-author-box'];
}
public function get_script_depends(): array {
return ['bpcab-author-box'];
}
protected function register_controls(): void {
// SECTION : Contenu
$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__('À propos de l’auteur', 'bpcab'),
'placeholder' => esc_html__('Ex : À propos de Marie', 'bpcab'),
'label_block' => true,
]
);
// Contrôle simple : ID utilisateur (numérique).
// Variante plus avancée : select2 alimenté en AJAX (hors scope), ou select statique.
$this->add_control(
'user_id',
[
'label' => esc_html__('ID utilisateur (auteur)', 'bpcab'),
'type' => Controls_Manager::NUMBER,
'min' => 1,
'step' => 1,
'default' => (int) get_current_user_id(),
'description' => esc_html__('Astuce : récupérez l’ID dans Utilisateurs > Tous les utilisateurs.', 'bpcab'),
]
);
$this->add_control(
'show_bio',
[
'label' => esc_html__('Afficher la bio', 'bpcab'),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__('Oui', 'bpcab'),
'label_off' => esc_html__('Non', 'bpcab'),
'return_value' => 'yes',
'default' => 'yes',
]
);
$this->add_control(
'show_posts',
[
'label' => esc_html__('Afficher les derniers articles', 'bpcab'),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__('Oui', 'bpcab'),
'label_off' => esc_html__('Non', 'bpcab'),
'return_value' => 'yes',
'default' => 'yes',
]
);
$this->add_control(
'posts_count',
[
'label' => esc_html__('Nombre d’articles', 'bpcab'),
'type' => Controls_Manager::NUMBER,
'min' => 1,
'max' => 12,
'step' => 1,
'default' => 3,
'condition' => [
'show_posts' => 'yes',
],
]
);
$this->add_control(
'profile_url',
[
'label' => esc_html__('URL du profil (optionnel)', 'bpcab'),
'type' => Controls_Manager::URL,
'placeholder' => 'https://',
'show_external' => true,
'description' => esc_html__('Si vide, le nom n’est pas cliquable.', 'bpcab'),
]
);
$this->end_controls_section();
// SECTION : Style (encart)
$this->start_controls_section(
'section_style_box',
[
'label' => esc_html__('Style : Encart', 'bpcab'),
'tab' => Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'box_bg',
[
'label' => esc_html__('Couleur de fond', 'bpcab'),
'type' => Controls_Manager::COLOR,
'default' => '',
'selectors' => [
'{{WRAPPER}} .bpcab-author-box' => 'background-color: {{VALUE}};',
],
]
);
$this->add_responsive_control(
'box_padding',
[
'label' => esc_html__('Padding', 'bpcab'),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => ['px', 'em', 'rem', '%'],
'selectors' => [
'{{WRAPPER}} .bpcab-author-box' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
]
);
$this->add_group_control(
Group_Control_Border::get_type(),
[
'name' => 'box_border',
'selector' => '{{WRAPPER}} .bpcab-author-box',
]
);
$this->add_group_control(
Group_Control_Box_Shadow::get_type(),
[
'name' => 'box_shadow',
'selector' => '{{WRAPPER}} .bpcab-author-box',
]
);
$this->end_controls_section();
// SECTION : Style (typos)
$this->start_controls_section(
'section_style_text',
[
'label' => esc_html__('Style : Texte', 'bpcab'),
'tab' => Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'title_color',
[
'label' => esc_html__('Couleur du titre', 'bpcab'),
'type' => Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .bpcab-author-box__title' => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'title_typography',
'selector' => '{{WRAPPER}} .bpcab-author-box__title',
]
);
$this->add_control(
'name_color',
[
'label' => esc_html__('Couleur du nom', 'bpcab'),
'type' => Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .bpcab-author-box__name' => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'name_typography',
'selector' => '{{WRAPPER}} .bpcab-author-box__name',
]
);
$this->add_control(
'bio_color',
[
'label' => esc_html__('Couleur de la bio', 'bpcab'),
'type' => Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .bpcab-author-box__bio' => 'color: {{VALUE}};',
],
'condition' => [
'show_bio' => 'yes',
],
]
);
$this->end_controls_section();
}
protected function render(): void {
$settings = $this->get_settings_for_display();
$title = isset($settings['title']) ? sanitize_text_field((string) $settings['title']) : '';
$user_id = isset($settings['user_id']) ? absint($settings['user_id']) : 0;
$show_bio = (!empty($settings['show_bio']) && $settings['show_bio'] === 'yes');
$show_posts = (!empty($settings['show_posts']) && $settings['show_posts'] === 'yes');
$posts_count = isset($settings['posts_count']) ? absint($settings['posts_count']) : 3;
$posts_count = max(1, min(12, $posts_count));
$user = $user_id ? get_user_by('id', $user_id) : false;
echo '<div class="bpcab-author-box">';
if ($title !== '') {
echo '<div class="bpcab-author-box__title">' . esc_html($title) . '</div>';
}
if (!$user instanceof WP_User) {
// Fallback propre : évite une box vide.
echo '<p>' . esc_html__('Auteur introuvable (vérifiez l’ID utilisateur).', 'bpcab') . '</p>';
echo '</div>';
return;
}
$display_name = (string) $user->display_name;
$description = (string) get_user_meta((int) $user->ID, 'description', true);
$avatar = get_avatar((int) $user->ID, 96, '', $display_name, [
'class' => 'bpcab-author-box__avatar',
]);
// URL de profil optionnelle (Elementor URL control).
$profile_url = '';
$profile_is_external = false;
$profile_nofollow = false;
if (!empty($settings['profile_url']) && is_array($settings['profile_url'])) {
$profile_url = !empty($settings['profile_url']['url']) ? esc_url($settings['profile_url']['url']) : '';
$profile_is_external = !empty($settings['profile_url']['is_external']);
$profile_nofollow = !empty($settings['profile_url']['nofollow']);
}
echo '<div class="bpcab-author-box__header">';
echo '<div class="bpcab-author-box__avatar-wrap">' . $avatar . '</div>';
echo '<div class="bpcab-author-box__meta">';
$name_html = '<span class="bpcab-author-box__name">' . esc_html($display_name) . '</span>';
if ($profile_url) {
$rel = [];
if ($profile_is_external) {
// target blank sans noopener = vulnérable.
// Elementor gère souvent ça côté UI, mais on le force côté rendu.
$rel[] = 'noopener';
}
if ($profile_nofollow) {
$rel[] = 'nofollow';
}
$target = $profile_is_external ? ' target="_blank"' : '';
$rel_attr = !empty($rel) ? ' rel="' . esc_attr(implode(' ', array_unique($rel))) . '"' : '';
$name_html = '<a class="bpcab-author-box__name bpcab-author-box__name--link" href="' . esc_url($profile_url) . '"' . $target . $rel_attr . '>' . esc_html($display_name) . '</a>';
}
echo $name_html;
if ($show_bio && $description !== '') {
// Bio : autoriser un sous-ensemble HTML ? Ici on reste strict : texte simple.
echo '<div class="bpcab-author-box__bio">' . esc_html($description) . '</div>';
}
echo '</div>'; // meta
echo '</div>'; // header
if ($show_posts) {
$posts = get_posts([
'post_type' => 'post',
'post_status' => 'publish',
'author' => (int) $user->ID,
'numberposts' => $posts_count,
'no_found_rows' => true,
'ignore_sticky_posts' => true,
'suppress_filters' => false,
]);
echo '<div class="bpcab-author-box__posts">';
if (!empty($posts)) {
echo '<ul class="bpcab-author-box__posts-list">';
foreach ($posts as $post) {
$permalink = get_permalink($post);
$post_title = get_the_title($post);
echo '<li class="bpcab-author-box__posts-item">';
echo '<a class="bpcab-author-box__posts-link" href="' . esc_url($permalink) . '">' . esc_html($post_title) . '</a>';
echo '</li>';
}
echo '</ul>';
} else {
echo '<p class="bpcab-author-box__empty">' . esc_html__('Aucun article récent.', 'bpcab') . '</p>';
}
echo '</div>';
}
echo '</div>'; // box
}
// Optionnel : aperçu dans l’éditeur (JS template). On le laisse simple.
// Si vous ne le faites pas, Elementor affichera un rendu serveur en preview (souvent suffisant).
protected function content_template(): void {}
}
3) CSS widgetu
Vytvoriť: wp-content/plugins/bpcab-elementor-widgets/assets/author-box.css
.bpcab-author-box{
display:block;
border-radius:12px;
background:#fff;
}
.bpcab-author-box__title{
font-weight:700;
margin:0 0 12px 0;
}
.bpcab-author-box__header{
display:flex;
gap:12px;
align-items:flex-start;
}
.bpcab-author-box__avatar{
border-radius:999px;
display:block;
}
.bpcab-author-box__meta{
display:block;
min-width:0;
}
.bpcab-author-box__name{
display:inline-block;
font-weight:700;
text-decoration:none;
}
.bpcab-author-box__bio{
margin-top:6px;
opacity:.9;
}
.bpcab-author-box__posts{
margin-top:14px;
}
.bpcab-author-box__posts-list{
margin:0;
padding-left:18px;
}
.bpcab-author-box__posts-item{
margin:6px 0;
}
4) JS (voliteľné)
Vytvoriť: wp-content/plugins/bpcab-elementor-widgets/assets/author-box.js
/* Fichier volontairement vide.
Gardez-le si vous prévoyez d'ajouter des interactions.
Sinon, supprimez get_script_depends() et l'enregistrement du script. */
Vysvetlenie kódu
Čo sa deje v zákulisí (jednoduchá verzia)
Plugin čaká na načítanie Elementoru. Potom uloží widget. Elementor tento widget zobrazí v editore a keď ho presuňte na stránku, Elementor:
- zobrazuje vaše ovládacie prvky (obsah + štýl),
- ukladá nastavenia do JSON súboru stránky (post meta),
- volanie
render()na vytvorenie HTML kódu pre front-end.
Prečo práve tieto háčiky a nie iné?
plugins_loadedJe vhodný čas skontrolovať prostredie a prítomnosť Elementoru.did_action('elementor/loaded'): zabraňuje volaniu tried Elementoru pred ich automatickým načítaním.elementor/widgets/register: hook určený na registráciu widgetov. Toto je ten, ktorý zabraňuje chybám „Trieda sa nenašla“.wp_register_stylePripravujeme aktíva, ale nespájame ich globálne.
Sanitizácia verzus únik (chyby, ktoré vidím najčastejšie)
Dve pravidlá:
- sanitácia : keď normalizujete hodnotu (napr.:
absintpre občiansky preukaz,sanitize_text_field(pre titul). - unikajúci : pri tlači v HTML (napr.:
esc_html,esc_url,esc_attr).
Vo widgete sú nastavenia v čase vykresľovania vyčistené a potom systematicky uložené v čase zobrazenia. Áno, Elementor už väčšinou ukladá „čisté“ hodnoty, ale nespoliehajte sa naň: import JSON, kopírovanie a vkladanie alebo plugin tretej strany môžu vložiť neočakávané reťazce.
Podmienené načítanie aktív
get_style_depends() vráti uložený popisovač štýlu. Elementor načíta tento štýl iba v prípade, že je widget prítomný na stránke. Ide o jednoduchý vzor, ktorý sa vyhýba CSS na úrovni celej stránky.
Žiadosť o najnovšie články
Používame get_posts() s:
no_found_rowsžiadne stránkovanie, takže nie je potrebné počítať.ignore_sticky_posts: vyhýba sa prekvapivým výsledkom.numberpostsobmedzené na 12: ponecháva „ľahký“ widget.
Možná alternatíva: WP_Query ak potrebujete väčšiu kontrolu, ale tu get_posts() dostatočné a zostáva čitateľné.
Varianty a prípady použitia
Variant 1 – Užívateľsky prívetivejší výber autora (rozbaľovací zoznam)
Ovládací prvok „ID používateľa“ je užitočný ako príklad, ale vaši vydavatelia vás budú preklínať. Jednoduchšia alternatíva: zostavte si zoznam možností z používateľov (buďte opatrní pri stránkach s tisíckami používateľov).
Dans register_controls()vymeňte ovládanie user_id od:
<?php
// ✅ Variante : select (attention : coûteux si beaucoup d'utilisateurs).
$users = get_users([
'fields' => ['ID', 'display_name'],
'number' => 200, // borne volontaire
'orderby' => 'display_name',
'order' => 'ASC',
]);
$options = [];
foreach ($users as $u) {
$options[(string) $u->ID] = $u->display_name . ' (#' . $u->ID . ')';
}
$this->add_control(
'user_id',
[
'label' => esc_html__('Auteur', 'bpcab'),
'type' => Controls_Manager::SELECT,
'options' => $options,
'default' => (string) get_current_user_id(),
]
);
Okrajový prípad: Na členskej stránke s 50 000 účtami sa tento SELECT stane nepoužiteľným. V tomto prípade prepnite na ovládací prvok AJAX (Select2) alebo vynútite pole „ID“ pomocou používateľského rozhrania (alebo vyhľadávania REST).
Možnosť 2 – Namiesto článkov zobrazte CPT (napr. „portfólio“)
Jednoducho zmeňte 'post_type' => 'post' en 'post_type' => 'portfolio' (alebo tabuľku). Zvážte, či by ste typ príspevku nemali sprístupniť pomocou SELECT ak ich máte viacero.
Variant 3 – Ľahké ukladanie do vyrovnávacej pamäte (prechodné) podľa autora
Ak sa váš widget na stránke použije 10-krát (toto sa stáva na vstupných stránkach „tímu“), môžete skryť zoznam príspevkov. Zjednodušený príklad:
<?php
$cache_key = 'bpcab_ab_posts_' . (int) $user->ID . '_' . (int) $posts_count;
$posts = get_transient($cache_key);
if ($posts === false) {
$posts = get_posts([
'post_type' => 'post',
'post_status' => 'publish',
'author' => (int) $user->ID,
'numberposts' => $posts_count,
'no_found_rows' => true,
'ignore_sticky_posts' => true,
]);
// Cache 10 minutes.
set_transient($cache_key, $posts, 10 * MINUTE_IN_SECONDS);
}
Poznámka: Ak publikujete často, vyrovnávacia pamäť môže oneskoriť zobrazenie. Na stránke s vysokou návštevnosťou budete uprednostňovať perzistentnú vyrovnávaciu pamäť objektov (Redis) a zneplatnenie (hooky). save_post) namiesto pevného TTL.
Kompatibilita s Divi 5 / Elementor / Avada
Elementor (editor a front-end)
- Widget sa zobrazí v kategórii „Všeobecné“.
- Štýly sú vymedzené prostredníctvom
{{WRAPPER}}čo obmedzuje kolízie CSS. - Ak používate generovanie vyrovnávacej pamäte/CSS z Elementoru, po vykonaní zmien ju vymažte.
Divi 5
Divi nepoužíva rozhranie Elementor Widget API. Váš widget nebude k dispozícii v Divi Builderi.
Realistický prístup, ak musíte podporovať aj Divi 5:
- Zverejnite „kompatibilný“ shortcode, ktorý opätovne používa rovnakú logiku PHP (zdieľanú funkciu), a potom ho vložte pomocou modulu Divi Code/Shortcode.
- Alebo si vytvorte vyhradený modul Divi 5 (čistejší, dlhší).
Avada (výrobca fúzií)
Rovnaká logika: Avada nepoužíva widgety Elementoru. Pre Avada:
- Kompatibilné s krátkymi kódmi (Fusion Builder ich vie integrovať).
- Alebo vlastný prvok Fusion, ak potrebujete natívne používateľské rozhranie Avada.
Tip pre „viacero staviteľov“, ktorý často používam
Uchovávajte obchodnú logiku v samostatnej PHP triede (napr. BPCAB_Author_Box_Renderer) a vyrobte adaptéry:
- Widget Elementor → volá renderer.
- Skrátený kód → volá renderer.
- Blokovať Gutenberg dynamický → volá renderer.
Vyhnete sa duplicitnej logike dotazov, záložným metódam a escapu.
Kontroly po inštalácii
- Aktivujte plugin v Rozšírenie.
- Otvorte stránku pomocou Elementoru.
- Vyhľadajte „Vloženie autora (BPCAB)“.
- Presuňte widget a zadajte platné ID používateľa.
- Skontrolujte prednú stranu:
- zobrazený avatar
- zobrazovaný názov (klikateľný, ak je zadaná URL adresa),
- Zobrazenie životopisu, ak je aktivované
- Zoznam zobrazených a obmedzených položiek.
- Otestujte ovládacie prvky štýlu: farbu pozadia, typografiu názvu, odsadenie.
Ak máte systém vyrovnávacej pamäte (doplnok vyrovnávacej pamäte, Cloudflare, vyrovnávacia pamäť servera), vymažte ho. Často som videl vývojárov, ktorí si mysleli, že „widget sa nenačítava“, pričom v skutočnosti front-end pred aktiváciou zobrazoval skrytú HTML stránku.
Ak to nefunguje
Stručný kontrolný zoznam (v poradí)
- Chyba 500 po aktivácii pohľad
wp-content/debug.log(alebo protokoly servera). - Neviditeľný widget v Elementore Je Elementor aktívny? Háčik
elementor/widgets/registerJe postihnutý? - Trieda sa nenašla Pravdepodobne ste načítali triedu príliš skoro alebo ste načítali cestu
require_onceje nepravdivé. - CSS nebol použitý Je súbor v správnej ceste? Popisovač
bpcab-author-boxJe to správne uložené? Vymažte vyrovnávaciu pamäť Elementoru. - Nič sa nezobrazuje : otestujte existujúce ID používateľa a potom vypnite možnosť „Zobraziť najnovšie články“, aby ste izolovali dopyt.
Diagnostický graf
| symptóm | Príčina pravdepodobná | overenie | Riešenie |
|---|---|---|---|
| Chýba widget v zozname Elementoru | Hook nebol vykonaný / Elementor nebol načítaný | Pozrite sa, či did_action('elementor/loaded') je pravda (protokol), skontrolujte rozšírenia |
Aktivujte Elementor, vyhnite sa init, použite elementor/widgets/register |
| Chyba „Trieda 'ElementorWidget_Base' sa nenašla“ | Trieda sa zaplnila príliš skoro | Trasovanie zásobníka v debug.log |
Presunúť nahrávku do elementor/widgets/register a udržať require_once v |
| CSS sa nenačítal | Popisovač nie je zaregistrovaný alebo je nesprávna cesta | Na karte Sieť vyhľadajte author-box.css |
Správne plugins_url()skontrolovať get_style_depends()vymazať vyrovnávaciu pamäť |
| Zobrazuje sa meno autora, ale nie avatar | Gravatar zablokovaný / konfigurácia avatara | Nastavenia > Chat > Avatary alebo sieťové obmedzenia | Povoliť Gravatar alebo použiť lokálny avatar (plugin) / záložnú verziu |
| Prázdny zoznam položiek | Autor bez publikovaných príspevkov / chybný typ príspevku | Skontrolujte autora príspevkov a stav „Publikované“. | Zmeniť ID, publikovať príspevok, upraviť post_type |
| Neviditeľné zmeny | Vyrovnávacia pamäť prehliadača / vyrovnávacia pamäť stránky / vyrovnávacia pamäť CSS Elementoru | Test v režime súkromného prehliadania + vymazanie vyrovnávacej pamäte pluginu + regenerácia CSS | Vymazať vyrovnávacie pamäte, obnoviť súbory CSS Elementoru, ak je to povolené |
Časté úskalia a chyby
| Chyba | Spôsobiť | Riešenie |
|---|---|---|
Skopírujte kód do functions.php témy |
Nesprávne miesto: téma sa môže zmeniť a poradie načítavania môže poškodiť Elementor | Použite špecializovaný plugin (ako je tento) alebo MU-plugin |
| Zabudnutie bodkočiarky v triede widgetu | Závažná chyba PHP | umožniť WP_DEBUG_LOG, znova si prečítajte uvedený riadok, použite IDE |
použitie add_action('init', ...) uložiť widget |
Elementor ešte nie je načítaný | použitie elementor/widgets/register a test did_action('elementor/loaded') |
| CSS/JS „nenájdené“ | Nesprávna cesta v plugins_url() alebo súbor nebol vytvorený |
Skontrolujte štruktúru adresárov a presné názvy súborov |
| Štýlový konflikt s témou (Avada/Divi) | CSS je príliš všeobecné (napr.: .title) |
Pridajte predponu k svojim triedam (bpcab-) a použite {{WRAPPER}} v selektoroch Elementoru |
| Chyba po aktualizácii PHP | Prísne typovanie + nekompatibilný starý kód | Ponechajte PHP 8.1+ a opravte upozornenia/chyby typu (logy) |
| Testovanie priamo v produkcii | Riziko bielej obrazovky | Príprava na staging + verziované nasadenie + vrátenie zmien |
| Zmätok medzi dezinfekciou a únikom | Predpokladané „čisté“ údaje | Systematicky dezinfikujte vchody, evakuujte východy |
Tipy na bezpečnosť, výkon a údržbu
- XSS zabezpečenie : uniknúť každému ukončeniu podľa kontextu (
esc_htmltext,esc_urladresy URL,esc_attratribút). Referencia: developer.wordpress.org/apis/security/escaping - Oprávnenie Tu nie je žiadny front-endový formulár, takže žiadne jednorazové číslo. Ak pridáte akcie (napr. tlačidlo „sledovať autora“), použite
wp_nonce_fielda skontrolujtecurrent_user_can. - výkon Obmedzte počet príspevkov a povoľte ukladanie do vyrovnávacej pamäte, ak sa widget opakuje. Na veľkých webových stránkach sa vyhnite
get_users()bez obmedzení. - kompatibilita Udržujte svoje triedy a identifikátory s predponami. Toto je najlepšia ochrana pred kolíziami názvov s inými doplnkami Elementoru.
- údržba Upravte verziu pluginu (Git) a pridajte zoznam zmien. Vyhnite sa „vloženým“ úryvkom v plugine pre úryvky: Videl som aktualizácie, ktoré narušili automaticky načítané triedy, pretože plugin pre úryvky zmenil poradie vykonávania.
- SEO Tento widget nepridáva skrytý obsah, takže nehrozí žiadna SEO pasca. Buďte opatrní, ak pridávate podmienený obsah iba pomocou JS.
zdroje
- Príručka pre vývojárov pluginov pre WordPress
- Bezpečnostné API pre WordPress (validácia, sanitizácia, escaping)
- referencia funkcie wp_register_style()
- referencia funkcie get_posts()
- Referencia get_avatar()
- Oficiálna stránka Elementoru na wordpress.org
- Zrkadlo jadra WordPressu na GitHub (wordpress-develop)
- WordPress Core Trac
- Poznámky k vydaniu PHP 8.1
Často kladené otázky
Prečo vytvoriť plugin namiesto vloženia kódu do podradenej témy?
Pretože widget Elementoru je funkcia. Ak zmeníte témy (alebo ak sa nahradí Avada/Divi), chcete widget zachovať. A tiež sa vyhnete prekvapeniam s poradím načítavania.
Funguje tento widget, ak nie je nainštalovaný Elementor Pro?
Áno. Kód používa rozhranie API widgetov Elementor (bezplatná verzia). Elementor Pro ponúka ďalšie funkcie (Tvorca tém atď.), ale tento widget je od neho nezávislý.
Prečo nepoužiť shortcode vo widgete „Shortcode“ v Elementore?
Stratíte väčšinu natívnych ovládacích prvkov štýlu a nakoniec budete musieť vložiť globálny CSS. Pre opakujúci sa komponent je widget prehľadnejší.
Ako pridám v Elementore kategóriu „BPCAB“ namiesto „Všeobecné“?
Vyhradenú kategóriu Elementoru si môžete uložiť pomocou hookov kategórií (v závislosti od verzie Elementoru). Robím to, keď mám 5+ widgetov; inak sa držím možnosti „Všeobecné“, aby som sa vyhol fragmentácii používateľského rozhrania.
Pourquoi get_settings_for_display() a nie get_settings() ?
get_settings_for_display() vracia hodnoty pripravené na zobrazenie (s niektorými internými transformáciami Elementoru). Toto je vo všeobecnosti správna voľba v render().
Ako sa vyhnúť zadávaniu používateľského ID (nie je to užívateľsky prívetivé)?
použite SELECT Obmedzte prístup (maximálne 200 používateľov) alebo implementujte ovládací prvok AJAX. Na stránkach s vysokou návštevnosťou je niekedy najstabilnejším riešením pole ID + interná dokumentácia.
Prečo sa môj CSS po úprave neaktualizuje?
Vymazať vyrovnávaciu pamäť:
- vyrovnávacia pamäť prehliadača
- vyrovnávacia pamäť vášho pluginu pre vyrovnávaciu pamäť,
- a ak používate generovanie CSS od Elementoru, vynútite regeneráciu (podľa vašej konfigurácie).
Dá sa tento widget použiť v šablóne Elementor Theme Builder (jeden príspevok)?
Áno. Je to vlastne dobrý prípad použitia: pole s menom autora v dolnej časti článku. V takom prípade vylepšite logiku tak, aby sa použilo meno autora aktuálneho príspevku, ak... user_id je prázdny (jednoduchý variant na pridanie).
Ako môžem prispôsobiť widget tak, aby automaticky zobrazoval autora aktuálneho príspevku?
Dans render()a $user_id hodnota 0, získať get_post_field('post_author', get_the_ID()) Keď sa nachádzate v slučke/šablóne, buďte opatrní so statickými stránkami, ktorým chýba kontext príspevku.
Je tento kód kompatibilný s PHP 8.2/8.3/8.4?
Áno, v zásade (moderná syntax, striktné typovanie). Hlavný bod, na ktorý si treba dať pozor, je kompatibilita s Elementorom a zastarané upozornenia z iných pluginov.
Ako môžem správne otestovať bez toho, aby som poškodil stránku?
Pripravené testovanie, povolené protokoly a jednoduchá metóda: aktivujte doplnok, umiestnite widget na testovaciu stránku, skontrolujte rozhranie a potom otestujte prípad „autor sa nenašiel“ (neexistujúce ID) na overenie záložných riešení.