让Elementor支持文章置顶

WordPress的文章是自带“置顶”功能的,但它似乎仅支持某些主题的首页。如果使用了某些插件排序,比如elementor制作文章列表,是不支持的。所以我开发了这个功能,利用WordPress原有的“置顶”设置让哪些文章置顶,并且让Elementor的Archive Post、Loop Grid、Loop Carousel都支持。

如何支持函数我就不赘述了,functions.php

 

/**
 * Elementor Loop Grid & Loop Carousel:置顶文章优先显示 + CSS 标记
 * 
 * [NEW] 同时支持 Current Query(Loop Grid)和 Custom Query(Loop Carousel)
 * 配置化版本:支持灵活控制生效范围
 */

// ========== 配置区域:在这里控制生效范围 ==========
$sticky_config = [
    'enable_home'           => true,   // 首页
    'enable_all_archives'   => false,  // 所有归档页(包括分类、标签、作者、日期等)
    'enable_all_categories' => false,  // 所有分类归档页
    'enable_all_tags'       => false,  // 所有标签归档页
    'enable_all_singles'    => false,  // 所有文章页(单篇 post)
    'enable_all_pages'      => false,  // 所有 Page 页面(单页)
    'enable_author'         => false,  // 作者归档页
    'enable_date'           => false,  // 日期归档页
    'enable_search'         => false,  // 搜索结果页

    // [NEW] Loop Carousel / Custom Query 支持
    'enable_custom_queries' => true,   // 是否对 Elementor 的自定义查询(Custom Query)也生效

    // 指定分类 slug(留空 = 不限定;填入则只在这些分类树内生效)
    'specific_categories'   => ['products'],

    // 指定标签 slug(留空 = 不限定)
    'specific_tags'         => [],

    // 指定自定义分类法 taxonomy(留空 = 不限定)
    'specific_taxonomies'   => [],

    // 调试模式:true = 在页面底部显示调试信息(仅管理员可见)
    'debug_mode'            => false,
];

// ========== 以下代码无需修改,自动根据上面配置生效 ==========

// 全局变量收集调试消息
global $sticky_debug_messages;
$sticky_debug_messages = [];

// 自定义调试函数(收集消息)
function sticky_debug_log($message) {
    global $sticky_debug_messages;
    $sticky_debug_messages[] = $message;
}

// [NEW] 通用函数:判断当前查询是否应该启用置顶逻辑
function sticky_should_apply($query, $sticky_config) {
    // 如果是后台,跳过
    if (is_admin() && !wp_doing_ajax()) return false;

    $is_main_query = $query->is_main_query();
    $is_custom_query = !$is_main_query && $sticky_config['enable_custom_queries'];

    // 必须是主查询 或 允许自定义查询
    if (!$is_main_query && !$is_custom_query) return false;

    // 只处理文章类型为 post 的查询
    $post_type = $query->get('post_type');
    if (!empty($post_type) && $post_type !== 'post' && !in_array('post', (array)$post_type)) {
        return false;
    }

    return true;
}

