WordPress widget tutorial – piszemy własny widget, witajcie w drugiej części kursu dla początkujących dotyczącego tworzenia widgetów dla WordPress zupełnie od zera.

WordPress widget tutorialW części pierwszej tego kursu przygotowaliśmy środowisko do śledzenia postępów, utworzyliśmy klasę rozszerzającą WordPressową klasę bazową – WP_Widget, nadaliśmy unikalny identyfikator, nazwę i opis, oraz go zainicjowaliśmy tak by był  widoczny z poziomu kokpitu.

Część 2 – WordPress widget tutorial – piszemy własny widget

W tej części zajmiemy się formularzem widget-u (backend-em), dzięki któremu możliwe będzie sterowanie widget-em z poziomu kokpitu. A przesłane opcje, walidować i zapisywać w bazie danych.

Produktem tego kursu będzie prosty widget wyświetlający najnowsze wpisy na blogu, zupełnie tak jak ten wbudowany w WordPress, ale nasz będzie miał trochę więcej opcji:

  • możliwość wyboru kategorii
  • opcja wyświetlenia wypisu
  • ustawienie długości wypisu
  • możliwość wyświetlania miniatur wpisu

Wstępne zapisywanie opcji widget-u w bazie WordPress

Dla ułatwienia sobie zadania, zanim zaczniemy pisać formularz z opcjami do widget-u możemy przedtem nieco zmodyfikować metodę klasy naszego widgetu, tak by od razu zapisywała wszystkie przesłane dane w bazie danych.

Otwieramy plik z ostatnio utworzoną klasą i przechodzimy do metody update() i zamieniamy ją na:

public function update( $new_instance, $old_instance ) {
        return $new_instance;
}

Jak to działa? W skrócie mówiąc, funkcja powinna zwracać tablicę asocjacyjną zawierającą już sprawdzone dane (opcje widgetu), które następnie są przez WordPress serializowane i zapisywane w bazie pod identyfikatorem widget-u.

Jako developerzy widget-ów, wiemy jakie niebezpieczeństwa mogą wiązać się z brakiem walidacji danych pobranych od użytkowników, ale nam w tym momencie chodzi tylko o to, by sprawdzać, czy pola dodawane do formularza są dobrze skonstruowane i czy będą później zaznaczać zapisane opcje, a samą walidacją zajmiemy się na końcu tej części kursu.

Dodajemy pola do formularza widget-u

Przechodzimy teraz do funkcji form(), w której dodawać będziemy kolejne pola do formularza widget-u.

Zacznijmy od dodania pola nadającego nagłówek (nazwę) widget-u:

Na początku funkcji dodajemy linijkę:

$instance = wp_parse_args( (array) $instance, array( 'title' => 'Najnowsze Posty' ) );

Jak zapewne zauważyłeś metoda form() przyjmuje jeden argument $instance. W nim znajduje się tablica asocjacyjna z ustawieniami widgetu pobranymi z bazy danych. Teraz ta zmienna jest pusta.

Dodanie tej linijki sprawia, że w przypadku braku którejś opcji w bazie (jak teraz), do zmiennej $instance zostaną dodane opcje domyślne, które definiujemy właśnie w tym miejscu. Jeżeli mielibyśmy w bazie zapisane już jakieś informacje, to domyślne dane zostaną nadpisane tymi z bazy.

W tym przypadku ustawiamy title na Najnowsze Posty, czyli zawsze, gdy w bazie będzie brakowało tej zmiennej, wyświetlony zostanie ciąg Najnowsze Posty.

I wreszcie, dodajemy kod html, uzupełniony o specjalne funkcje WordPress-a, dzięki którym pole formularza będzie zapisywać wartość opcji pod właściwym identyfikatorem, i później uzupełniać te pole zapisaną wartością z bazy danych:

<p>
    <!-- Widgets name -->
    <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 esc_attr($instance['title']); ?>" />
</p>

O ile etykieta (label) nie jest tutaj bardzo ważna, to musimy dokładnie przyjrzeć się w jaki sposób mamy nazwać pole input, oraz jak nadać mu wartość.

