WordPress-Tutorial: Eigenes Widget schreiben
04.06.2010 in: PHP, Webdesign und WordPress • 7 Kommentare
Letzte Änderung: 05.06.2010, 18:32 Uhr.
Widgets liegen in der WordPress-Architektur irgendwo zwischen Theme und Plugin: Die eigene Funktionalität und das Aussehen sind meistens eng an vorhandenen Code gebunden, doch können sie auch ganz eigenständig erzeugt werden.
Sie lassen sich in Sidebars einbetten; man kann sie aber auch »manuell« im Theme aufrufen mit der Funktion the_widget()
.
Die Widgets-API ist die einzige, die objektorientiertes Arbeiten erzwingt – deshalb eignet sie sich gut zum Lernen und gefahrlosen Ausprobieren, finde ich.
Themes und Plugins hingegen dürfen komplett prozedural geschrieben werden. Werden sie leider oft auch.
Ein Widget muß die Klasse WP_Widget
erweitern. Ein Konstruktor wird für den Aufruf gebraucht und eine Funktion widget()
für die Ausgabe. Um den Speicherfresser create_function()
zu umgehen, packe ich noch eine Funktion register()
dazu. Das sieht im einfachsten Falle so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php add_action( 'widgets_init' , array ('Toscho_Mini_Widget', 'register' ) ); class Toscho_Mini_Widget extends WP_Widget { function Toscho_Mini_Widget() { $this->WP_Widget(__CLASS__, __CLASS__); } function widget($args, $instance) { print 'Juhu, hat geklappt!'; } function register() { register_widget(__CLASS__); } } |
Hier müffelt es noch schlimm nach PHP 4. Wenn der Konstruktor nicht wie die Klasse heißt, läuft der Speicher aus … das müssen wir vorerst wohl hinnehmen.
Das Beispiel oben kann im Backend aktiviert werden, wenn es im Theme eingebunden wird und wenn das Theme mindestens eine Sidebar registriert hat.
Es tut natürlich noch nicht viel.
Im nächsten Schritt sehen wir uns mal ein Beispiel an, das wir tatsächlich einsetzen könnten: Wir wollen die letzten Posts auflisten, dabei aber bestimmte Kategorien ausschließen oder zulassen.
Da können wir uns bei der Klasse WP_Widget_Recent_Posts
einigen Code abgucken. Die liegt WordPress schon bei in der default-widgets.php.
Wir brauchen zusätzlich noch eine Eingabemaske für die Kategorien, und wir müssen den Query erweitern. Die nötigen Argumente finden wir in der Dokumentation zu query_posts()
.
Die beiden letzten Funktionen stammen nicht aus der Elternklasse, sondern ich habe sie eingebaut, um den Rest des Codes lesbar zu halten.
Live läuft genau dieses Widget gerade auf der Website der Interessengemeinschaft Britisch Kurzhaar-Katzen links in der Sidebar; hier werden die Jungtiermeldungen ausgeklammert, weil sie schon sehr prominent auf der Startseite präsentiert werden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
<?php add_action( 'widgets_init' , array ('Restricted_Latest_Posts_Widget', 'register' ) ); /** * Restricted Recent Posts widget class. * * Restricts the list of recent posts to selected categories. * Derivate of the default widget. * * @todo Select post types * @todo Select taxonomy */ class Restricted_Latest_Posts_Widget extends WP_Widget { // Adjust this to your needs. var $max_posts = 50 , $wid_name = 'widget_restricted_recent_posts'; /** * Constructor. */ function Restricted_Latest_Posts_Widget() { $widget_ops = array( 'classname' => $this->wid_name , 'description' => 'Letzte Beiträge per Kategorie ausgewählt. Mit Komma trennen.' ); $this->WP_Widget( 'restricted-recent-posts' , 'Restricted Recent Posts' , $widget_ops ); $this->alt_option_name = $this->wid_name; add_action( 'save_post', array(&$this, 'flush_widget_cache') ); add_action( 'deleted_post', array(&$this, 'flush_widget_cache') ); add_action( 'switch_theme', array(&$this, 'flush_widget_cache') ); } /** * Called statically to register the widget. * * @return void. */ function register() { register_widget(__CLASS__); } function widget($args, $instance) { $cache = wp_cache_get($this->wid_name, 'widget'); if ( ! is_array($cache) ) { $cache = array(); } if ( isset ( $cache[ $args['widget_id'] ] ) ) { echo $cache[ $args['widget_id'] ]; return; } ob_start(); extract($args); $title = empty ( $instance['title'] ) ? 'Restricted Recent Posts' : $instance['title']; $title = apply_filters('widget_title', $title); $number = $this->sanitize_number($instance['number']); $include_cats = trim($instance['include_cats']); $exclude_cats = trim($instance['exclude_cats']); $query_args = array( 'showposts' => $number , 'nopaging' => 0 , 'post_status' => 'publish' , 'caller_get_posts' => 1 ); if ( ! empty ( $include_cats ) ) { $query_args['category__in'] = $this->catlist_to_array($include_cats); } elseif ( ! empty ( $exclude_cats ) ) { $query_args['category__not_in'] = $this->catlist_to_array($exclude_cats); } $r = new WP_Query($query_args); if ( $r->have_posts() ) { print $before_widget . ( $title ? $before_title . $title . $after_title : '' ) . '<ul>'; while ( $r->have_posts() ) { $r->the_post(); print '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>'; } print "</ul>$after_widget"; // Restore global post data stomped by the_post(). wp_reset_query(); } $cache[ $args['widget_id'] ] = ob_get_flush(); wp_cache_add($this->wid_name, $cache, 'widget'); } function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = strip_tags($new_instance['title']); $instance['number'] = $this->sanitize_number($new_instance['number']); $instance['exclude_cats'] = $new_instance['exclude_cats']; $instance['include_cats'] = $new_instance['include_cats']; $this->flush_widget_cache(); $alloptions = wp_cache_get( 'alloptions', 'options' ); if ( isset ( $alloptions[$this->wid_name] ) ) { delete_option($this->wid_name); } return $instance; } function flush_widget_cache() { wp_cache_delete($this->wid_name, 'widget'); } function form( $instance ) { $title = isset ( $instance['title'] ) ? esc_attr($instance['title']) : ''; if ( ! isset ( $instance['number']) || ! $number = (int) $instance['number'] ) { $number = 5; } $incats = isset ( $instance['include_cats'] ) ? esc_attr($instance['include_cats']) : ''; $excats = isset ( $instance['exclude_cats'] ) ? esc_attr($instance['exclude_cats']) : ''; ?> <p> <label for="<?php echo $this->get_field_id('title'); ?>"> <?php _e('Title:'); ?> </label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" /> </p> <p> <label for="<?php echo $this->get_field_id('number'); ?>"> Artikelmenge </label> <input id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo $number; ?>" size="3" /> <small>(maximal <?php print $this->max_posts; ?>)</small> </p> <p> <label for="<?php echo $this->get_field_id('include_cats'); ?>"> Auf Kategorien beschränken: </label> <input id="<?php echo $this->get_field_id('include_cats'); ?>" name="<?php echo $this->get_field_name('include_cats'); ?>" type="text" value="<?php echo $incats; ?>" size="10" /> <br /><strong>oder</strong><br /> <label for="<?php echo $this->get_field_id('exclude_cats'); ?>"> Kategorien ausschließen: </label> <input id="<?php echo $this->get_field_id('exclude_cats'); ?>" name="<?php echo $this->get_field_name('exclude_cats'); ?>" type="text" value="<?php echo $excats; ?>" size="10" /> </p> <?php } /** * Guarantee a number between 1 and max_posts. * * @return int */ function sanitize_number($nr) { $nr = (int) $nr; if ( $nr > $this->max_posts ) { return $this->max_posts; } if ( 0 == $nr ) { return 1; } return $nr; } /** * Used by widget() to fill the category parameter. * * @param string $catlist * @return array */ function catlist_to_array($catlist) { $tmp_cats = explode(',', $catlist); $new_cats = array (); foreach ( $tmp_cats as $cat ) { $new_cats[] = (int) trim($cat); } return $new_cats; } } |
Zwei mögliche Erweiterungen habe ich im Kopf angedeutet. Wer will es versuchen?
Eine sehr viel umfangreichere Erweiterung der WordPress-internen Widgets liefert Justin Tadlocks Plugin Widgets Reloaded. Sehr praktisch – und von dem sauberen Code kann man eine Menge lernen.
Siehe auch:
- WordPress-Tutorial: Ein Framebreaker-Plugin schreiben
- WordPress: Alle Artikel auflisten
- WordPress: Parallele Widgets
Patrick am 04.06.2010 · 15:44
Reicht es aus, die erforderlichen Kommentarzeilen am Anfang der Datei zu machen und die Datei in den Plugin-Ordner zu schieben, um ein Widget als Plugin zur Verfügung zu stellen?
Thomas Scholz am 04.06.2010 · 15:52
@Patrick: Ja, das genügt. Ich würde dann noch eine Funktion einbauen, die das Update verhindert, damit es nicht von einem gleichnamigen Plugin aus dem Repository überschrieben wird. Ein Beispiel für solch eine Funktion findest du im Plugin Backend Style Enhancements.
Kai am 14.11.2010 · 15:11
Klasse! Ich bin gerade dabei Dein Tutorial umzusetzen und es scheint zu klappen! Ich würde mir noch ein solches Tutorial wünschen für Widgets mit etwas ausgefalleneren Eigenschaften...
Sascha am 16.05.2011 · 23:35
Sagt mal liebe Kollegen, wie kann ich verhindern, dass ich bei einem Update jedesmal das ganze neu aufsetzen muss. Die Datei würde mir schon reichen, wo ich das einstellen muss - such mir grade "nen Wolf".
Thomas Scholz am 16.05.2011 · 23:45
@Sascha: Das ganze Was? Update wovon?
Ronny am 25.07.2011 · 09:42
In der Zwischenzeit gibt es gute Bücher zu dem Thema. Wer durch das Tutorial Geschmack bekommen hat, sollte mal bei Amazon stöbern. Vorallem im Englischen ist schon gute Literatur vorhanden. Es lohnt, es macht Spaß, wenn man es mal richtig verstanden hat.
Robert Hartl am 08.02.2012 · 11:07
Klasse Anleitung, vor allem der Hinweise mit den Codevorlagen bei den default Widgets hat mir geholfen!