wordpress:如何为帖子添加层次结构

Tim*_*rov 5 wordpress post hierarchy

我正在 wordpress 平台上创建一个网站,我希望能够在其中发布我自己的书籍文本。所以我想要的是有一种层次结构,我可以在其中添加一个帖子,然后向其中添加子项(章节)。我找到了这个:

register_post_type( 'post', array(
        'labels' => array(
            'name_admin_bar' => _x( 'Post', 'add new on admin bar' ),
        ),
        'public'  => true,
        '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
        '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
        'capability_type' => 'post',
        'map_meta_cap' => true,
        'hierarchical' => false,
        'rewrite' => false,
        'query_var' => false,
        'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats' ),
    ) );
Run Code Online (Sandbox Code Playgroud)

并试图制作'hierarchical"=>true,但没有效果。任何人都可以帮忙吗?

小智 8

这是我的解决方法。这正是您想要的,能够为内置帖子类型帖子设置帖子父级。您可以通过向registred_post_type动作挂钩添加动作来实现此目的。只需将其添加到主题的functions.php 中即可。

add_action('registered_post_type', 'igy2411_make_posts_hierarchical', 10, 2 );

// Runs after each post type is registered
function igy2411_make_posts_hierarchical($post_type, $pto){

    // Return, if not post type posts
    if ($post_type != 'post') return;

    // access $wp_post_types global variable
    global $wp_post_types;

    // Set post type "post" to be hierarchical
    $wp_post_types['post']->hierarchical = 1;

    // Add page attributes to post backend
    // This adds the box to set up parent and menu order on edit posts.
    add_post_type_support( 'post', 'page-attributes' );

}
Run Code Online (Sandbox Code Playgroud)

使帖子分层可能有帮助的原因可能有很多。我的用例是客户想要将他们(已经存在的)帖子组织成问题,其中子帖子是一个问题(父帖子)的文章。

这可以通过将查询限制为仅显示没有父级的帖子来轻松实现,使用。

 'post_parent' => 0,
Run Code Online (Sandbox Code Playgroud)

在您的查询 $args.


小智 7

可湿性粉剂 4.9.*

上面的解决方法使友好 URL 变得疯狂。

我将层次结构添加到任何现有帖子类型的解决方案:

add_filter( 'register_post_type_args', 'add_hierarchy_support', 10, 2 );
function add_hierarchy_support( $args, $post_type ){

    if ($post_type === 'post') { // <-- enter desired post type here

        $args['hierarchical'] = true;
        $args['supports'] = array_merge($args['supports'], array ('page-attributes') );
    }

    return $args;
}
Run Code Online (Sandbox Code Playgroud)

在 /wp-admin/options-permalink.php 重新保存 wp 设置

  • 它可以工作,但是为什么它会将菜单项名称从“Post”更改为“Page”?现在我在侧边栏上看到两个“页面”菜单项。 (2认同)

amu*_*ell 7

更新

由于评论引发了新的用例和问题,我重写了这段代码,并在我自己的网站上使用它[在 5.8.2 中测试]。我已经提供了一个要点。您可以将其包含在您的functions.php中,或者将其放入插件中。

https://gist.github.com/amurrell/00d29a86fc1a773274bf049ef545b29f

此新更新利用 SQL(快速!)来解析 slug 和帖子 id 以确定永久链接和路由。它会生成完全匹配的帖子 id,即使您post_name对不同的帖子后代使用相同的帖子 id 。它真的又快又可靠!

本质上,最有趣的功能是get_post_from_uri($uri)

  • 我构建了一个自定义查询,它将为我们确定永久链接并找到完全匹配的帖子 ID。
  • 为什么要查询?WordPress 没有任何功能可以帮助确定帖子的完整血统,而不会让您陷入循环。
  • 但数据中存在关系!
  • 因此,确保我们获得良好友好的永久链接 URL 的完美方法是利用数据库和查询语言的强大功能。

让我们看看查询是如何工作的。这可能不是完美的 1-1 代码,因为我将其设为动态的,但概念是存在的:

