Статьи

Учебник по Ajax в интерфейсе WordPress: на самом деле

В этой серии мы обсуждаем, как реализовать Ajax во внешнем интерфейсе WordPress. В первом посте из этой серии мы рассмотрели, как Ajax работает на высоком уровне, рассмотрели, как вводить Ajax в Dashboard WordPress, и рассмотрели два метода, доступных для включения Ajax в WordPress.

На данный момент пришло время создать что-то, что продемонстрирует, как мы можем использовать Ajax во внешнем интерфейсе WordPress. Для этого мы напишем наш собственный плагин, следя за тем, чтобы мы следовали рекомендациям WordPress.

К концу этой статьи у нас будет полностью работающий плагин с общедоступным исходным кодом на GitHub. С этим сказал, давайте начнем.


Если вы читали какую-либо из моих предыдущих статей , то знаете, что мне всегда нравится начинать свои проекты с планирования того, что мы собираемся делать. Этот проект ничем не отличается.

В этом примере мы собираемся ввести флажок, который позволяет читателям, вошедшим на сайт WordPress, отмечать прочитанные ими сообщения в блоге. Таким образом, они будут видеть только сообщения, которые они еще не прочитали с момента входа на сайт.

Чтобы построить это, мы будем использовать несколько вещей:

  • Свежая установка WordPress
  • WordPress Theme Unit Test, чтобы предоставить нам множество примеров контента
  • Тема «Двадцать одиннадцать» (поскольку она так широко доступна)
  • Учетная запись пользователя с ролью «Подписчик»

Как только вы настроите среду, вот план действий, который мы будем выполнять для нашего плагина:

  • Мы создадим новый плагин в каталоге плагинов «Я прочитал это» (в каталоге «ive-read-this»)
  • В каждом посте мы будем ставить флажок и метку, которая позволяет пользователям отмечать, что они прочитали этот пост.
  • Как только сообщение будет помечено как прочитанное, опция исчезнет из поля зрения

Понял? Идите вперед и войдите в систему как пользователь, которого вы создали с ролью «Подписчик», и давайте начнем!


После того, как вы импортировали тестовые данные, ваша установка должна выглядеть примерно так:

Отсюда мы готовы начать писать плагин.

Прежде чем мы на самом деле напишем какой-либо код, я хотел бы продолжить и заглушить файлы плагинов. Это означает, что мы настроим каталог, базовую структуру плагина и любые каталоги, которые нам могут понадобиться для использования зависимостей плагина.

Так как мы будем предоставлять легкий стиль, а также файлы JavaScript для запуска Ajax-запроса, вот как должна выглядеть базовая структура каталогов. Каталог lang является необязательным, хотя я считаю, что это лучший метод:

Теперь давайте оформим текст, необходимый для plugin.php . Обратите внимание, что это будет не что иное, как скелет. Он будет в первую очередь отвечать за создание основы для того, что мы будем строить позже в этой статье:

01
02
03
04
05
06
07
08
09
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
<?php
/*
Plugin Name: I’ve Read This
Plugin URI: http://github.com/tommcfarlin/ive-read-this/
Description: A simple plugin for allowing site members to mark when they’ve read a post.
Version: 1.0
Author: Tom McFarlin
Author URI: http://tommcfarlin.com/
Author Email: [email protected]
License:
 
  Copyright 2012 Tom McFarlin ([email protected])
 
  This program is free software;
  it under the terms of the GNU General Public License, version 2, as
  published by the Free Software Foundation.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY;
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public License
  along with this program;
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 
*/
 
class IveReadThis {
 
    /*———————————————*
     * Constructor
     *———————————————*/
 
    /**
     * Initializes the plugin by setting localization, filters, and administration functions.
     */
    function __construct() {
 
        load_plugin_textdomain( ‘ive-read-this’, false, dirname( plugin_basename( __FILE__ ) ) . ‘/lang’ );
 
        // Register site styles and scripts
        add_action( ‘wp_enqueue_scripts’, array( &$this, ‘register_plugin_styles’ ) );
        add_action( ‘wp_enqueue_scripts’, array( &$this, ‘register_plugin_scripts’ ) );
 
    } // end constructor
 
    /**
     * Registers and enqueues plugin-specific styles.
     */
    public function register_plugin_styles() {
 
        wp_register_style( ‘ive-read-this’, plugins_url( ‘ive-read-this/css/plugin.css’ ) );
        wp_enqueue_style( ‘ive-read-this’ );
 
    } // end register_plugin_styles
 