Myślę, że w tym momencie jest już w miarę jasne o co chodzi, dlatego nie będę opisywał każdego pola z osobna, tylko pokaże jak teraz wygląda mój pełny formularz:

public function form( $instance ) {

    //ustawiamy opcje domyslne
    $my_defaults = array(
        'number' => 5,
        'title' => 'Najnowsze Posty',
        'categories' => array(),
        'show_excerpt' => false,
        'excerpt_length' => 200,
        'show_thumbnail' => false
    );
    
    //uzupelniamy opcje zapisane w bazie o opcje domyslne
    $instance = wp_parse_args( (array) $instance, $my_defaults );

?>
    <p>
        <!-- Widgets name -->
        <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 esc_attr($instance['title']); ?>" />
    </p>
    <p>
        <!-- Number of posts to show -->
        <label for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( 'Number of posts to show:' ); ?></label>
        <input id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" type="text" value="<?php echo $instance['number']; ?>" size="3" />
    </p>
    <p>
        <!-- Get and display Categories checkbox options -->
        <label><?php _e('Categories:'); ?></label><br />
        <?php
        $categories_list = get_categories('hide_empty=0&orderby=name');
        foreach ($categories_list as $cat ):
            $cat_id = intval($cat->cat_ID);
            $cat_name = $cat->cat_name;
            $selected = '';

            if(in_array($cat_id, $instance['categories']))
                $selected=' checked="checked"';    ?>

            <input value="<?php echo $cat_id; ?>" class="radio" type="checkbox"<?php echo $selected; ?> id="<?php echo $this->get_field_id('categories'); echo $cat_id; ?>" name="<?php echo $this->get_field_name('categories'); ?>[]" /> <label for="<?php echo $this->get_field_id('categories'); echo $cat_id; ?>"><?php echo $cat_name; ?></label><br />
        <?php endforeach; ?>
    </p>
    <p>
        <!-- Show excerpt -->
        <input class="checkbox" type="checkbox" name="<?php echo $this->get_field_name('show_excerpt'); ?>" id="<?php echo $this->get_field_id('show_excerpt'); ?>" value="true" <?php checked(true, $instance['show_excerpt']);?> />
        <label for="<?php echo $this->get_field_id('show_excerpt'); ?>"> <?php _e( 'Display post excerpt' ); ?></label><br />
    </p>
    <p>
        <!-- Excerpt length -->
        <label for="<?php echo $this->get_field_id( 'excerpt_length' ); ?>"><?php _e( 'Excerpt length:' ); ?></label>
        <input id="<?php echo $this->get_field_id( 'excerpt_length' ); ?>" name="<?php echo $this->get_field_name( 'excerpt_length' ); ?>" type="text" value="<?php echo $instance['excerpt_length']; ?>" size="5" />
    </p>
    <p>
        <!-- Show excerpt -->
        <input class="checkbox" type="checkbox" name="<?php echo $this->get_field_name('show_thumbnail'); ?>" id="<?php echo $this->get_field_id('show_thumbnail'); ?>" value="true" <?php checked(true, $instance['show_thumbnail']);?> />
        <label for="<?php echo $this->get_field_id('show_thumbnail'); ?>"> <?php _e( 'Display post thumbnail' ); ?></label><br />
    </p>
<?php    
}

Jeżeli brakuje wam jakiegoś pola i nie za bardzo wiecie jak ma wyglądać jego kod, czy jakie klasy dodać do tych pól, to bardzo dobrym źródłem wiedzy jest kod widgetów wbudowanych w WordPress. Plik z domyślnymi widgetami znajdziecie w katalogu wp-includes/default-widgets.php, albo tutaj online. Można też sprawdzić widgety dodawane do innych motywów, albo pobrać z repozytorium WordPress-a w postaci pluginów, jest tego mnóstwo.