例子:

我有以下帖子:

  • 气候[547]
  • 创新联盟[1395]
    • 气候[1808]
  • 采购 [518]
    • 城市销售周期 [1345]
      • 气候[1811]

在 SQL 中查看:

mysql> select id, post_name, post_parent from wp_posts where post_type = 'post' and id in (1811, 1808, 1345, 1395, 547, 518);
+------+-------------------------+-------------+
| id   | post_name               | post_parent |
+------+-------------------------+-------------+
|  518 | procurement             |           0 |
|  547 | climate                 |           0 |
| 1345 | city-sales-cycle        |         518 |
| 1395 | alliance-for-innovation |           0 |
| 1808 | climate                 |        1395 |
| 1811 | climate                 |        1345 |
+------+-------------------------+-------------+
Run Code Online (Sandbox Code Playgroud)

前网址:alliance-for-innovation/climate

完整的查询...

mysql> select * from
    -> (select TRIM(BOTH '/' FROM concat(
    ->   IFNULL(p3_slug,''),
    ->   '/',
    ->   IFNULL(p2_slug,''),
    ->   '/',
    ->   p1_slug
    -> )
    -> ) as slug,
    -> id
    -> from (
    -> select d2.*, p3.post_name as p3_slug, p3.post_parent as p3_parent from (
    -> select d1.*, p2.post_name as p2_slug, p2.post_parent as p2_parent from (
    -> select id, post_name as p1_slug, post_parent as p1_parent from wp_posts where post_type = 'post' and post_name = 'climate'
    -> ) as d1
    -> left join wp_posts p2 on p2.id = d1.p1_parent
    -> ) as d2
    -> left join wp_posts p3 on p3.id = d2.p2_parent) as d3
    ->
    -> ) as all_slugs
    -> where slug = 'alliance-for-innovation/climate';
+---------------------------------+------+
| slug                            | id   |
+---------------------------------+------+
| alliance-for-innovation/climate | 1808 |
+---------------------------------+------+
1 row in set (0.01 sec)
Run Code Online (Sandbox Code Playgroud)

我现在拥有帖子ID 或永久链接slug,我应该使用!

值得注意的是,我进入了 p3 级别,这是比 URL 所需的额外级别(分为两部分)。这是为了防止类似的事情alliance-for-innovation/climate/something匹配。

它是如何工作的?分解查询

有一个内部查询会查找 URL 的最后一部分,即基本名称。在这种情况下,它会是climate

mysql> select id, post_name as p1_slug, post_parent as p1_parent from wp_posts where post_type = 'post' and post_name = 'climate';
+------+---------+-----------+
| id   | p1_slug | p1_parent |
+------+---------+-----------+
|  547 | climate |         0 |
| 1808 | climate |      1395 |
| 1811 | climate |      1345 |
+------+---------+-----------+
Run Code Online (Sandbox Code Playgroud)

/以编程方式,我们不断在与 url 中的数量直接相关的查询周围添加抽象,以便我们可以找到有关 post_parent 的 slug 的更多信息。

mysql> select d1.*, p2.post_name as p2_slug, p2.post_parent as p2_parent from (
    -> select id, post_name as p1_slug, post_parent as p1_parent from wp_posts where post_type = 'post' and post_name = 'climate'
    -> ) as d1
    -> left join wp_posts p2 on p2.id = d1.p1_parent;
+------+---------+-----------+-------------------------+-----------+
| id   | p1_slug | p1_parent | p2_slug                 | p2_parent |
+------+---------+-----------+-------------------------+-----------+
|  547 | climate |         0 | NULL                    |      NULL |
| 1808 | climate |      1395 | alliance-for-innovation |         0 |
| 1811 | climate |      1345 | city-sales-cycle        |       518 |
+------+---------+-----------+-------------------------+-----------+
Run Code Online (Sandbox Code Playgroud)

在我们抽象足够多次之后,我们可以选择 concat 作为 slug,例如: p1_slug + '/' + p2_slug

