WPAdmin.pl

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

Piszemy Widget do WordPress

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.

W 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:

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:

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

Po 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!

 

 

Exit mobile version