    /**
     * Registers and enqueues plugin-specific scripts.
     */
    public function register_plugin_scripts() {
 
        wp_register_script( ‘ive-read-this’, plugins_url( ‘ive-read-this/js/plugin.js’ ), array( ‘jquery’ ) );
        wp_enqueue_script( ‘ive-read-this’ );
 
    } // end register_plugin_scripts
 
} // end class
 
new IveReadThis();
?>

На этом этапе вы можете активировать плагин на панели инструментов плагина WordPress, хотя на самом деле ничего не произойдет. Если вы готовы, сделайте это сейчас — вы сможете наблюдать, как плагин оживает, когда мы работаем над этим.

Поскольку мы собираемся установить флажок в каждом сообщении, который позволяет пользователям переключаться независимо от того, прочитали они сообщение или нет, нам необходимо учесть следующее:

  • Мы должны убедиться, что флажок отображается только когда пользователь вошел в систему
  • Флажок должен быть внизу сообщения на странице одного сообщения, так как именно там пользователь отметит, что он прочитал сообщение

Мы можем достичь того и другого, добавив следующую функцию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * Adds a checkbox to the end of a post in single view that allows users who are logged in
 * to mark their post as read.
 *
 * @param $content The post content
 * @return The post content with or without the added checkbox
 */
public function add_checkbox( $content ) {
 
    // We only want to modify the content if the user is logged in
    if( is_user_logged_in() && is_single() ) {
 
        // Build the element that will be used to mark this post as read
        $html = ‘<div id=»ive-read-this-container»>’;
            $html .= ‘<label for=»ive-read-this»>’;
                $html .= ‘<input type=»checkbox» name=»ive-read-this» id=»ive-read-this» value=»0″ />’;
                $html .= __( «I’ve read this post.», ‘ive-read-this’ );
            $html .= ‘</label>’;
        $html .= ‘</div><!— /#ive-read-this-container —>’;
 
        // Append it to the content
        $content .= $html;
 
    } // end if
 
    return $content;
 
} // end add_checkbox

Внимательно прочитайте комментарии, так как они должны точно объяснить, что происходит; Однако, если вам неясно, не стесняйтесь оставлять комментарии.

Далее нам нужно добавить следующую строку в конструктор, чтобы был the_content фильтр the_content :

1
2
// Setup the action for rendering the checkbox
add_filter( ‘the_content’, array( &$this, ‘add_checkbox’ ) );

Наконец, давайте добавим немного стиля, чтобы придать чекбоксу немного уникальный внешний вид в контексте темы Twenty Eleven. В файле plugin.css плагина добавьте следующий код:

1
2
3
4
5
6
#ive-read-this-container {
    margin: 1em 0 1em;
    background: #eee;
    border: 1px solid #ccc;
    padding: 0.25em;
}

Теперь, если вы войдете в установку WordPress и перейдете к нижней части одного сообщения, вы должны увидеть что-то вроде следующего изображения:

На данный момент мы готовы начать писать JavaScript.

Первое, что нам нужно сделать, это настроить JavaScript так, чтобы он запускался, только если на странице находится контейнер «Я прочитал это». Есть множество способов сделать это, но так как мы загружаем JavaScript на каждую страницу, мы будем использовать JavaScript, чтобы проверить наличие флажка «Я прочитал это», который мы выписываем на страницу.

Для этого добавьте следующий код в plugin.js . Комментарии к коду должны быть понятны. Если нет, оставьте комментарий!