mysql> select TRIM(BOTH '/' FROM concat(
    ->   IFNULL(p3_slug,''),
    ->   '/',
    ->   IFNULL(p2_slug,''),
    ->   '/',
    ->   p1_slug
    -> )
    -> ) as slug,
    -> id
    -> from (
    -> select d2.*, p3.post_name as p3_slug, p3.post_parent as p3_parent from (
    -> select d1.*, p2.post_name as p2_slug, p2.post_parent as p2_parent from (
    -> select id, post_name as p1_slug, post_parent as p1_parent from wp_posts where post_type = 'post' and post_name = 'climate'
    -> ) as d1
    -> left join wp_posts p2 on p2.id = d1.p1_parent
    -> ) as d2
    -> left join wp_posts p3 on p3.id = d2.p2_parent) as d3
    ->
    -> ;
+--------------------------------------+------+
| slug                                 | id   |
+--------------------------------------+------+
| climate                              |  547 |
| alliance-for-innovation/climate      | 1808 |
| procurement/city-sales-cycle/climate | 1811 |
+--------------------------------------+------+
Run Code Online (Sandbox Code Playgroud)

where最后一步是为原始 url添加 a : alliance-for-innovation/climate。这就是您在我们首先检查的完整查询示例中看到的内容!

让我们看看其他人是怎么走的:

# climate

+---------+-----+
| slug    | id  |
+---------+-----+
| climate | 547 |
+---------+-----+
Run Code Online (Sandbox Code Playgroud)
# procurement/city-sales-cycle/climate

+--------------------------------------+------+
| slug                                 | id   |
+--------------------------------------+------+
| procurement/city-sales-cycle/climate | 1811 |
+--------------------------------------+------+
Run Code Online (Sandbox Code Playgroud)

我喜欢此更新的另一件事是我记得:

转义climate我们在查询中使用的 URL 的 , 或基名,因为这在技术上是用户输入的(通过 url)

$wpdb->_real_escape($basename));
Run Code Online (Sandbox Code Playgroud)

那么这个查询构建函数是如何动态的呢?

我们使用 PHP 数组、循环等来构建一个将作为查询的字符串,这样我们就不必使用 PHP 来处理有关数据本身的逻辑。

这是显示动态抽象的片段 - 例如。要抓取多少个 p1_slug、p2_slug、p3_slug。

// We will do 1 more depth level than we need to confirm the slug would not lazy match
  // This for loop builds inside out.
  for ($c = 1; $c < $depth + 2; $c++) {
    $d = $c;
    $p = $c + 1;

    $pre = "select d${d}.*, p${p}.post_name as p${p}_slug, p${p}.post_parent as p${p}_parent from (";
    $suf = ") as d${d} left join $wpdb->posts p${p} on p${p}.id = d${d}.p${c}_parent";

    $sql = $pre . $sql . $suf;

    $concats[] = sprintf("IFNULL(p${p}_slug,'')");
  }
Run Code Online (Sandbox Code Playgroud)

上一个答案:

我来到这里是为了实现:

  1. 为post_type帖子添加页面属性以添加父帖子
  2. 能够向 post_type 帖子添加页面模板
  3. 能够获得 post_type 帖子的分层永久链接结构

我能够使用已接受的答案来完成 1 和 2,但不能完成 3。

注意:要完全让 2 工作,您需要在页面模板的模板注释中指定 post_type,如下所示:

<?php
/*
Template Name: Your Post Template Name
Template Post Type: post
*/
Run Code Online (Sandbox Code Playgroud)

对于 3,我发现一个插件毁了我的 post_type 页面,其中有很多非常糟糕的、未维护的代码。

因此,我借鉴了这个答案,编写了一个解决方案来完成这一切:

(用4.9.8测试)

<?php

add_action('registered_post_type', 'make_posts_hierarchical', 10, 2 );

