Отличный вопрос! Реализация рейтинговой таблицы с фильтрацией по сезонам в WordPress — комплексная задача, которая затрагивает несколько ключевых аспектов разработки. Я подробно разберу несколько подходов — от самого простого до профессионального.
Анализ задачи
Перед началом реализации определим основные компоненты системы:
- Хранение данных - где и как хранить рейтинги
- Админ-интерфейс - ввод и управление данными
- Фронтенд-отображение - таблица на сайте
- Фильтрация - механизм выбора сезонов
- Сортировка - ordering по рейтингу
Способ 1: Использование плагинов (Для начинающих)
Рекомендуемые плагины:
TablePress + Дополнительные поля
- TablePress для создания таблиц
- Custom Post Type для сезонов
- Интеграция через шорткоды
SportsPress (для спортивных рейтингов)
- Специализированный плагин для спортивных данных
- Встроенная поддержка сезонов, лиг, турниров
- Готовые виджеты и шорткоды
Способ 2: Кастомная разработка (Рекомендуемый подход)
Шаг 1: Создание структуры данных
Создаем кастомный тип записи для рейтингов:
// functions.php function create_rating_post_type() { register_post_type('rating', [ 'labels' => [ 'name' => 'Рейтинги', 'singular_name' => 'Рейтинг' ], 'public' => true, 'has_archive' => true, 'supports' => ['title', 'custom-fields'], 'show_in_rest' => true ]); // Таксономия для сезонов register_taxonomy('season', ['rating'], [ 'labels' => [ 'name' => 'Сезоны', 'singular_name' => 'Сезон' ], 'hierarchical' => true, 'show_in_rest' => true ]); } add_action('init', 'create_rating_post_type');
Шаг 2: Мета-поля для рейтингов
// Добавляем мета-поля function add_rating_meta_boxes() { add_meta_box( 'rating_details', 'Детали рейтинга', 'render_rating_meta_box', 'rating', 'normal', 'high' ); } function render_rating_meta_box($post) { wp_nonce_field('save_rating_meta', 'rating_meta_nonce'); $rating = get_post_meta($post->ID, '_rating_value', true); $player_name = get_post_meta($post->ID, '_player_name', true); $team = get_post_meta($post->ID, '_team', true); $games_played = get_post_meta($post->ID, '_games_played', true); echo ' <p> <label>Имя игрока:</label> <input type="text" name="player_name" value="'.esc_attr($player_name).'" style="width:100%;"> </p> <p> <label>Команда:</label> <input type="text" name="team" value="'.esc_attr($team).'" style="width:100%;"> </p> <p> <label>Рейтинг:</label> <input type="number" name="rating_value" value="'.esc_attr($rating).'" step="0.1" min="0" max="10"> </p> <p> <label>Сыграно игр:</label> <input type="number" name="games_played" value="'.esc_attr($games_played).'"> </p>'; } function save_rating_meta($post_id) { if (!isset($_POST['rating_meta_nonce']) || !wp_verify_nonce($_POST['rating_meta_nonce'], 'save_rating_meta')) { return; } if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return; $fields = ['player_name', 'team', 'rating_value', 'games_played']; foreach ($fields as $field) { if (isset($_POST[$field])) { update_post_meta($post_id, '_'.$field, sanitize_text_field($_POST[$field])); } } } add_action('add_meta_boxes', 'add_rating_meta_boxes'); add_action('save_post', 'save_rating_meta');
Шаг 3: Шорткод для отображения таблицы
function rating_table_shortcode($atts) { $atts = shortcode_atts([ 'season' => '', 'limit' => 50 ], $atts); ob_start(); // Получаем сезоны для фильтра $seasons = get_terms([ 'taxonomy' => 'season', 'hide_empty' => false ]); // Форма фильтра echo '<div class="rating-filter">'; echo '<select id="season-filter" onchange="filterRatingTable()">'; echo '<option value="">Все сезоны</option>'; foreach ($seasons as $season) { $selected = $season->slug == $atts['season'] ? 'selected' : ''; echo '<option value="'.$season->slug.'" '.$selected.'>'.$season->name.'</option>'; } echo '</select>'; echo '</div>'; // Аргументы для WP_Query $args = [ 'post_type' => 'rating', 'posts_per_page' => $atts['limit'], 'meta_key' => '_rating_value', 'orderby' => 'meta_value_num', 'order' => 'DESC' ]; if (!empty($atts['season'])) { $args['tax_query'] = [ [ 'taxonomy' => 'season', 'field' => 'slug', 'terms' => $atts['season'] ] ]; } $ratings_query = new WP_Query($args); if ($ratings_query->have_posts()) { echo '<table class="rating-table">'; echo '<thead> <tr> <th>Место</th> <th>Игрок</th> <th>Команда</th> <th>Рейтинг</th> <th>Игры</th> <th>Сезон</th> </tr> </thead> <tbody>'; $position = 1; while ($ratings_query->have_posts()) { $ratings_query->the_post(); $post_id = get_the_ID(); $player_name = get_post_meta($post_id, '_player_name', true); $team = get_post_meta($post_id, '_team', true); $rating = get_post_meta($post_id, '_rating_value', true); $games = get_post_meta($post_id, '_games_played', true); $season_terms = get_the_terms($post_id, 'season'); $season_name = $season_terms ? $season_terms[0]->name : '—'; echo '<tr> <td>'.$position.'</td> <td>'.esc_html($player_name).'</td> <td>'.esc_html($team).'</td> <td>'.number_format($rating, 1).'</td> <td>'.$games.'</td> <td>'.$season_name.'</td> </tr>'; $position++; } echo '</tbody></table>'; wp_reset_postdata(); } else { echo '<p>Рейтинги не найдены.</p>'; } return ob_get_clean(); } add_shortcode('rating_table', 'rating_table_shortcode');
Шаг 4: JavaScript для AJAX-фильтрации
// rating-script.js function filterRatingTable() { const seasonSlug = document.getElementById('season-filter').value; const tableContainer = document.querySelector('.rating-table-container'); // Показываем индикатор загрузки tableContainer.innerHTML = '<div class="loading">Загрузка...</div>'; // AJAX запрос fetch('/wp-admin/admin-ajax.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'action=filter_ratings&season=' + seasonSlug }) .then(response => response.text()) .then(data => { tableContainer.innerHTML = data; }) .catch(error => { console.error('Error:', error); tableContainer.innerHTML = '<p>Ошибка загрузки данных</p>'; }); } // Инициализация при загрузке страницы document.addEventListener('DOMContentLoaded', function() { // Добавляем обработчик для селекта const seasonFilter = document.getElementById('season-filter'); if (seasonFilter) { seasonFilter.addEventListener('change', filterRatingTable); } });
Шаг 5: AJAX обработчик
// AJAX обработка фильтрации function ajax_filter_ratings() { $season = sanitize_text_field($_POST['season']); $args = [ 'post_type' => 'rating', 'posts_per_page' => 50, 'meta_key' => '_rating_value', 'orderby' => 'meta_value_num', 'order' => 'DESC' ]; if (!empty($season)) { $args['tax_query'] = [ [ 'taxonomy' => 'season', 'field' => 'slug', 'terms' => $season ] ]; } $ratings_query = new WP_Query($args); ob_start(); if ($ratings_query->have_posts()) { echo '<table class="rating-table">'; echo '<thead><tr><th>Место</th><th>Игрок</th><th>Команда</th><th>Рейтинг</th><th>Игры</th><th>Сезон</th></tr></thead><tbody>'; $position = 1; while ($ratings_query->have_posts()) { $ratings_query->the_post(); $post_id = get_the_ID(); $player_name = get_post_meta($post_id, '_player_name', true); $team = get_post_meta($post_id, '_team', true); $rating = get_post_meta($post_id, '_rating_value', true); $games = get_post_meta($post_id, '_games_played', true); $season_terms = get_the_terms($post_id, 'season'); $season_name = $season_terms ? $season_terms[0]->name : '—'; echo '<tr> <td>'.$position.'</td> <td>'.esc_html($player_name).'</td> <td>'.esc_html($team).'</td> <td>'.number_format($rating, 1).'</td> <td>'.$games.'</td> <td>'.$season_name.'</td> </tr>'; $position++; } echo '</tbody></table>'; wp_reset_postdata(); } else { echo '<p>Рейтинги не найдены для выбранного сезона.</p>'; } echo ob_get_clean(); wp_die(); } add_action('wp_ajax_filter_ratings', 'ajax_filter_ratings'); add_action('wp_ajax_nopriv_filter_ratings', 'ajax_filter_ratings');
Шаг 6: Стилизация
/* rating-styles.css */ .rating-filter { margin-bottom: 20px; } #season-filter { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } .rating-table { width: 100%; border-collapse: collapse; margin-top: 20px; } .rating-table th, .rating-table td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; } .rating-table th { background-color: #f8f9fa; font-weight: 600; } .rating-table tr:hover { background-color: #f8f9fa; } .loading { text-align: center; padding: 40px; font-size: 16px; color: #666; }
Шаг 7: Подключение скриптов и стилей
function enqueue_rating_assets() { wp_enqueue_script( 'rating-script', get_template_directory_uri() . '/js/rating-script.js', [], '1.0', true ); wp_enqueue_style( 'rating-styles', get_template_directory_uri() . '/css/rating-styles.css' ); // Локализация для AJAX wp_localize_script('rating-script', 'rating_ajax', [ 'ajax_url' => admin_url('admin-ajax.php') ]); } add_action('wp_enqueue_scripts', 'enqueue_rating_assets');
Способ 3: Использование Advanced Custom Fields (ACF)
Если вы используете ACF, процесс упрощается:
Настройка полей ACF:
- Группа полей: "Рейтинг"
- Поля:
- player_name (Text)
- team (Text)
- rating_value (Number)
- games_played (Number)
- Таксономия: season (создается автоматически)
Код для шорткода с ACF:
function acf_rating_table_shortcode($atts) { $atts = shortcode_atts(['season' => ''], $atts); ob_start(); // Фильтр сезонов $seasons = get_terms(['taxonomy' => 'season', 'hide_empty' => false]); echo '<div class="rating-filter">'; echo '<select id="season-filter">'; echo '<option value="">Все сезоны</option>'; foreach ($seasons as $season) { $selected = $season->slug == $atts['season'] ? 'selected' : ''; echo '<option value="'.$season->slug.'" '.$selected.'>'.$season->name.'</option>'; } echo '</select></div>'; // Таблица $args = [ 'post_type' => 'rating', 'posts_per_page' => -1, 'meta_key' => 'rating_value', 'orderby' => 'meta_value_num', 'order' => 'DESC' ]; if (!empty($atts['season'])) { $args['tax_query'] = [[ 'taxonomy' => 'season', 'field' => 'slug', 'terms' => $atts['season'] ]]; } $query = new WP_Query($args); if ($query->have_posts()) { echo '<div class="rating-table-container"><table class="rating-table"><thead><tr> <th>Место</th><th>Игрок</th><th>Команда</th><th>Рейтинг</th><th>Игры</th> </tr></thead><tbody>'; $position = 1; while ($query->have_posts()) { $query->the_post(); echo '<tr> <td>'.$position.'</td> <td>'.get_field('player_name').'</td> <td>'.get_field('team').'</td> <td>'.get_field('rating_value').'</td> <td>'.get_field('games_played').'</td> </tr>'; $position++; } echo '</tbody></table></div>'; wp_reset_postdata(); } return ob_get_clean(); } add_shortcode('acf_rating_table', 'acf_rating_table_shortcode');
Способ 4: Использование кастомной таблицы в БД
Для высоконагруженных проектов рекомендуется использовать кастомную таблицу:
// Создание таблицы function create_ratings_table() { global $wpdb; $table_name = $wpdb->prefix . 'ratings'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, player_name varchar(100) NOT NULL, team varchar(100) NOT NULL, rating_value decimal(3,1) NOT NULL, games_played int NOT NULL, season_slug varchar(50) NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } register_activation_hook(__FILE__, 'create_ratings_table');
Рекомендации по использованию
- Для простых проектов - используйте Способ 1 с плагинами
- Для типичных сайтов - Способ 2 или 3 с ACF
- Для высоконагруженных систем - Способ 4 с кастомной таблицей
Дополнительные улучшения
- Пагинация для больших