1
2
3
4
5
6
7
8
9
(function ($) {
    $(function () {
 
        // If the «I’ve Read This Container» is on this page, let’s setup the event handler
        if(1 === $(‘#ive-read-this-container’).length) {
        } // end if
 
    });
}(jQuery));

В коде, который вы видите выше, все, что мы поместим в условное выражение, сработает только при наличии элемента контейнера «Я прочитал это».

В этом случае мы знаем, что нам нужно отправить идентификатор сообщения на сервер. Поскольку пользователь вошел в систему, мы сможем получить его / ее идентификатор на стороне сервера.

Итак, следующий шаг — получить идентификатор поста, на котором мы находимся. К счастью, Twenty Eleven хранит номер поста в идентификаторе элемента article . Нам просто нужно разобрать это.

Давайте сделаем это сейчас:

01
02
03
04
05
06
07
08
09
10
11
12
13
// We use the change attribute so that the event handler fires
// whenever the checkbox or its associated label are clicked.
$(‘input[name=»ive-read-this»]’).change(function (evt) {
 
    // We can retrieve the ID of this post from the <article>’s ID.
    // so that we can mark that the user has read this particular post and we can hide it.
    var sArticleId, iPostId;
 
    // Get the article ID and split it — the second index is always the post ID in Twenty Eleven
    sArticleId = $(«article»).attr(‘id’);
    iPostId = parseInt(sArticleId.split(‘-‘)[1]);
 
});

На данный момент мы готовы настроить запрос Ajax. Для этого мы будем использовать метод $.post jQuery, и мы будем использовать стандартный ajaxurl WordPress для обработки нашего ответа.

Давайте продолжим и напишем код, который будет пересылать идентификатор сообщения. Мы будем беспокоиться об ответе позже в этой статье, поэтому в коде есть комментарий «TODO».

1
2
3
4
5
6
7
8
// Initial the request to mark this this particular post as read
$.post(ajaxurl, {
 
    post_id: iPostId
 
}, function (response) {
    // TODO
});

На данном этапе разработки полный исходный код JavaScript должен выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
(function ($) {
    $(function () {
 
        // If the «I’ve Read This Container» is on this page, let’s setup the event handler
        if(1 === $(‘#ive-read-this-container’).length) {
 
            // We use the change attribute so that the event handler fires
            // whenever the checkbox or its associated label are clicked.
            $(‘input[name=»ive-read-this»]’).change(function (evt) {
 
                // We can retrieve the ID of this post from the <article>’s ID.
                // so that we can mark that the user has read this particular post and we can hide it.
                var sArticleId, iPostId;
 
                // Get the article ID and split it — the second index is always the post ID in Twenty Eleven
                sArticleId = $(«article»).attr(‘id’);
                iPostId = parseInt(sArticleId.split(‘-‘)[1]);
 
                // Initialise the request to mark this particular post as read
                $.post(ajaxurl, {
 
                    post_id: iPostId
 
                }, function (response) {
                    // TODO
                });
 
            });
 
        } // end if
 
    });
}(jQuery));

Те из вас, кто работал с примером кода, когда читали статью, сразу заметят, что ваш браузер выдает консольную ошибку:

Uncaught ReferenceError: ajaxurl is not defined

К сожалению! И именно здесь мы должны убедиться, что правильно включили библиотеку Ajax WordPress.

Для этого нам нужно подключиться к действию wp_head . Добавьте следующую строку кода в конструктор вашего плагина:

1
2
// Include the Ajax library on the front end
add_action( ‘wp_head’, array( &$this, ‘add_ajax_library’ ) );

Затем добавьте следующую функцию. Вот что на самом деле отвечает за включение библиотеки Ajax:

01
02
03
04
05
06
07
08
09
10
11
12
/**
 * Adds the WordPress Ajax Library to the frontend.
 */
public function add_ajax_library() {
 
    $html = ‘<script type=»text/javascript»>’;
        $html .= ‘var ajaxurl = «‘ . admin_url( ‘admin-ajax.php’ ) . ‘»‘;
    $html .= ‘</script>’;
 
    echo $html;
 
} // end add_ajax_library

Теперь, если вы попытаетесь выполнить код, у вас не должно возникнуть проблем. На данный момент мы готовы продолжать.

Теперь, когда мы получили запрос на сервер, мы можем написать наш обработчик событий на стороне сервера. Вот как должен работать обработчик:

  • Убедитесь, что значение входящего идентификатора поста установлено и является числовым значением (это очень элементарная защита от подделки, но она работает для всех целей и задач).
  • Затем попробуйте обновить мету текущего пользователя, используя его / ее идентификатор и идентификатор сообщения.
  • Если обновление завершится неудачно, мы вернем -1 ; в противном случае мы вернем 1 . Мы будем обрабатывать эти значения в обработчике ответа в JavaScript.

Сначала мы добавим хук в конструктор плагина:

1
2
// Setup the event handler for marking this post as read for the current user
add_action( ‘wp_ajax_mark_as_read’, array( &amp;$this, ‘mark_as_read’ ) );

Далее мы на самом деле реализуем обработчик:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
/**
 * Uses the current user ID and the incoming post ID to mark this post as read
 * for the current user.
 *
 * We store this post’s ID in the associated user’s meta so that we can hide it
 * from displaying in the list later.
 */
public function mark_as_read() {
 
    // First, we need to make sure the post ID parameter has been set and that it’s a numeric value
    if( isset( $_POST[‘post_id’] ) && is_numeric( $_POST[‘post_id’] ) ) {
 
        // If we fail to update the user meta, respond with -1;
        echo false == update_user_meta( wp_get_current_user()->ID, $_POST[‘post_id’], ‘ive_read_this’ ) ?
 
    } // end if
 
    die();
 
} // end mark_as_read

Для этого давайте вернемся к выдающемуся TODO в функции ответа JavaScript, над которым мы работали. Вот полный сценарий:

01
02
03
04
05
06
07
08
09
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
(function ($) {
    $(function () {
 
        // If the «I’ve Read This Container» is on this page, let’s setup the event handler
        if(1 === $(‘#ive-read-this-container’).length) {
 
            // We use the change attribute so that the event handler fires
            // whenever the checkbox or its associated label are clicked.
            $(‘input[name=»ive-read-this»]’).change(function (evt) {
 
                // We can retrieve the ID of this post from the <article>’s ID.
                // so that we can mark that the user has read this particular post and we can hide it.
                var sArticleId, iPostId;
 
                // Get the article ID and split it — the second index is always the post ID in Twenty Eleven
                sArticleId = $(«article»).attr(‘id’);
                iPostId = parseInt(sArticleId.split(‘-‘)[1]);
 
                // Initial the request to mark this this particular post as read
                $.post(ajaxurl, {
 
                    action: ‘mark_as_read’,
                    post_id: iPostId
 
                }, function (response) {
 
                    // If the server returns ‘1’, then we can mark this post as read, so we’ll hide the checkbox
                    // container.
                    if (1 === parseInt(response)) {
 
                        $(‘#ive-read-this-container’).slideUp(‘fast’);
 
                    // Otherwise, let’s alert the user that there was a problem.
                    // want to handle this more gracefully.
                    } else {
 
                        alert(«There was an error marking this post as read. Please try again.»);
 
                    } // end if/else
 
                });
 
            });
 
        } // end if
 
    });
}(jQuery));

Если пользователь случайно нашел путь к отдельной странице поста (например, был связан с ней), мы должны проверить, не пометили ли они ранее ее для прочтения.

Для этого нам нужно реорганизовать функцию add_checkbox чтобы она проверяла, вошел ли пользователь в систему, и читает метаданные пользователя, чтобы определить, была ли запись ранее помечена как прочитанная:

01
02
03
04
05
06
07
08
09
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
/**
 * Adds a checkbox to the end of a post in single view that allows users who are logged in
 * to mark their post as read.
 *
 * @param $content The post content
 * @return The post content with or without the added checkbox
 */
public function add_checkbox( $content ) {
 
    // We only want to modify the content if the user is logged in
    if( is_single() ) {
 
        // If the user is logged in…
        if( is_user_logged_in() ) {
 
            // And if they’ve previously read this post…
            if( ‘ive_read_this’ == get_user_meta( wp_get_current_user()->ID, get_the_ID(), true ) ) {
 
                // Build the element to indicate this post has been read
                $html = ‘<div id=»ive-read-this-container»>’;
                    $html .= ‘<strong>’;
                        $html .= __( «I’ve read this post.», ‘ive-read-this’ );
                    $html .= ‘</strong>’;
                $html .= ‘</div><!— /#ive-read-this-container —>’;
 
            // Otherwise, give them the option to mark this post as read
            } else {
 
                // Build the element that will be used to mark this post as read
                $html = ‘<div id=»ive-read-this-container»>’;
                    $html .= ‘<label for=»ive-read-this»>’;
                        $html .= ‘<input type=»checkbox» name=»ive-read-this» id=»ive-read-this» value=»0″ />’;
                        $html .= __( «I’ve read this post.», ‘ive-read-this’ );
                    $html .= ‘</label>’;
                $html .= ‘</div><!— /#ive-read-this-container —>’;
 
            } // end if
 
            // Append it to the content
            $content .= $html;
 
        } // end if
 
    } // end if
 
    return $content;
 
} // end add_checkbox

На данный момент у вас есть рабочий плагин:

  • Вы должны быть в состоянии перейти к любому сообщению
  • Если вы не читали его, то сможете установить флажок, и он исчезнет.
  • Если вы перезагрузите страницу, вы увидите уведомление о том, что сообщение было помечено как прочитанное.

Неплохо, правда?

Конечно, всегда есть возможность самостоятельно поэкспериментировать с этим. Например, вы можете исключить эти записи из основного цикла, если они помечены как прочитанные. Другой вариант — добавить пользовательское имя класса, а затем оформить сообщение, чтобы указать, что его прочитал текущий пользователь.

Наконец, помните, что вы можете получить весь исходный код полностью на GitHub .