// Runs after each post type is registered
function make_posts_hierarchical($post_type, $pto){

    // Return, if not post type posts
    if ($post_type != 'post') return;

    // access $wp_post_types global variable
    global $wp_post_types;

    // Set post type "post" to be hierarchical
    $wp_post_types['post']->hierarchical = 1;

    // Add page attributes to post backend
    // This adds the box to set up parent and menu order on edit posts.
    add_post_type_support( 'post', 'page-attributes' );

}

/**
 * Get parent post slug
 * 
 * Helpful function to get the post name of a posts parent
 */
function get_parent_post_slug($post) {
  if (!is_object($post) || !$post->post_parent) {
    return false;
  }

  return get_post($post->post_parent)->post_name;
}

/**
 * 
 * Edit View of Permalink
 * 
 * This affects editing permalinks, and $permalink is an array [template, replacement]
 * where replacement is the post_name and template has %postname% in it.
 * 
 **/
add_filter('get_sample_permalink', function($permalink, $post_id, $title, $name, $post) {
  if ($post->post_type != 'post' || !$post->post_parent) {
    return $permalink;
  }

  // Deconstruct the permalink parts
  $template_permalink = current($permalink);
  $replacement_permalink = next($permalink);

  // Find string
  $postname_string = '/%postname%/';

  // Get parent post
  $parent_slug = get_parent_post_slug($post);

  $altered_template_with_parent_slug = '/' . $parent_slug . $postname_string;
  $new_template = str_replace($postname_string, $altered_template_with_parent_slug, $template_permalink);

  $new_permalink = [$new_template, $replacement_permalink];

  return $new_permalink;
}, 99, 5);

/**
 * Alter the link to the post
 * 
 * This affects get_permalink, the_permalink etc. 
 * This will be the target of the edit permalink link too.
 * 
 * Note: only fires on "post" post types.
 */
add_filter('post_link', function($post_link, $post, $leavename){

  if ($post->post_type != 'post' || !$post->post_parent) {
    return $post_link;
  }
  
  $parent_slug = get_parent_post_slug($post);
  $new_post_link = str_replace($post->post_name, $parent_slug . '/' . $post->post_name, $post_link);

  return $new_post_link;
}, 99, 3);

/**
 * Before getting posts
 * 
 * Has to do with routing... adjusts the main query settings
 * 
 */
add_action('pre_get_posts', function($query){
  global $wpdb, $wp_query;

  $original_query = $query;
  $uri = $_SERVER['REQUEST_URI'];

  // Do not do this post check all the time
  if ( $query->is_main_query() && !is_admin()) {

    // get the post_name
    $basename = basename($uri);
    // find out if we have a post that matches this post_name
    $test_query = sprintf("select * from $wpdb->posts where post_type = '%s' and post_name = '%s';", 'post', $basename);
    $result = $wpdb->get_results($test_query);

    // if no match, return default query, or if there's no parent post, this is not necessary
    if (!($post = current($result)) || !$post->post_parent) {
      return $original_query;
    }

    // get the parent slug
    $parent_slug = get_parent_post_slug($post);
    // concat the parent slug with the post_name to get most of the url
    $hierarchal_slug = $parent_slug . '/' . $post->post_name;

    // if the concat of parent-slug/post-name is not in the uri, this is not the right post.
    if (!stristr($uri, $hierarchal_slug)) {
      return $original_query;
    }

    // pretty high confidence that we need to override the query.
    $query->query_vars['post_type'] = ['post'];
    $query->is_home     = false; 
    $query->is_page     = true;  
    $query->is_single   = true; 
    $query->queried_object_id = $post->ID;  
    $query->set('page_id', $post->ID);

    return $query;
  }


}, 1);
Run Code Online (Sandbox Code Playgroud)

您可以将其保存到文件中custom-posts-hierarchy.php并将其包含在主题的functions.php 文件中,或者您可以添加到顶部:

/*
Plugin Name: Custom Posts Hierarchy
Plugin URI:
Description: Add page attributes to posts and support hiearchichal
Author: Angela Murrell
Version:
Author URI: 
*/
Run Code Online (Sandbox Code Playgroud)

并将其放入您的插件文件夹中。祝你好运!