Статьи

Использование Backbone в WordPress Admin: бэкэнд

Слухи верны! Панель администратора WordPress теперь использует Underscore и Backbone! Это означает, что с минимальными усилиями мы можем начать использовать эти фантастические библиотеки JavaScript в наших собственных плагинах. Этот урок покажет вам, как именно вы можете это сделать. Мы создадим административную часть плагина Quiz. Мы будем использовать простой пользовательский тип записи для сохранения вопросов, а затем в каждом вопросе добавим мета-поле, которое позволит нам ввести до 4 ответов и выбрать правильный. Мы рассмотрим, как использовать шаблоны, как подключаться к событиям нажатия и нажатия клавиш, как сохранять данные обратно в базу данных WordPress и, самое главное, как «вытащить свою правду из Dom» в качестве создателя. Джереми Ашкенас любит это выражать.

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


В этой первой части мы настроим серверную часть нашего плагина. Это будет включать в себя настройку файлов и папок, а также реализацию всех функций, которые требуются нашему плагину в PHP. Нам нужно будет:

  1. Зарегистрируйте пользовательский тип сообщения — для наших вопросов
  2. Добавьте мета-поле, которое позволит нам вводить ответы на той же странице
  3. Сохраняйте информацию из мета-полей при сохранении записи
  4. Сохранить информацию из наших запросов AJAX (через Backbone)

После того, как мы настроим наш сервер, мы продолжим выводить необходимый HTML-код для нашего мета-блока вместе с данными для каждого ответа в формате JSON. Мы также напишем JavaScript, который связывает все вместе. Мы рассмотрим:

  1. Вывод базового HTML для мета-блока
  2. Вывод шаблона на стороне клиента вместе с ответами в формате JSON
  3. JavaScript должен был связать все это

Я надеюсь, что эта небольшая серия будет интересна для вас, и я с нетерпением жду, чтобы помочь вам начать работу с Backbone.js в плагине WordPress.


Этот небольшой плагин будет использовать пользовательский тип сообщения для сохранения вопросов. Затем в мета-поле мы создадим четыре входа, которые позволят пользователям вводить возможные ответы на текущий вопрос и выбирать, какой из них является правильным. При изменении ответа соответствующая кнопка сохранения станет активной. При щелчке мы будем использовать встроенный метод Backbone model.save() для сохранения данных обратно в базу данных WordPress. Кроме того, когда ответы записываются во входные данные, поле выбора ниже автоматически обновит свои значения, так как будет искать изменения в моделях. Все эти вещи относительно просты в работе с Backbone, и после прочтения этого руководства вы сможете перейти на новый уровень с помощью их использования в WordPress.

answers01

Есть много чего, так что давайте начнем!


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

  1. Создайте папку с именем wp_quiz
  2. Создайте файл PHP внутри с тем же именем
  3. Создайте папку с именем js
  4. Создайте папку с именем src

Структура вашей папки должна выглядеть следующим образом.

files01

Внутри wp_quiz.php .

1
2
3
4
5
6
7
8
/*
Plugin Name: WP Quiz
Plugin URI: http://wp.tutsplus.com/author/shaneosbourne/
Description: An example of using Backbone within a plugin.
Author: Shane Osbourne
Version: 0.1
Author URI: http://wp.tutsplus.com/author/shaneosbourne/
*/

Все еще внутри wp_quiz.php , нам нужно сделать следующие вещи:

  1. Включите наш основной класс плагинов
  2. Создайте функцию, которая создаст экземпляр класса
  3. Добавить хук, чтобы вызывать функцию только когда пользователь является администратором
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/** wp_quiz.php **/
 
include ‘src/WpQuiz.php’;
 
// Create an instance of the Plugin Class
function call_wp_quiz() {
    return new WpQuiz( ‘admin’ );
}
 
// Only when the current user is an Admin
if ( is_admin )
    add_action( ‘init’, ‘call_wp_quiz’ );
 
// Helper function
if ( ! function_exists( ‘pp’ ) ) {
    function pp() {
        return plugin_dir_url( __FILE__ );
    }
}

Помещение вспомогательной функции pp() в этот файл позволит нам ссылаться на другие файлы относительно корня папки плагина (вскоре вы увидите это в действии).


Внутри папки src создайте файл с именем WpQuiz.php .

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

  1. Зарегистрируйте пользовательский тип сообщения
  2. Добавить метабокс
  3. Получить содержимое для мета-блока и вывести в него как HTML, так и некоторые данные JSON
  4. Прослушивание запросов PUT и сохранение данных в базе данных.
  5. Сохраняйте данные нашего мета-бокса при обычных действиях «сохранить»

Прежде чем писать методы, мы собираемся сохранить некоторую информацию в качестве свойств класса. Мы храним эту информацию в верхней части нашего файла классов, чтобы потом было легче вносить изменения. Массив answerIds содержит ключи, которые мы будем использовать в этом плагине для сохранения данных с помощью встроенной функции add_post_meta() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
/** src/WpQuiz.php **/
class WpQuiz {
 