Listę checkbox można łatwo zamienić na pola typu radio – wystarczy zmienić typ input-ów na radio – teraz zamiast listy wielokrotnego wyboru, otrzymamy listę z jedną opcją do wyboru.

Walidacja opcji widget-u i zapis w bazie WordPress

Ostatnią rzeczą, którą musimy się jeszcze zająć w tej części to walidacja przesłanych przez formularz opcji, ewentualna ich obróbka i zapis (uaktualnienie) w bazie danych. Spójrz na mój końcowy kod:

public function update( $new_instance, $old_instance ) {
    
    $instance = $old_instance;

    //tytul widgetu
    $instance['title'] = strip_tags($new_instance['title']);
    //ilosc wyswietlanych postow
    $instance['number'] = absint($new_instance['number']);    
    //zaznaczone kategorie
    $instance['categories']  = (array) $new_instance['categories'];
    //czy pokazac wypis
    $instance['show_excerpt'] = isset($new_instance['show_excerpt']);
    //dlugosc wypisu
    $instance['excerpt_length'] = absint($new_instance['excerpt_length']);
    //czy pokazac miniaturke wpisu
    $instance['show_thumbnail'] = isset($new_instance['show_thumbnail']);

    return $instance;
}

 

Sposób działania tej metody jest dosyć prosty – w argumentach podawane są dwie tablice asocjacyjne:

  • $new_instance – jest to tablica nowych, właśnie przesłanych przez formularz opcji;
  • $old_instance, to tablica opcji aktualnie zapisanych w bazie danych.

A sama metoda powinna zwrócić kombinację tych dwóch tablic – przede wszystkim nowo-przesłane opcje, uzupełnione o stare, jeżeli nowe opcje nie będą spełniały stawianych im wymagań.

Nawet dla tych kilku opcji, metoda walidująca wprowadzone dane wydaje się być strasznie krótka, prawda? Myślę, że wytłumaczyć to można w ten sposób, że administracja widgetami należy do administratora witryny i jest raczej mało prawdopodobne, aby admin próbował sabotować własną witrynę. Dlatego mamy tutaj taką prostą, podstawową walidację.

Nie ma tutaj dużej filozofii, z pól tekstowych czyścimy wszelkie tagi, dla liczb całkowitych używamy funkcji absint(), która zwróci 0, jeżeli liczba jest ujemna i/lub niecałkowita. Dla pól typu checkbox, sprawdzamy tylko czy pole jest ustawione.

Oczywiście można bawić się w dodatkowe „ify” i „regexy”, sprawdzać czy np. łańcuch jest pusty i ewentualnie wstawiać starą wartość, ale patrząc z przedstawionej wcześniej perspektywy nie ma to większego sensu.

 

Pełny kod:

<?php

class Najnowsze_Posty extends WP_Widget {

    /**
     * Sets up the widgets name etc
     */
    public function __construct() {
        $widget_id = 'najnowsze_posty';
        $widget_name = __('Najnowsze Posty', 'NajnowszePosty');
        $widget_opt = array('description'=>'Ten widget wyświetla najnowsze posty.');

        parent::__construct($widget_id, $widget_name, $widget_opt);
    }

    /**
     * Outputs the content of the widget
     *
     * @param array $args
     * @param array $instance
     */
    public function widget( $args, $instance ) {
        // outputs the content of the widget
    }

