
🛠 Hur man skapar ett widget-plugin för WordPress: en steg-för-steg-guide
💡 Så bygger du ett widget-plugin för WordPress
- Skapa en mapp
my-widget-pluginiwp-content/plugins/och en filmy-widget-plugin.phpmed plugin-huvudet, aktivera sedan i adminpanelen - Deklarera en klass som ärver
WP_Widgetoch åsidosätt metoderna__construct(),form(),update()ochwidget() - Registrera widgeten med funktionen
register_widget()på hookenwidgets_initså att WordPress kan se den i listan - I
form()-metoden renderar du inställningsfälten; iupdate()sanerar och sparar du data viasanitize_text_field() - I
widget()-metoden skriver du ut innehåll på frontend och escape:ar värden medesc_html()ochwp_kses_post()
Steg 1: skapa plugin-stommen
En widget i WordPress är inte bara en rad i functions.php. Om du vill att inställningar ska sparas, adminformuläret ska fungera och widgeten ska överleva nästa temauppdatering, måste du paketera den som ett plugin. Det isolerar koden och gör widgeten oberoende av temabyten.
Börja med en tom mapp. Navigera till wp-content/plugins/ och skapa en katalog som heter my-widget-plugin. Skapa en fil med namnet my-widget-plugin.php inuti den. Det är den här filen WordPress läser först vid aktivering. Öppna filen och lägg till standardhuvudet för plugins:
1 <?php 2 /* 3 Plugin Name: My Widget Plugin 4 Plugin URI: https://www.wpexplorer.com/create-widget-plugin-wordpress/ 5 Description: Adds a customizable widget with text, textarea, checkbox, and dropdown. 6 Version: 1.0 7 Author: AJ Clarke 8 Author URI: https://www.wpexplorer.com/ 9 License: GPL2 10 */
Spara filen. Gå nu till WordPress adminpanel → Plugins. Om pluginet dyker upp i listan är stommen klar. Klicka på "Aktivera". Det gör inget användbart än, vi har bara meddelat att det finns. Men WordPress vet redan att pluginet existerar och är redo att köra dess kod. Det här är en viktig princip: registrering först, logik sedan.
Hur pluginet fungerar internt, vilka hooks som körs vid aktivering och hur WordPress hittar din fil tas upp i videon ovan. Nu går vi vidare till den mest intressanta delen: widget-klassen.
Steg 2: registrera widgeten via WP_Widget
WordPress har en inbyggd klass som heter WP_Widget; den har funnits i kärnan sedan version 2.8 och är fortfarande grunden för alla anpassade widgetar. Du behöver inte skriva sparlogik, fältgenerering eller registrering från grunden: ärv bara klassen och åsidosätt fyra metoder.
Lägg till den här koden i din my-widget-plugin.php direkt efter plugin-huvudet, före avslutande ?>:
1 // Widget class 2 class My_Custom_Widget extends WP_Widget { 3 4 public function __construct() { 5 parent::__construct( 6 'my_custom_widget', 7 __( 'My Custom Widget', 'text_domain' ), 8 array( 9 'customize_selective_refresh' => true, 10 ) 11 ); 12 } 13 14 public function form( $instance ) { 15 /* ... admin form ... */ 16 } 17 18 public function update( $new_instance, $old_instance ) { 19 /* ... saving settings ... */ 20 } 21 22 public function widget( $args, $instance ) { 23 /* ... frontend output ... */ 24 } 25 } 26 27 // Widget registration 28 function my_register_custom_widget() { 29 register_widget( 'My_Custom_Widget' ); 30 } 31 add_action( 'widgets_init', 'my_register_custom_widget' );
Låt oss bryta ner vad som händer här. Klassen My_Custom_Widget ärver WP_Widget, vilket ger dig färdiga metoder som get_field_id() och get_field_name() för att generera formulärfältsattribut. I konstruktorn skickar vi tre saker till föräldraklassen: ett unikt widget-ID (gemener, inga mellanslag, my_custom_widget), dess läsbara namn (funktionen __() gör det översättningsbart) och en options-array. Parametern customize_selective_refresh => true gör att widgeten kan uppdateras i anpassaren utan att ladda om hela sidan, en liten detalj som sparar mycket frustration under konfigurationen.
Funktionen my_register_custom_widget() anropar register_widget() på hooken widgets_init. Det är så WordPress får kännedom om din widget. Utan den här raden kommer inget att synas i adminpanelen.
Nu fyller vi metoderna form(), update() och widget() med riktig logik.
Steg 3: skapa widgetformuläret i adminpanelen
Formuläret är vad administratören ser när widgeten dras in i en sidopanel. Det består av fält: textinmatningar, dropdowns, kryssrutor. Varje fält måste kunna spara sitt värde och visa det aktuella värdet när det öppnas igen.
3.1. Funktionen form() och inmatningsfält
Lägg till den här koden i din klass form()-metod. Den skapar fem fält: en titel, en textinmatning, en textarea, en kryssruta och en dropdown.
1 public function form( $instance ) { 2 3 $defaults = array( 4 'title' => '', 5 'text' => '', 6 'textarea' => '', 7 'checkbox' => '', 8 'select' => '', 9 ); 10 11 $args = wp_parse_args( (array) $instance, $defaults ); 12 $title = $args['title']; 13 $text = $args['text']; 14 $textarea = $args['textarea']; 15 $checkbox = $args['checkbox']; 16 $select = $args['select']; 17 ?> 18 19 <p> 20 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"> 21 <?php _e( 'Widget Title', 'text_domain' ); ?> 22 </label> 23 <input class="widefat" 24 id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" 25 name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" 26 type="text" 27 value="<?php echo esc_attr( $title ); ?>" /> 28 </p> 29 30 <p> 31 <label for="<?php echo esc_attr( $this->get_field_id( 'text' ) ); ?>"> 32 <?php _e( 'Text:', 'text_domain' ); ?> 33 </label> 34 <input class="widefat" 35 id="<?php echo esc_attr( $this->get_field_id( 'text' ) ); ?>" 36 name="<?php echo esc_attr( $this->get_field_name( 'text' ) ); ?>" 37 type="text" 38 value="<?php echo esc_attr( $text ); ?>" /> 39 </p> 40 41 <p> 42 <label for="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>"> 43 <?php _e( 'Textarea:', 'text_domain' ); ?> 44 </label> 45 <textarea class="widefat" 46 id="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>" 47 name="<?php echo esc_attr( $this->get_field_name( 'textarea' ) ); ?>"><?php 48 echo wp_kses_post( $textarea ); 49 ?></textarea> 50 </p> 51 52 <p> 53 <input id="<?php echo esc_attr( $this->get_field_id( 'checkbox' ) ); ?>" 54 name="<?php echo esc_attr( $this->get_field_name( 'checkbox' ) ); ?>" 55 type="checkbox" 56 value="1" 57 <?php checked( '1', $checkbox ); ?> /> 58 <label for="<?php echo esc_attr( $this->get_field_id( 'checkbox' ) ); ?>"> 59 <?php _e( 'Show additional block', 'text_domain' ); ?> 60 </label> 61 </p> 62 63 <p> 64 <label for="<?php echo $this->get_field_id( 'select' ); ?>"> 65 <?php _e( 'Display variant', 'text_domain' ); ?> 66 </label> 67 <select name="<?php echo $this->get_field_name( 'select' ); ?>" 68 id="<?php echo $this->get_field_id( 'select' ); ?>" 69 class="widefat"> 70 <?php 71 $options = array( 72 '' => __( '— Select —', 'text_domain' ), 73 'option_1' => __( 'Option 1', 'text_domain' ), 74 'option_2' => __( 'Option 2', 'text_domain' ), 75 'option_3' => __( 'Option 3', 'text_domain' ), 76 ); 77 foreach ( $options as $key => $name ) { 78 printf( 79 '<option value="%s" %s>%s</option>', 80 esc_attr( $key ), 81 selected( $select, $key, false ), 82 esc_html( $name ) 83 ); 84 } 85 ?> 86 </select> 87 </p> 88 89 <?php }
Var uppmärksam på två saker. För det första har vi ersatt den föråldrade funktionen extract() med direkt array-åtkomst. extract() har länge varit utesluten ur WordPress kodningsstandarder; den skapar variabler namngivna efter array-nycklar, vilket är osäkert och gör felsökning svårare. För det andra passerar varje utdatavärde genom esc_attr(), wp_kses_post() eller esc_html(). Det här är inte paranoia: data från databasen kan komma från varsomhelst, och sanering är obligatoriskt.
3.2. Funktionen update() för att spara
Metoden update() anropas när "Spara"-knappen klickas i widgetformuläret. Dess uppgift är att validera varje fält och returnera en sanerad array för att skrivas till databasen.
1 public function update( $new_instance, $old_instance ) { 2 $instance = $old_instance; 3 4 $instance['title'] = isset( $new_instance['title'] ) 5 ? sanitize_text_field( $new_instance['title'] ) : ''; 6 $instance['text'] = isset( $new_instance['text'] ) 7 ? sanitize_text_field( $new_instance['text'] ) : ''; 8 $instance['textarea'] = isset( $new_instance['textarea'] ) 9 ? wp_kses_post( $new_instance['textarea'] ) : ''; 10 $instance['checkbox'] = isset( $new_instance['checkbox'] ) ? 1 : false; 11 $instance['select'] = isset( $new_instance['select'] ) 12 ? sanitize_text_field( $new_instance['select'] ) : ''; 13 14 return $instance; 15 }
Här använder vi sanitize_text_field() istället för wp_strip_all_tags() eftersom den också normaliserar blanktecken, tar bort osynliga kontrolltecken och konverterar strängen till säker UTF-8. Det här är en mer grundlig rensning. wp_strip_all_tags() lämnar "rå" text med alla blankteckensartefakter intakta. För textformulärfält, välj alltid sanitize_text_field().
Textarean behåller wp_kses_post(): den tillåter grundläggande HTML (länkar, fet text, listor) men tar bort skript. Kryssrutan returnerar 1 eller false, vilket är läsbart och entydigt i databasen.
Steg 4: rendera widgeten på frontend
Funktionen widget() är vad besökaren ser. Den tar emot två parametrar: $args (widget-omslaget, taggar före och efter titeln och sidopanelen) och $instance (de sparade inställningarna för just den här instansen).
1 public function widget( $args, $instance ) { 2 3 $title = isset( $instance['title'] ) 4 ? apply_filters( 'widget_title', $instance['title'] ) : ''; 5 $text = isset( $instance['text'] ) ? $instance['text'] : ''; 6 $textarea = isset( $instance['textarea'] ) ? $instance['textarea'] : ''; 7 $select = isset( $instance['select'] ) ? $instance['select'] : ''; 8 $checkbox = ! empty( $instance['checkbox'] ) ? $instance['checkbox'] : false; 9 10 echo $args['before_widget']; 11 12 echo '<div class="widget-text wp_widget_plugin_box">'; 13 14 if ( $title ) { 15 echo $args['before_title'] . esc_html( $title ) . $args['after_title']; 16 } 17 18 if ( $text ) { 19 echo '<p>' . esc_html( $text ) . '</p>'; 20 } 21 22 if ( $textarea ) { 23 echo '<div class="widget-textarea">' . wp_kses_post( $textarea ) . '</div>'; 24 } 25 26 if ( $select ) { 27 echo '<p class="widget-select">' . esc_html( $select ) . '</p>'; 28 } 29 30 if ( $checkbox ) { 31 echo '<p class="widget-checkbox-result">' . esc_html__( 'Additional block activated', 'text_domain' ) . '</p>'; 32 } 33 34 echo '</div>'; 35 36 echo $args['after_widget']; 37 }
Nyckelpunkten här: vi har ersatt extract( $args ) med direkt åtkomst via $args['before_widget']. Anledningen är densamma: extract() är föråldrad och osäker. Vi har också slagit in utdatan i esc_html() där vanlig text förväntas (titeln, textsträngen, select-värdet). Textarean renderas genom wp_kses_post(), så om administratören infogade en länk eller fet text kommer de att bevaras.
CSS-klassen wp_widget_plugin_box låter dig styla blocket från temats stilmall. Byt gärna namn på den, se bara till att klassen är unik och inte krockar med temaklasser.
Fullständig plugin-kod
Låt oss sätta ihop allt. Här är den kompletta filen my-widget-plugin.php, redo att kopieras och aktiveras:
1 <?php 2 /* 3 Plugin Name: My Widget Plugin 4 Plugin URI: https://www.wpexplorer.com/create-widget-plugin-wordpress/ 5 Description: Adds a customizable widget with text, textarea, checkbox, and dropdown. 6 Version: 1.0 7 Author: AJ Clarke 8 Author URI: https://www.wpexplorer.com/ 9 License: GPL2 10 */ 11 12 class My_Custom_Widget extends WP_Widget { 13 14 public function __construct() { 15 parent::__construct( 16 'my_custom_widget', 17 __( 'My Custom Widget', 'text_domain' ), 18 array( 'customize_selective_refresh' => true ) 19 ); 20 } 21 22 public function form( $instance ) { 23 $defaults = array( 24 'title' => '', 'text' => '', 'textarea' => '', 25 'checkbox' => '', 'select' => '' 26 ); 27 $args = wp_parse_args( (array) $instance, $defaults ); 28 $title = $args['title']; 29 $text = $args['text']; 30 $textarea = $args['textarea']; 31 $checkbox = $args['checkbox']; 32 $select = $args['select']; 33 ?> 34 <p> 35 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"> 36 <?php _e( 'Widget Title', 'text_domain' ); ?> 37 </label> 38 <input class="widefat" 39 id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" 40 name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" 41 type="text" value="<?php echo esc_attr( $title ); ?>" /> 42 </p> 43 <p> 44 <label for="<?php echo esc_attr( $this->get_field_id( 'text' ) ); ?>"> 45 <?php _e( 'Text:', 'text_domain' ); ?> 46 </label> 47 <input class="widefat" 48 id="<?php echo esc_attr( $this->get_field_id( 'text' ) ); ?>" 49 name="<?php echo esc_attr( $this->get_field_name( 'text' ) ); ?>" 50 type="text" value="<?php echo esc_attr( $text ); ?>" /> 51 </p> 52 <p> 53 <label for="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>"> 54 <?php _e( 'Textarea:', 'text_domain' ); ?> 55 </label> 56 <textarea class="widefat" 57 id="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>" 58 name="<?php echo esc_attr( $this->get_field_name( 'textarea' ) ); ?>"><?php 59 echo wp_kses_post( $textarea ); 60 ?></textarea> 61 </p> 62 <p> 63 <input id="<?php echo esc_attr( $this->get_field_id( 'checkbox' ) ); ?>" 64 name="<?php echo esc_attr( $this->get_field_name( 'checkbox' ) ); ?>" 65 type="checkbox" value="1" <?php checked( '1', $checkbox ); ?> /> 66 <label for="<?php echo esc_attr( $this->get_field_id( 'checkbox' ) ); ?>"> 67 <?php _e( 'Show additional block', 'text_domain' ); ?> 68 </label> 69 </p> 70 <p> 71 <label for="<?php echo $this->get_field_id( 'select' ); ?>"> 72 <?php _e( 'Display variant', 'text_domain' ); ?> 73 </label> 74 <select name="<?php echo $this->get_field_name( 'select' ); ?>" 75 id="<?php echo $this->get_field_id( 'select' ); ?>" class="widefat"> 76 <?php 77 $options = array( 78 '' => __( '— Select —', 'text_domain' ), 79 'option_1' => __( 'Option 1', 'text_domain' ), 80 'option_2' => __( 'Option 2', 'text_domain' ), 81 'option_3' => __( 'Option 3', 'text_domain' ), 82 ); 83 foreach ( $options as $key => $name ) { 84 printf( 85 '<option value="%s" %s>%s</option>', 86 esc_attr( $key ), 87 selected( $select, $key, false ), 88 esc_html( $name ) 89 ); 90 } 91 ?> 92 </select> 93 </p> 94 <?php 95 } 96 97 public function update( $new_instance, $old_instance ) { 98 $instance = $old_instance; 99 $instance['title'] = isset( $new_instance['title'] ) 100 ? sanitize_text_field( $new_instance['title'] ) : ''; 101 $instance['text'] = isset( $new_instance['text'] ) 102 ? sanitize_text_field( $new_instance['text'] ) : ''; 103 $instance['textarea'] = isset( $new_instance['textarea'] ) 104 ? wp_kses_post( $new_instance['textarea'] ) : ''; 105 $instance['checkbox'] = isset( $new_instance['checkbox'] ) ? 1 : false; 106 $instance['select'] = isset( $new_instance['select'] ) 107 ? sanitize_text_field( $new_instance['select'] ) : ''; 108 return $instance; 109 } 110 111 public function widget( $args, $instance ) { 112 $title = isset( $instance['title'] ) 113 ? apply_filters( 'widget_title', $instance['title'] ) : ''; 114 $text = isset( $instance['text'] ) ? $instance['text'] : ''; 115 $textarea = isset( $instance['textarea'] ) ? $instance['textarea'] : ''; 116 $select = isset( $instance['select'] ) ? $instance['select'] : ''; 117 $checkbox = ! empty( $instance['checkbox'] ) ? $instance['checkbox'] : false; 118 119 echo $args['before_widget']; 120 echo '<div class="widget-text wp_widget_plugin_box">'; 121 122 if ( $title ) { 123 echo $args['before_title'] . esc_html( $title ) . $args['after_title']; 124 } 125 if ( $text ) { 126 echo '<p>' . esc_html( $text ) . '</p>'; 127 } 128 if ( $textarea ) { 129 echo '<div class="widget-textarea">' . wp_kses_post( $textarea ) . '</div>'; 130 } 131 if ( $select ) { 132 echo '<p class="widget-select">' . esc_html( $select ) . '</p>'; 133 } 134 if ( $checkbox ) { 135 echo '<p class="widget-checkbox-result">' 136 . esc_html__( 'Additional block activated', 'text_domain' ) . '</p>'; 137 } 138 139 echo '</div>'; 140 echo $args['after_widget']; 141 } 142 } 143 144 function my_register_custom_widget() { 145 register_widget( 'My_Custom_Widget' ); 146 } 147 add_action( 'widgets_init', 'my_register_custom_widget' );
Placera den här filen i wp-content/plugins/my-widget-plugin/, aktivera pluginet och dra widgeten till valfri sidopanel via Utseende → Widgetar. Fyll i fälten, spara och kontrollera din webbplats.
Den färdiga koden finns också på GitHub: wpexplorer/my-widget-plugin, där du kan jämföra den med originalversionen från 2017 och se exakt vad vi ändrade.
⁉️🤔 Vanliga frågor
Varför lägga en widget i ett plugin när man bara kan lägga till kod i temats functions.php?
Kod i functions.php är knuten till det aktiva temat. Byt tema, och widgeten försvinner. Ett plugin fungerar oberoende av temat. Dessutom kan ett plugin aktiveras selektivt på olika webbplatser, vilket temakod inte kan. Om widgeten löser ett affärsbehov (till exempel att visa ett prenumerationsformulär med en specifik layout) hör den hemma i ett plugin.
Varför är sanitize_text_field() bättre än wp_strip_all_tags()?
sanitize_text_field()tar inte bara bort HTML-taggar; den normaliserar också blanktecken, tar bort osynliga kontrolltecken och konverterar strängen till UTF-8. Det här är en mer grundlig rensning.wp_strip_all_tags()lämnar "rå" text med alla blankteckensartefakter intakta. För textformulärfält, välj alltidsanitize_text_field().
Varför ersatte ni extract() med direkt array-åtkomst?
Funktionen
extract()har varit utesluten ur WordPress kodningsstandarder sedan version 4.3. Den skapar variabler namngivna efter array-nycklar i det lokala scopet; om en nyckel matchar en befintlig variabel skriver du över den och får en svårhittad bugg. Direkt åtkomst som$args['before_widget']är läsbar och säker.
Behöver jag stödja blockwidgetar (Gutenberg)?
Den klassiska
WP_Widgetfungerar med blockbaserade sidopaneler genom bakåtkompatibilitet: WordPress slår automatiskt in den i ett Legacy Widget Block. Detta räcker för de flesta scenarier. Om du vill skapa verkligt inbyggda block, se Block Editor Handbook, som har ett separat API. Men att börja medWP_Widgetär enklare: koden är kortare, felsökning går snabbare och det fungerar på alla WordPress-versioner utan extra kompatibilitetsplugins.
Hur felsöker jag en widget som inte syns i adminpanelen?
Kontrollera tre saker. För det första: kördes hooken
widgets_init? Lägg tillerror_log( 'Widget registered' )i registreringsfunktionen och kolla loggarna. För det andra: finns det ett fatalt fel i konstruktorn? AktiveraWP_DEBUGi wp-config.php. För det tredje: matchar klassnamnet iregister_widget()namnet på klassen som ärverWP_Widget? Ett enda stavfel, och widgeten kommer inte att visas.
Vad händer nu: från mall till din egen widget
Vi har byggt ett fungerande plugin med fem fälttyper. Det här är inte en färdig produkt; det är en stomme. Ta den som din grund och anpassa den efter dina behov. Behöver du en widget för prenumerationsformulär? Byt ut textfälten mot e-post- och namn-fält, och lägg till ett API-anrop till en e-posttjänst inuti widget(). Behöver du ett block med kampanjer och banners? Ladda bilder via mediauppladdaren och rendera dem i din markup. Mekaniken är alltid densamma: form() ritar fälten, update() sparar dem, widget() renderar utdatan.
Börja smått: kopiera hela koden ovan, aktivera den på en testsajt och experimentera med inställningarna. När du förstår hur data flödar från formuläret till frontend kan du börja lägga till egna fält. Och om widgeten kraschar med en vit skärm, gå tillbaka till steg 2 och kontrollera konstruktorn.