// [NEW] 通用函数:检查当前页面/查询类型是否在允许范围内
function sticky_check_page_type($query, $sticky_config) {
    $is_allowed = false;
    $page_type = '';
    $cat_ids_to_check = [];

    // 1. 首页
    if ($query->is_home() && $sticky_config['enable_home']) {
        $is_allowed = true;
        $page_type = 'home';
    }

    // 2. 所有归档页
    if ($query->is_archive() && $sticky_config['enable_all_archives']) {
        $is_allowed = true;
        $page_type = 'archive';
    }

    // 3. 分类归档页
    if ($query->is_category() || $query->get('category_name') || $query->get('cat')) {
        $category_name = $query->get('category_name');
        $cat_id = $query->get('cat');
        
        $term = null;
        if ($category_name) {
            $term = get_term_by('slug', $category_name, 'category');
        } elseif ($cat_id) {
            $term = get_term($cat_id, 'category');
        }
        
        if ($term && !is_wp_error($term)) {
            $target_cat_id = $term->term_id;
            
            // 3a. 所有分类归档
            if ($sticky_config['enable_all_categories']) {
                $is_allowed = true;
                $page_type = 'category (all)';
            }
            
            // 3b. 指定分类树
            if (!empty($sticky_config['specific_categories'])) {
                if (in_array($term->slug, $sticky_config['specific_categories'])) {
                    $is_allowed = true;
                    $page_type = 'category (specific: ' . $term->slug . ')';
                } else {
                    // 检查祖先
                    $ancestors = get_ancestors($target_cat_id, 'category');
                    foreach ($ancestors as $ancestor_id) {
                        $ancestor_term = get_term($ancestor_id, 'category');
                        if ($ancestor_term && in_array($ancestor_term->slug, $sticky_config['specific_categories'])) {
                            $is_allowed = true;
                            $page_type = 'category (child of: ' . $ancestor_term->slug . ')';
                            break;
                        }
                    }
                }
            }

            // 获取该分类及其所有子分类的ID
            if ($is_allowed) {
                $cat_ids_to_check[] = $target_cat_id;
                $children = get_term_children($target_cat_id, 'category');
                if (!is_wp_error($children) && !empty($children)) {
                    $cat_ids_to_check = array_merge($cat_ids_to_check, $children);
                }
            }
        }
    }

    // 4. 标签归档页
    if ($query->is_tag() || $query->get('tag')) {
        if ($sticky_config['enable_all_tags']) {
            $is_allowed = true;
            $page_type = 'tag (all)';
        } elseif (!empty($sticky_config['specific_tags'])) {
            $tag_slug = $query->get('tag');
            if (in_array($tag_slug, $sticky_config['specific_tags'])) {
                $is_allowed = true;
                $page_type = 'tag (specific: ' . $tag_slug . ')';
            }
        }
    }

    // 5. 自定义分类法归档页
    if (($query->is_tax() || $query->get('taxonomy')) && !empty($sticky_config['specific_taxonomies'])) {
        $taxonomy = $query->get('taxonomy');
        $term_slug = $query->get('term');
        
        if ($taxonomy && $term_slug) {
            if (isset($sticky_config['specific_taxonomies'][$taxonomy]) 
                && in_array($term_slug, $sticky_config['specific_taxonomies'][$taxonomy])) {
                $is_allowed = true;
                $page_type = 'custom taxonomy (' . $taxonomy . ': ' . $term_slug . ')';
            }
        }
    }

    // 6. 作者归档页
    if ($query->is_author() && $sticky_config['enable_author']) {
        $is_allowed = true;
        $page_type = 'author';
    }

    // 7. 日期归档页
    if ($query->is_date() && $sticky_config['enable_date']) {
        $is_allowed = true;
        $page_type = 'date';
    }

    // 8. 搜索结果页
    if ($query->is_search() && $sticky_config['enable_search']) {
        $is_allowed = true;
        $page_type = 'search';
    }

    // 9. 单篇文章页
    if ($query->is_single() && !$query->is_page() && $sticky_config['enable_all_singles']) {
        $is_allowed = true;
        $page_type = 'single post';
    }

    // 10. 单个 Page 页面
    if ($query->is_page() && $sticky_config['enable_all_pages']) {
        $is_allowed = true;
        $page_type = 'page';
    }

    // [NEW] 11. 自定义查询(Loop Carousel 等)
    // 如果是自定义查询,检查是否有分类参数
    if (!$query->is_main_query() && $sticky_config['enable_custom_queries']) {
        // 检查查询参数中的分类
        $cat = $query->get('cat');
        $category_name = $query->get('category_name');
        
        if ($cat || $category_name) {
            $term = null;
            if ($category_name) {
                $term = get_term_by('slug', $category_name, 'category');
            } elseif ($cat) {
                $term = get_term($cat, 'category');
            }
            
            if ($term && !is_wp_error($term)) {
                $target_cat_id = $term->term_id;
                
                // 检查是否在允许的分类树内
                if (!empty($sticky_config['specific_categories'])) {
                    if (in_array($term->slug, $sticky_config['specific_categories'])) {
                        $is_allowed = true;
                        $page_type = 'custom query (category: ' . $term->slug . ')';
                    } else {
                        $ancestors = get_ancestors($target_cat_id, 'category');
                        foreach ($ancestors as $ancestor_id) {
                            $ancestor_term = get_term($ancestor_id, 'category');
                            if ($ancestor_term && in_array($ancestor_term->slug, $sticky_config['specific_categories'])) {
                                $is_allowed = true;
                                $page_type = 'custom query (child of: ' . $ancestor_term->slug . ')';
                                break;
                            }
                        }
                    }
                } elseif ($sticky_config['enable_all_categories']) {
                    $is_allowed = true;
                    $page_type = 'custom query (category)';
                }

                if ($is_allowed) {
                    $cat_ids_to_check[] = $target_cat_id;
                    $children = get_term_children($target_cat_id, 'category');
                    if (!is_wp_error($children) && !empty($children)) {
                        $cat_ids_to_check = array_merge($cat_ids_to_check, $children);
                    }
                }
            }
        }
        
        // 如果没有分类限定,但启用了所有归档或首页,也允许
        if (!$is_allowed && ($sticky_config['enable_all_archives'] || $sticky_config['enable_home'])) {
            $is_allowed = true;
            $page_type = 'custom query (general)';
        }
    }

    return [
        'is_allowed' => $is_allowed,
        'page_type' => $page_type,
        'cat_ids' => $cat_ids_to_check
    ];
}