    /**
     * Outputs the options form on admin
     *
     * @param array $instance The widget options
     */
    public function form( $instance ) {

        //ustawiamy opcje domyslne
        $my_defaults = array(
            'number' => 5,
            'title' => 'Najnowsze Posty',
            'categories' => array(),
            'show_excerpt' => false,
            'excerpt_length' => 200,
            'show_thumbnail' => false
        );
        
        //uzupelniamy opcje zapisane w bazie o opcje domyslne
        $instance = wp_parse_args( (array) $instance, $my_defaults );

    ?>
        <p>
            <!-- Widgets name -->
            <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 esc_attr($instance['title']); ?>" />
        </p>
        <p>
            <!-- Number of posts to show -->
            <label for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( 'Number of posts to show:' ); ?></label>
            <input id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" type="text" value="<?php echo $instance['number']; ?>" size="3" />
        </p>
        <p>
            <!-- Get and display Categories checkbox options -->
            <label><?php _e('Categories:'); ?></label><br />
            <?php
            $categories_list = get_categories('hide_empty=0&orderby=name');
            foreach ($categories_list as $cat ):
                $cat_id = intval($cat->cat_ID);
                $cat_name = $cat->cat_name;
                $selected = '';

                if(in_array($cat_id, $instance['categories']))
                    $selected=' checked="checked"';    ?>

                <input value="<?php echo $cat_id; ?>" class="radio" type="checkbox"<?php echo $selected; ?> id="<?php echo $this->get_field_id('categories'); echo $cat_id; ?>" name="<?php echo $this->get_field_name('categories'); ?>[]" /> <label for="<?php echo $this->get_field_id('categories'); echo $cat_id; ?>"><?php echo $cat_name; ?></label><br />
            <?php endforeach; ?>
        </p>
        <p>
            <!-- Show excerpt -->
            <input class="checkbox" type="checkbox" name="<?php echo $this->get_field_name('show_excerpt'); ?>" id="<?php echo $this->get_field_id('show_excerpt'); ?>" value="true" <?php checked(true, $instance['show_excerpt']);?> />
            <label for="<?php echo $this->get_field_id('show_excerpt'); ?>"> <?php _e( 'Display post excerpt' ); ?></label><br />
        </p>
        <p>
            <!-- Excerpt length -->
            <label for="<?php echo $this->get_field_id( 'excerpt_length' ); ?>"><?php _e( 'Excerpt length:' ); ?></label>
            <input id="<?php echo $this->get_field_id( 'excerpt_length' ); ?>" name="<?php echo $this->get_field_name( 'excerpt_length' ); ?>" type="text" value="<?php echo $instance['excerpt_length']; ?>" size="5" />
        </p>
        <p>
            <!-- Show excerpt -->
            <input class="checkbox" type="checkbox" name="<?php echo $this->get_field_name('show_thumbnail'); ?>" id="<?php echo $this->get_field_id('show_thumbnail'); ?>" value="true" <?php checked(true, $instance['show_thumbnail']);?> />
            <label for="<?php echo $this->get_field_id('show_thumbnail'); ?>"> <?php _e( 'Display post thumbnail' ); ?></label><br />
        </p>
    <?php    
    }

    /**
     * Processing widget options on save
     *
     * @param array $new_instance The new options
     * @param array $old_instance The previous options
     */
    public function update( $new_instance, $old_instance ) {
        
        $instance = $old_instance;

        //tytul widgetu
        $instance['title'] = strip_tags($new_instance['title']);
        //ilosc wyswietlanych postow
        $instance['number'] = absint($new_instance['number']);    
        //zaznaczone kategorie
        $instance['categories']  = (array) $new_instance['categories'];
        //czy pokazac wypis
        $instance['show_excerpt'] = isset($new_instance['show_excerpt']);
        //dlugosc wypisu
        $instance['excerpt_length'] = absint($new_instance['excerpt_length']);
        //czy pokazac miniaturke wpisu
        $instance['show_thumbnail'] = isset($new_instance['show_thumbnail']);

        return $instance;
    }
}

Zakończenie

WordPress widget tutorialPo przerobieniu tej części, widget powinien się już ładnie wyświetlać, formularz zarządzający jego opcjami również jest już gotowy: zapisuje i odczytuje z bazy zapisane dane. Efekt widoczny jest na obrazku po lewej.

W kolejnej, ostatniej już części tego kursu, dowiecie się w jaki sposób pobrać z bazy danych posty, oraz jak je wyświetlić uwzględniając wszystkie zapisane opcje.

Już teraz zapraszam na kolejną część kursu WordPress widget tutorial – piszemy własny widget!

 

 

Wpis otagowano:

Pomogłem? Dodaj coś od siebie! Skomentuj ten wpis:

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *