Skip to content
🛠 Hvordan lage en widget-plugin for WordPress: en trinnvis guide

🛠 Hvordan lage en widget-plugin for WordPress: en trinnvis guide

💡 Slik bygger du en widget-plugin for WordPress

  • Opprett en my-widget-plugin-mappe i wp-content/plugins/ og en my-widget-plugin.php-fil med plugin-headeren, og aktiver den deretter i administrasjonspanelet
  • Deklarer en klasse som utvider WP_Widget og overstyr metodene __construct(), form(), update() og widget()
  • Registrer widgeten med funksjonen register_widget()widgets_init-hooken slik at WordPress kan se den i listen
  • I form()-metoden, render innstillingsfeltene; i update(), sanitér og lagre data via sanitize_text_field()
  • I widget()-metoden, send ut innhold på frontend, og escape verdier med esc_html() og wp_kses_post()

Steg 1: opprette plugin-skjelettet

En widget i WordPress er ikke bare en linje i functions.php. Hvis du vil at innstillinger skal vedvare, at admin-skjemaet skal fungere, og at selve widgeten skal overleve neste temaoppdatering, må du pakke den som en plugin. Dette isolerer koden og gjør widgeten uavhengig av ethvert temabytte.

Start med en tom mappe. Naviger til wp-content/plugins/ og opprett en katalog kalt my-widget-plugin. Inni den, opprett en fil med navnet my-widget-plugin.php. Dette er filen WordPress leser først ved aktivering. Åpne filen og legg til standard plugin-header:

1<?php
2/*
3Plugin Name: My Widget Plugin
4Plugin URI: https://www.wpexplorer.com/create-widget-plugin-wordpress/
5Description: Adds a customizable widget with text, textarea, checkbox, and dropdown.
6Version: 1.0
7Author: AJ Clarke
8Author URI: https://www.wpexplorer.com/
9License: GPL2
10*/

Lagre filen. Gå nå til WordPress-administrasjonspanelet → Plugins. Hvis pluginen dukker opp i listen, er skjelettet klart. Klikk «Aktiver». Den gjør ikke noe nyttig ennå; vi har bare kunngjort dens eksistens. Men WordPress vet allerede at denne pluginen finnes og er klar til å kjøre koden sin. Dette er et viktig prinsipp: registrering først, logikk etterpå.

Hvordan pluginen fungerer internt, hvilke hooks som fyres av ved aktivering, og hvordan WordPress finner filen din, dekkes i videoen over. La oss nå gå videre til den mest interessante delen: widget-klassen.

Steg 2: registrere widgeten via WP_Widget

WordPress tilbyr en innebygd klasse kalt WP_Widget; den har vært en del av kjernen siden versjon 2.8 og er fortsatt fundamentet for alle egendefinerte widgeter. Du trenger ikke skrive lagringslogikk, feltgenerering eller registrering fra bunnen av: bare utvid klassen og overstyr fire metoder.

Legg til denne koden i my-widget-plugin.php rett etter plugin-headeren, før den avsluttende ?>:

1// Widget class
2class 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
28function my_register_custom_widget() {
29 register_widget( 'My_Custom_Widget' );
30}
31add_action( 'widgets_init', 'my_register_custom_widget' );

La oss bryte ned hva som skjer her. Klassen My_Custom_Widget utvider WP_Widget, som gir deg ferdige metoder som get_field_id() og get_field_name() for å generere attributter for skjemafelter. I konstruktøren sender vi tre ting til foreldreklassen: en unik widget-ID (små latinske bokstaver, ingen mellomrom, my_custom_widget), dens lesbare navn (__()-funksjonen gjør den oversettbar), og en opsjonsarray. Parameteren customize_selective_refresh => true lar widgeten oppdatere seg i Tilpasser uten å laste hele siden på nytt, en liten detalj som sparer mye frustrasjon under oppsett.

Funksjonen my_register_custom_widget() kaller register_widget()widgets_init-hooken. Dette er hvordan WordPress lærer om widgeten din. Uten denne linjen vil ingenting dukke opp i administrasjonspanelet.

La oss nå fylle form()-, update()- og widget()-metodene med ekte logikk.

Steg 3: lage widget-skjemaet i administrasjonspanelet

Skjemaet er det administratoren ser når widgeten dras inn i et sidefelt. Det består av felter: tekstinput, nedtrekkslister, avkrysningsbokser. Hvert felt må kunne lagre verdien sin og vise den gjeldende når det åpnes på nytt.

3.1. Funksjonen form() og input-felter

Legg til denne koden i form()-metoden til klassen din. Den oppretter fem felter: en tittel, et tekstinput, et tekstområde, en avkrysningsboks og en nedtrekksliste.

1public 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 }

Vær oppmerksom på to ting. For det første erstattet vi den utdaterte extract()-funksjonen med direkte array-tilgang. extract() har lenge vært ekskludert fra WordPress' kodestandarder; den oppretter variabler navngitt etter array-nøkler, noe som er usikkert og gjør feilsøking vanskeligere. For det andre blir hver utdataverdi sendt gjennom esc_attr(), wp_kses_post() eller esc_html(). Dette er ikke paranoia: data fra databasen kan komme fra hvor som helst, og sanitering er obligatorisk.

3.2. Funksjonen update() for lagring

update()-metoden kalles når «Lagre»-knappen klikkes i widget-skjemaet. Dens jobb er å validere hvert felt og returnere en sanitert array for skriving til databasen.

1public 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}

Her bruker vi sanitize_text_field() i stedet for wp_strip_all_tags() fordi den også normaliserer mellomrom, fjerner usynlige kontrolltegn og konverterer strengen til sikker UTF-8. Dette er en mer grundig opprydding. wp_strip_all_tags() etterlater «rå» tekst med alle mellomromsartefakter intakte. For tekstskjemafelter, velg alltid sanitize_text_field().

Tekstområdet beholder wp_kses_post(): det tillater grunnleggende HTML (lenker, fet tekst, lister) men stripper skript. Avkrysningsboksen returnerer 1 eller false, noe som er lesbart og entydig i databasen.

Steg 4: rendre widgeten på frontend

widget()-funksjonen er det den besøkende ser. Den mottar to parametere: $args (widget-omslaget, tagger før og etter tittel og sidefelt) og $instance (de lagrede innstillingene for denne spesifikke instansen).

1public 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}

Nøkkelpunktet her: vi erstattet extract( $args ) med direkte tilgang via $args['before_widget']. Årsaken er den samme: extract() er utdatert og usikker. Vi pakket også utdataene inn i esc_html() der ren tekst forventes (tittelen, tekststrengen, select-verdien). Tekstområdet rendres gjennom wp_kses_post(), så hvis administratoren satte inn en lenke eller fet tekst, vil de bli bevart.

CSS-klassen wp_widget_plugin_box lar deg style blokken fra temaets stilark. Du kan gjerne gi den et annet navn, bare sørg for at klassen er unik og ikke kommer i konflikt med temaklasser.

Full plugin-kode

La oss sette alt sammen. Her er den komplette my-widget-plugin.php-filen, klar til å kopieres og aktiveres:

1<?php
2/*
3Plugin Name: My Widget Plugin
4Plugin URI: https://www.wpexplorer.com/create-widget-plugin-wordpress/
5Description: Adds a customizable widget with text, textarea, checkbox, and dropdown.
6Version: 1.0
7Author: AJ Clarke
8Author URI: https://www.wpexplorer.com/
9License: GPL2
10*/
11
12class 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
144function my_register_custom_widget() {
145 register_widget( 'My_Custom_Widget' );
146}
147add_action( 'widgets_init', 'my_register_custom_widget' );