// pre_get_posts 动作(设置条件和查询变量)
add_action('pre_get_posts', function ($query) use ($sticky_config) {
    // 检查是否应该处理这个查询
    if (!sticky_should_apply($query, $sticky_config)) return;

    // 检查页面类型是否允许
    $check_result = sticky_check_page_type($query, $sticky_config);
    if (!$check_result['is_allowed']) {
        sticky_debug_log('[Sticky Debug] Skipping: Page type not in allowed scope');
        return;
    }

    $page_type = $check_result['page_type'];
    $cat_ids_to_check = $check_result['cat_ids'];

    sticky_debug_log('[Sticky Debug] Sticky processing triggered for: ' . $page_type);

    // ========== 获取并过滤置顶文章 ==========
    $sticky_ids = get_option('sticky_posts');
    if (empty($sticky_ids)) {
        sticky_debug_log('[Sticky Debug] No sticky posts found');
        return;
    }
    sticky_debug_log('[Sticky Debug] All sticky IDs: ' . implode(',', $sticky_ids));

    // 过滤出属于当前查询的置顶文章
    $sticky_in_this_query = [];
    foreach ($sticky_ids as $post_id) {
        if (get_post_type($post_id) !== 'post') continue;

        // 如果有分类限定,检查文章是否属于该分类树
        if (!empty($cat_ids_to_check)) {
            $has_match = false;
            foreach ($cat_ids_to_check as $cat_id) {
                if (has_term($cat_id, 'category', $post_id)) {
                    $has_match = true;
                    break;
                }
            }
            if (!$has_match) continue;
        }

        $sticky_in_this_query[] = $post_id;
    }

    if (empty($sticky_in_this_query)) {
        sticky_debug_log('[Sticky Debug] No sticky posts in this query scope');
        return;
    }
    sticky_debug_log('[Sticky Debug] Sticky IDs for this query: ' . implode(',', $sticky_in_this_query));

    // ========== 分页处理 ==========
    $paged = max(1, (int) $query->get('paged'), (int) get_query_var('paged'));
    sticky_debug_log('[Sticky Debug] Current page: ' . $paged);

    if ($paged > 1) {
        // 非第一页:排除置顶文章
        $existing_exclude = $query->get('post__not_in');
        if (!is_array($existing_exclude)) {
            $existing_exclude = !empty($existing_exclude) ? [$existing_exclude] : [];
        }
        $query->set('post__not_in', array_merge($existing_exclude, $sticky_in_this_query));
        sticky_debug_log('[Sticky Debug] Non-first page: Excluded stickies');
        return;
    }

    // 第一页:设置查询变量,用于 posts_clauses 重排
    $query->set('sticky_posts_for_sort', $sticky_in_this_query);
    sticky_debug_log('[Sticky Debug] First page: Set sticky_posts_for_sort for reordering');
}, 1); // [NEW] 优先级改为 1,确保在 Elementor 查询之前执行

// posts_clauses 过滤器(修改 SQL ORDER BY 以强制置顶在前)
add_filter('posts_clauses', function ($clauses, $query) {
    global $sticky_debug_messages, $wpdb;

    $sticky_ids = $query->get('sticky_posts_for_sort');
    if (empty($sticky_ids)) return $clauses;

    sticky_debug_log('[Sticky Debug] Modifying ORDER BY for sticky IDs: ' . implode(',', $sticky_ids));

    // 构建 CASE 语句
    $case = 'CASE ';
    foreach ($sticky_ids as $index => $id) {
        $case .= "WHEN {$wpdb->posts}.ID = $id THEN $index ";
    }
    $case .= 'ELSE ' . (count($sticky_ids) + 1) . ' END';

    // 修改 ORDER BY
    $clauses['orderby'] = "$case ASC, " . $clauses['orderby'];
    sticky_debug_log('[Sticky Debug] Modified ORDER BY: ' . $clauses['orderby']);

    return $clauses;
}, 10000, 2);

// 给置顶文章自动加 CSS class:is-sticky-post
add_filter('post_class', function ($classes, $class, $post_id) {
    if (is_sticky($post_id)) {
        $classes[] = 'is-sticky-post';
    }
    return $classes;
}, 10, 3);

// 调试输出(仅管理员,且 debug_mode 开启时)
if ($sticky_config['debug_mode']) {
    add_action('wp_footer', function () {
        global $sticky_debug_messages;

        if (!current_user_can('manage_options')) return;

        echo '<pre style="background: #f0f0f0; padding: 10px; margin: 20px; border: 1px solid #ccc; font-size: 12px; white-space: pre-wrap; z-index: 9999; position: relative;">';
        echo "<strong>Sticky Debug Log:</strong>\n";
        if (!empty($sticky_debug_messages)) {
            echo implode("\n", $sticky_debug_messages);
        } else {
            echo "No debug messages - hooks may not have triggered.";
        }
        echo '</pre>';
    }, 999);
}

 

 

我们可以给它再加个简单的CSS,让访问一眼就知道哪些是“置顶”的。

 

.is-sticky-post {
    position: relative;
    border: 2px solid #ff6b6b;
}

.is-sticky-post::before {
    content: "";
    position: absolute;
    top: 10px;
    right: 10px;
    width: 28px;
    height: 28px;
    background-color: #ff6b6b;
    /* 图钉 SVG(旋转45度的钉子) */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M16 9V4h1c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1h1v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z'/%3E%3C/svg%3E");
    background-size: 18px 18px;
    background-position: center;
    background-repeat: no-repeat;
    border-radius: 50%;
    z-index: 10;
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
Share the Post:

相关文章

This Headline Grabs Visitors’ Attention

A short description introducing your business and the services to visitors.