    // Names of Custom Post Type
    public $postTypeNameSingle = ‘Question’;
    public $postTypeNamePlural = ‘Questions’;
 
    // Meta Box Stuff
    public $metaBoxTitle = ‘Answers’;
    public $metaBoxTempl = ‘templates/metabox.templ.php’;
 
    // Question Id’s
    public $answerIds = array( ‘quiz-a-1’, ‘quiz-a-2’, ‘quiz-a-3’, ‘quiz-a-4’ );
 
    // Javascript
    public $jsAdmin = ‘/js/admin.js’;
 
}
  1. Сначала мы регистрируем пользовательский тип записи, используя другой вспомогательный метод (который будет показан позже)
  2. Затем мы регистрируем крюк для загрузки нашего мета-окна
  3. Нам также нужен отдельный метод для принятия наших запросов AJAX
  4. Наконец, когда страница загружена, мы хотим сохранить информацию из нашего мета-окна.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/** src/WpQuiz.php **/
 
public function __construct( $type ) {
    switch ( $type ) {
        case ‘admin’ :
            // Register the Post Type
            $this->registerPostType(
                $this->postTypeNameSingle,
                $this->postTypeNamePlural
            );
 
            // Add the Meta Box
            add_action( ‘add_meta_boxes’, array( $this, ‘addMetaBox’ ) );
 
            // Accept an Ajax Request
            add_action( ‘wp_ajax_save_answer’, array( $this, ‘saveAnswers’ ) );
 
            // Watch for Post being saved
            add_action( ‘save_post’, array( $this, ‘savePost’ ) );
    }
}
  1. Добавьте файлы JavaScript, необходимые для этого плагина — снова с помощью вспомогательного метода (увидим позже)
  2. Создайте уникальный идентификатор для этого плагина на основе названия типа сообщения
  3. Добавьте мета-поле, используя свойства, которые мы установили ранее
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/** src/WpQuiz.php **/
 
public function addMetaBox() {
 
    // Load the Javascript needed on this admin page.
    $this->addScripts();
 
    // Create an id based on Post-type name
    $id = $this->postTypeNameSingle .
 
    // Add the meta box
    add_meta_box(
        $id,
        $this->metaBoxTitle,
        array( $this, ‘getMetaBox’ ), // Get the markup needed
        $this->postTypeNameSingle
    );
 
}

Здесь мы перебираем наши идентификаторы ответов и создаем массив, содержащий метаданные getOneAnswer с помощью нашего вспомогательного метода getOneAnswer . Мы создаем этот новый массив, чтобы мы могли его кодировать и отправлять в наш шаблон в формате JSON — так, как это нравится Backbone. Мы отправляем данные в наш шаблон с $viewData массива $viewData показанного ниже. Это защищает весь HTML-код и позволяет нам работать над ним в отдельном файле. getTemplatePart метод getTemplatePart , но если вы хотите получить подробное объяснение того, почему я его использую, ознакомьтесь с getTemplatePart процесса — getTemplatePart от логики!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
/** src/WpQuiz.php **/
 
public function getMetaBox( $post ) {
 
    // Get the current values for the questions
    $json = array();
    foreach ( $this->answerIds as $id ) {
        $json[] = $this->getOneAnswer( $post->ID, $id );
    }
 
    // Set data needed in the template
    $viewData = array(
        ‘post’ => $post,
        ‘answers’ => json_encode( $json ),
        ‘correct’ => json_encode( get_post_meta( $post->ID, ‘correct_answer’ ) )
    );
 
    echo $this->getTemplatePart( $this->metaBoxTempl, $viewData );
 
}

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

1
2
3
4
5
6
7
8
/** src/WpQuiz.php **/
 
public function getOneAnswer( $post_id, $answer_id ) {
    return array(
        ‘answer_id’ => $answer_id,
        ‘answer’ => get_post_meta( $post_id, $answer_id, true )
    );
}

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

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
/** src/WpQuiz.php **/
 
public function savePost( $post_id ) {
    // Check that we are saving our Custom Post type
    if ( $_POST[‘post_type’] !== strtolower( $this->postTypeNameSingle ) ) {
        return;
    }
 
    // Check that the user has correct permissions
    if ( ! $this->canSaveData( $post_id ) ) {
        return;
    }
 
    // Access the data from the $_POST global and create a new array containing
    // the info needed to make the save
    $fields = array();
    foreach ( $this->answerIds as $id ) {
        $fields[$id] = $_POST[$id];
    }
 
    // Loop through the new array and save/update each one
    foreach ( $fields as $id => $field ) {
        add_post_meta( $post_id, $id, $field, true );
        // or
        update_post_meta( $post_id, $id, $field );
    }
 
    // Save/update the correct answer
    add_post_meta( $post_id, ‘correct_answer’, $_POST[‘correct_answer’], true );
    // or
    update_post_meta( $post_id, ‘correct_answer’, $_POST[‘correct_answer’] );
 
}

