如何在 WordPress 分类中对帖子进行排序
已发表: 2020-12-18到目前为止,Nelio 已经在 WordPress.org 存储库中发布了几个插件。 其中一些是完全免费的,并为常见问题提供优雅的解决方案,例如 Nelio Maps,它允许您在使用 Gutenberg 创建的页面或帖子上插入 Google 地图,以及 Nelio Compare Images,它允许您插入顾名思义,一个块用于并排比较两个图像。 我们还发布了几个帮助我们支付账单的高级插件:Nelio Content 和 Nelio A / B 测试。
如果您查看我们的网站,您会发现我们的两个高级插件占据了显眼的位置,因为它们都有一些登陆和定价页面,并且它们都有相关的知识库,用户可以在其中找到问题的答案。 今天我们将讨论我们如何为 Nelio A/B 测试和 Nelio Content 创建文档页面,以及我们必须做些什么来按照我们的意愿对问题进行排序。
如何在 WordPress 中创建自定义帖子类型
首先,让我们创建一个自定义帖子类型来跟踪我们的知识库将包含的所有问题。 您可以在以下屏幕截图中看到结果,其中有一个 Nelio A/B 测试知识库的这种新帖子类型的实例:

要创建自定义帖子类型,您只需使用 WordPress 的register_post_type函数,如下所示:
function nelio_add_help_type() { $labels = array( 'name' => __( 'Questions', 'nelio-help' ), 'singular_name' => __( 'Question', 'nelio-help' ), 'menu_name' => __( 'Testing's Help', 'nelio-help' ), 'all_items' => __( 'All Questions', 'nelio_help' ), 'add_new_item' => __( 'Add New Question', 'nelio_help' ), 'edit_item' => __( 'Edit Question', 'nelio_help' ), 'view_item' => __( 'View Question', 'nelio_help' ), ); register_post_type( 'nab_help', array( 'capability_type' => 'post', 'labels' => $labels, 'map_meta_cap' => true, 'menu_icon' => 'dashicons-welcome-learn-more', 'public' => true, 'show_in_rest' => true, 'supports' => [ 'title', 'editor', 'author' ], ) ); } add_action( 'init', 'nelio_add_help_type' );瞧! 您可能已经知道有像 Advanced Custom Fields 这样的插件可以帮助您创建和自定义自定义帖子类型的界面,但我不一定推荐它来实现这样一个简单的示例。
顺便说一句,如果您想知道这个片段应该放在哪里,不要错过我们关于如何使用自定义插件自定义 WordPress 的教程。
如何创建新的分类来组织内容
我们的知识库按“主题”和“关键字”组织,如以下屏幕截图所示:

主题和关键字相当于您在 WordPress 帖子中默认拥有的类别和标签。 让我们只关注前者,因为后者完全相同。
正如您已经想象的那样,主题是我们为自定义内容类型创建的自定义分类法。 要将新分类法添加到 WordPress 帖子类型,您只需使用register_taxonomy函数,如下所示:
function nelio_add_help_taxonomy() { $labels = array( 'name' => __( 'Topics', 'nelio-help' ), 'singular_name' => __( 'Topic', 'nelio-help' ), 'menu_name' => __( 'Topics', 'nelio-help' ), 'all_items' => __( 'All Topics', 'nelio_help' ), /* ... */ ); register_taxonomy( 'nab_topic', [ 'nab_help' ], array( 'hierarchical' => true, 'label' => __( 'Topic', 'nelio-help' ), 'query_var' => true, 'show_admin_column' => false, 'show_ui' => true, 'show_in_rest' => true, ) ); } add_action( 'init', 'nelio_add_help_taxonomy' ); 该功能使用起来非常简单。 首先,您命名分类法(在我们的示例中为nab_topic )。 然后您指定与之相关的帖子类型(我们只有一个: nab_help )。 最后,添加更多参数来自定义分类。 结果是一种新的分类法,用于创建我们需要对知识库中的问题进行分类的具体主题:

如何对分类中的帖子进行排序
现在我们有了一个自定义分类法,我们可以向我们创建的每个主题添加问题,我们如何在特定主题中对这些问题进行排序? 好吧,恐怕我们将不得不编写一些代码......
看看数据库
WordPress 及其与您的帖子的关系存储在四个不同的数据库表中:
-
wp_terms包括您在特定分类中创建的所有术语。 例如,在我们的主题分类中,我们有诸如General Questions 、 Compatibility和Payments and Billing 之类的术语。 -
wp_termmeta帮助我们存储与每个术语相关的附加数据。 它与wp_postmeta非常相似。 -
wp_term_taxonomy将wp_terms中的每个术语与其定义分类相关联。 因此,例如,此表跟踪一般问题术语实际上是nab_topic分类法的一个实例这一事实。 -
wp_term_relationships将每个术语(term_taxonomy_id)与其“包含”的帖子(object_id)相关联。 例如,这张表告诉我们关于什么是 Nelio A/B 测试的问题是一个一般问题(这就是为什么在浏览主题时会出现该问题)。
WordPress 2.5 在wp_term_relationships表中添加了一个新的数字字段term_order ,它允许我们指定某个元素在分类中占据的位置。 这听起来像是我们正在寻找的候选人,以便在我们的主题中对我们的问题进行排序......但有一个问题:据我所知,WordPress 没有提供标准机制来(1)定义term_order字段的值和( 2) 使用它对某个分类中的帖子进行实际排序。 让我们解决这个问题!
如何使用term_order字段对分类中包含的帖子进行排序
假设我们能够以某种方式设置所需的term_order值,如以下屏幕截图所示:

默认情况下,WordPress 会忽略此字段,并且帖子不会在前端进行排序。 这就是为什么上面屏幕截图中显示的三个问题没有正确排序的原因。
幸运的是,我们可以轻松地告诉 WordPress 使用该字段对帖子进行排序。 只需使用posts_orderby过滤器来调整检索属于某个术语的帖子的查询,以便它包含排序请求:
add_filter( 'posts_orderby', 'nelio_sort_questions_in_topic', 99, 2 ); function nelio_sort_questions_in_topic( $orderby, $query ) { if ( ! nelio_is_topic_tax_query( $query ) ) return; global $wpdb; return "{$wpdb->term_relationships}.term_order ASC"; } function nelio_is_topic_tax_query( $query ) { if ( empty( $query->tax_query ) ) return; if ( empty( $query->tax_query->queries ) ) return; return in_array( $query->tax_query->queries[0]['taxonomy'], [ 'nab_topic' ], true ); } 前面的代码片段所做的只是检查我们是否正在查询某个分类法(在我们的例子中是nab_topic ),并且当我们这样做时,我们在查询中添加一个ORDER BY子句,以便最终结果按$wpdb->term_relationships表中的term_order属性:

实现用户界面以对分类的内容进行排序
最后,我们只剩下一件事要做了。 在上一节中,我们假设我们能够以某种方式在term_order字段中设置正确的值。 但是怎么做? 是否有屏幕或选项以用户友好的方式设置此值? 据我所知,没有。 但是我们可以创建一个。

在我看来,这个问题的最佳解决方案是拥有一个 UI,允许我选择我想要排序的分类,然后拖放所有问题以建立它们的顺序。 像这样,去:

为了达到预期的效果,我们必须执行以下步骤:
- 在 WordPress 仪表板上注册一个新页面
- 实现将呈现分类选择器和所选分类中的帖子的功能
- 添加一个很小的 JavaScript 片段,以便 UI 响应式
- 完成帖子排序后,创建一个新的回调来保存结果顺序
您可以使用新的 WordPress 开发堆栈使这个界面变得任意复杂。 但是今天我将概述一个快速而肮脏的解决方案来完成工作。
正如我所说,第一件事是注册我们将放置接口的页面。 我们可以在admin_menu操作期间使用add_submenu_page函数轻松完成此操作:
add_action( 'admin_menu', function() { add_submenu_page( 'edit.php?post_type=nab_help', 'Sort', 'Sort', 'edit_others_posts', 'nab-help-question-sorter', __NAMESPACE__ . '\render_question_sorter' ) } ); render_question_sorter方法也非常简单:
function render_question_sorter() { echo '<div class="wrap"><h1>Sort Questions</h1>'; $terms = get_terms( 'nab_topic' ); render_select( $terms ); foreach ( $terms as $term ) { render_questions_in_term( $term ); } render_script(); echo '</div>'; } 如您所见,我们只需检索nab_topic分类中的所有术语,然后我们依靠三个辅助函数来(1)呈现选择器,(2)呈现每个类别中的问题,(3)添加一个小脚本来制作所有内容动态的。
渲染选择器就像遍历每个术语并渲染一个option一样简单:
function render_select( $terms ) { echo '<select>'; foreach ( $terms as $term ) { printf( '<option value="%s">%s</option>', esc_attr( $term->slug ), esc_html( $term->name ) ); } echo '</select>'; }为了呈现我们在分类的每个术语中遇到的问题,我们向数据库发起一个查询并遍历所有这些问题:
function render_questions_in_term( $term ) { printf( '<div class="term-block" data-term->', esc_attr( $term->slug ), esc_attr( $term->term_id ) ); echo '<div class="sortable">' $query = new WP_Query( array( 'post_type' => 'nab_help', 'post_per_page' => -1, 'tax_query' => array( array( 'taxonomy' => 'nab_topic', 'field' => 'term_id', 'terms' => $term->term_id, 'orderby' => 'term_order', ), ), ) ); while ( $query->have_posts() ) { $query->the_post(); global $post; printf( '<div class="question" data-question->%s</div>', esc_attr( $post->ID ), esc_html( $post->post_title ) ); } echo '</div>'; printf( '<input type="button" data-term- value="%s" />', esc_attr( $term->term_id ), esc_attr( 'Save' ) ); echo '</div>'; } 请注意,此函数呈现一个使用当前$term的slug作为其 ID 的div ,它将问题分组在另一个div元素中,最后包含一个保存按钮。 当我们最终渲染连接我们的应用程序的脚本时,所有这些细节都会很有用。
一旦一切准备就绪,我们只需要添加一些 JavaScript 魔法。 特别是,这个快速而肮脏的 UI 需要两个脚本( jquery和jquery-ui )来实现拖放功能,然后是以下 JS 片段:
( function() { // MAKE QUESTIONS SORTABLE $( '.sortable' ).sortable(); // TERM SELECTOR const select = document.getElementById( 'topic' ); const termBlocks = [ ...document.querySelectorAll( '.term-block' ) ]; select.addEventListener( 'change', showSelectedTermBlock ); // Helper function function showSelectedTermBlock() { /* ... */ } // SAVE BUTTONS [ ...document.querySelectorAll( '.term-block input[type="button"]' ) ].forEach( addButtonLogic ); // Helper function function addButtonLogic( button ) { const termId = button.getAttribute( 'data-term-id' ); button.addEventListener( 'click', () => { button.disabled = true; const selector = `div[data-term-] .question`; const ids = [ ...document.querySelectorAll( selector ) ] .map( ( question ) => question.getAttribute( 'data-question-id' ); $.ajax( { url: ajaxurl, method: 'POST', data: { action: 'nelio_save_tax_sorting', objectIds: ids, termId, }, } ).always( () => button.disable = false ); } } } )() 乍一看,前面的代码片段可能看起来很复杂。 但我保证:不是。 首先,我们使用 jQuery UI 的sortable功能使我们的问题“可排序”。 然后,我们向我们的select组件添加一个侦听器,该侦听器使用辅助函数显示/隐藏适当的问题集。 最后,我们将click监听器添加到我们的保存按钮,这样当用户点击它们时,问题的排序顺序就会存储在数据库中。
如您所见,保存这个新订单是通过 AJAX 请求完成的,这意味着我们需要实现其 PHP 回调对应项:
add_action( 'wp_ajax_nelio_save_tax_sorting', __NAMESPACE__ . '\save_tax_sorting' ); function save_tax_sorting() { $term_id = isset( $_POST['termId'] ) ? absint( $_POST['termId'] : 0 ); if ( ! $term_id ) die(); $object_ids = isset( $_POST['objectIds'] ) ? $_POST['objectIds'] : []; if ( ! is_array( $object_ids ) || empty( $object_ids ) ) die(); $object_ids = array_values( array_map( 'absint', $object_ids ) ); global $wpdb; foreach ( $object_ids as $order => $object_id ) { $wpdb->update( $wpdb->term_relationships, array( 'term_order' => $order + 1 ), array( 'object_id' => $object_id, 'term_taxonomy_id' => $term_id, ), ); } die(); }就是这样! 很容易吧?
总之
WordPress 不提供默认机制来对特定分类中的帖子进行排序。 但没关系! 我们可以自己实现:在 WordPress 分类中对帖子进行排序所需要做的就是在term_relationships数据库表中设置term_order字段,然后使用posts_orderby过滤器扩展 WordPress,以便它使用该字段。
最后,我们概述了一个简单的 UI,可以通过拖放某个分类中包含的帖子轻松填充term_order字段。
我希望你喜欢这个教程,如果你喜欢,请与你的朋友和同事分享。 而且,与往常一样,如果您有任何问题,请将其留在下面的评论部分,我很乐意为您解答。
史蒂夫约翰逊在 Unsplash 上的特色图片。