Plasser denne filen i wp-content/plugins/my-widget-plugin/, aktiver pluginen, og dra widgeten inn i et hvilket som helst sidefelt via Utseende → Widgeter. Fyll ut feltene, lagre, og sjekk nettstedet ditt.

Den ferdige koden er også tilgjengelig på GitHub: wpexplorer/my-widget-plugin, hvor du kan sammenligne den med den originale 2017-versjonen og se nøyaktig hva vi endret.

⁉️🤔 Ofte stilte spørsmål

Hvorfor legge en widget i en plugin når du bare kan legge kode i temaets functions.php?

Kode i functions.php er knyttet til det aktive temaet. Bytt tema, og widgeten forsvinner. En plugin fungerer uavhengig av temaet. I tillegg kan en plugin aktiveres selektivt på forskjellige nettsteder, noe temakode ikke kan. Hvis widgeten løser et forretningsbehov (for eksempel å vise et abonnementsskjema med en spesifikk layout), hører den hjemme i en plugin.

Hvorfor er sanitize_text_field() bedre enn wp_strip_all_tags()?

sanitize_text_field() fjerner ikke bare HTML-tagger; den normaliserer også mellomrom, stripper usynlige kontrolltegn og konverterer strengen til UTF-8. Dette er en mer grundig opprydding. wp_strip_all_tags() etterlater «rå» tekst med alle mellomromsartefakter intakte. For tekstskjemafelter, velg alltid sanitize_text_field().

Hvorfor erstattet dere extract() med direkte array-tilgang?

Funksjonen extract() har vært ekskludert fra WordPress' kodestandarder siden versjon 4.3. Den oppretter variabler navngitt etter array-nøkler i det lokale skopet; hvis en nøkkel matcher en eksisterende variabel, vil du overskrive den og ende opp med en vanskelig å finne feil. Direkte tilgang som $args['before_widget'] er lesbart og sikkert.

Må jeg støtte blokk-widgeter (Gutenberg)?

Den klassiske WP_Widget fungerer med blokkbaserte sidefelt gjennom bakoverkompatibilitet: WordPress pakker den automatisk inn i en Legacy Widget Block. Dette er tilstrekkelig for de fleste scenarioer. Hvis du vil lage virkelig native blokker, se Block Editor Handbook, som har et separat API. Men å starte med WP_Widget er enklere: koden er kortere, feilsøking er raskere, og den fungerer på alle WordPress-versjoner uten ekstra kompatibilitetsplugins.

Hvordan feilsøker jeg en widget som ikke dukker opp i administrasjonspanelet?

Sjekk tre ting. Først: fyrte widgets_init-hooken av? Legg til error_log( 'Widget registered' ) i registreringsfunksjonen og sjekk loggene. Andre: er det en fatal feil i konstruktøren? Aktiver WP_DEBUG i wp-config.php. Tredje: samsvarer klassenavnet i register_widget() med navnet på klassen som utvider WP_Widget? En eneste skrivefeil, og widgeten vil ikke vises.

Hva nå: fra mal til din egen widget

Vi har bygget en fungerende plugin med fem felttyper. Dette er ikke et ferdig produkt; det er et skjelett. Ta det som ditt fundament og tilpass det til dine behov. Trenger du en widget for et abonnementsskjema? Bytt ut tekstfeltene med e-post- og navnefelt, og legg til et API-kall for en e-posttjeneste inne i widget(). Trenger du en blokk med kampanjer og bannere? Last inn bilder gjennom medieopplasteren og render dem i din markup. Mekanikken er alltid den samme: form() tegner feltene, update() lagrer dem, widget() renderer utdataene.

Start i det små: kopier hele koden over, aktiver den på et testnettsted, og eksperimenter med innstillingene. Når du forstår hvordan data flyter fra skjemaet til frontend, kan du begynne å legge til dine egne felter. Og hvis widgeten krasjer med en hvit skjerm, gå tilbake til steg 2 og sjekk konstruktøren.