Здесь мы будем получать данные, передаваемые на сервер от Backbone. Мы должны:

  1. Доступ к данным, отправленным как запрос PUT. Как это будет в формате JSON, нам нужно декодировать его
  2. Еще раз проверьте, что текущий пользователь имеет соответствующие разрешения
  3. Идите и попытайтесь сохранить
  4. Если либо Добавить, либо Обновление прошло успешно, мы можем просто вернуть вновь сохраненные данные, и Backbone будет считать это успешным
  5. Если ни один из них не был успешным, мы просто возвращаем 0, чтобы указать сбой
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** src/WpQuiz.php **/
 
public function saveAnswers() {
    // Get PUT data and decode it
    $model = json_decode( file_get_contents( «php://input» ) );
 
    // Ensure that this user has the correct permissions
    if ( ! $this->canSaveData( $model->post_id ) ) {
        return;
    }
 
    // Attempt an insert/update
    $update = add_post_meta( $model->post_id, $model->answer_id, $model->answer, true );
    // or
    $update = update_post_meta( $model->post_id, $model->answer_id, $model->answer );
 
    // If a save or update was successful, return the model in JSON format
    if ( $update ) {
        echo json_encode( $this->getOneAnswer( $model->post_id, $model->answer_id ) );
    } else {
        echo 0;
    }
    die();
}

Вот четыре помощника, упомянутых в приведенных выше фрагментах.

  1. canSaveData() — Это просто гарантирует, что текущий пользователь имеет соответствующие разрешения для редактирования / обновления этого сообщения.
  2. addScripts() — обратите внимание, что здесь мы гарантируем, что передаем 5-й параметр в wp_register_script() . Это загрузит наш пользовательский JavaScript в нижний колонтитул и обеспечит доступность наших данных JSON. Кроме того, если вы используете редактор WordPress в своем плагине, вам не нужно указывать Backbone как зависимость, так как он уже будет доступен для вас. Я включил это здесь ради примера все же.
  3. registerPostType() — это то, что я часто использую в плагинах. Это просто облегчает жизнь при добавлении нового пользовательского типа сообщения. Он принимает как единственную, так и множественную версии названия, потому что это не всегда так просто, как просто добавить ‘s’.
  4. getTemplatePart() — я никогда не любил иметь разметку внутри моих методов. Этот маленький помощник позволит использовать отдельный файл шаблона.
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/** src/WpQuiz.php **/
 
/**
* Determine if the current user has the relevant permissions
*
* @param $post_id
* @return bool
*/
private function canSaveData( $post_id ) {
    if ( defined( ‘DOING_AUTOSAVE’ ) && DOING_AUTOSAVE )
        return false;
    if ( ‘page’ == $_POST[‘post_type’] ) {
        if ( ! current_user_can( ‘edit_page’, $post_id ) )
            return false;
    } else {
        if ( ! current_user_can( ‘edit_post’, $post_id ) )
            return false;
    }
    return true;
}
 
private function addScripts() {
    wp_register_script( ‘wp_quiz_main_js’, pp() . $this->jsAdmin , array( ‘backbone’ ), null, true );
    wp_enqueue_script( ‘wp_quiz_main_js’ );
}
 
/**
* Register a Custom Post Type
*
* @param $single
* @param $plural
* @param null $supports
*/
private function registerPostType( $single, $plural, $supports = null ) {
 
    $labels = array(
        ‘name’ => _x( $plural, ‘post type general name’ ),
        ‘singular_name’ => _x( «$single», ‘post type singular name’ ),
        ‘add_new’ => _x( «Add New $single», «$single» ),
        ‘add_new_item’ => __( «Add New $single» ),
        ‘edit_item’ => __( «Edit $single» ),
        ‘new_item’ => __( «New $single» ),
        ‘all_items’ => __( «All $plural» ),
        ‘view_item’ => __( «View $single» ),
        ‘search_items’ => __( «Search $plural» ),
        ‘not_found’ => __( «No $plural found» ),
        ‘not_found_in_trash’ => __( «No $single found in Trash» ),
        ‘parent_item_colon’ => »,
        ‘menu_name’ => $plural
    );
    $args = array(
        ‘labels’ => $labels,
        ‘public’ => true,
        ‘publicly_queryable’ => true,
        ‘show_ui’ => true,
        ‘show_in_menu’ => true,
        ‘query_var’ => true,
        ‘rewrite’ => true,
        ‘capability_type’ => ‘post’,
        ‘has_archive’ => true,
        ‘hierarchical’ => false,
        ‘menu_position’ => null,
        ‘supports’ => ( $supports ) ?
    );
    register_post_type( $single, $args );
}
 
/**
* Render a Template File
*
* @param $filePath
* @param null $viewData
* @return string
*/
public function getTemplatePart( $filePath, $viewData = null ) {
 
    ( $viewData ) ?
 
    ob_start();
    include ( «$filePath» );
    $template = ob_get_contents();
    ob_end_clean();
 
    return $template;
}

На данный момент мы настроили все необходимое для нашего бэкенда. Пришло время сделать перерыв и подготовиться к следующей части, где мы будем разбираться с клиентскими шаблонами, JavaScript и Backbone.js. Я надеюсь увидеть вас там — это будет хорошо.