如何使用插件在主查询上添加自定义帖子类型的自定义内容模板部件

时间:2017-03-04 作者:cgogolin

我有一个自定义的帖子类型paper 在我的WordPress插件中定义,我通过以下方式在主查询中显示标准帖子:

function add_custom_post_types_to_query( $query ) {
    if ( is_home() && $query->is_main_query() )
        $query->set( \'post_type\', array( \'post\', \'paper\' ) );
    // As was pointed out by Ihor Vorotnov this return is not necessary. 
    // return $query;
}
add_action( \'pre_get_posts\', \'add_custom_post_types_to_query\' );
我还可以注册一个自定义single-paper.php 我的帖子类型的插件目录模板,通过:

function get_custom_post_type_single_template( $single_template ) {
     global $post;

     if ( $post->post_type == \'paper\' ) {
          $single_template = dirname( __FILE__ ) . \'/single-paper.php\';
     }
     return $single_template;
}
add_filter( \'single_template\', \'get_custom_post_type_single_template\' );
我想为content-____.php 我的主题使用的模板(onepress) 控制我的帖子在主查询中的显示方式。

这个index.php 主题甚至说:

<?php /* Start the Loop */ ?>
<?php while ( have_posts() ) : the_post(); ?>

<?php

    /*
     * Include the Post-Format-specific template for the content.
     * If you want to override this in a child theme, then include a file
     * called content-___.php (where ___ is the Post Format name) and that will be used instead.
     */
    get_template_part( \'template-parts/content\', get_post_format() );
    ?>

<?php endwhile; ?>
但我不想为此实现子主题,而是希望在插件中保留与自定义帖子类型相关的所有代码。

有没有办法添加一个过滤器,以便get_template_part( \'template-parts/content\', get_post_format() ); 使用我的插件提供的自定义模板paper 帖子和普通帖子主题的标准模板?

我已经尝试了上述代码的不同变体\'single_template\', 但无济于事。

5 个回复
最合适的回答,由SO网友:Fayaz 整理而成

不幸的是,背景get_template_part() 函数没有任何合适的过滤器来实现所需的功能。可以使用get_template_part_{$slug} 动作挂钩注入模板部分,但是,在不更改主题或子主题的情况下,仍将添加原始模板部分。因此,这样您将无法替换现有的模板部件。

但是,您可以通过以下选项之一实现所需的结果:

选项1:如果您只想更改paper 自定义post类型,然后您可以简单地使用WordPress生成的CSS类。WordPress中的任何标准主题都将生成$post_typetype-{$post_type} 帖子内容的CSS类。例如,自定义帖子类型paper 将会有paper &;type-paper CSS类和常规post 将会有posttype-post CSS类。此外,主页将具有home 正文中的CSS类。So目标paper 在主页中,您可以使用以下CSS规则:

body.home .type-paper {
    /* Custom CSS for paper entries in the home page */
}
选项2:修改CSS类:

如果默认CSS类对您来说不够,您还可以使用post_class 插件中的过滤器挂钩。像这样:

add_filter( \'post_class\', \'paper_post_class\' );
function paper_post_class( $class ) {
    if ( get_post_type() === \'paper\' && is_home() ) {

        // remove these classes
        $remove = array( \'css-class-to-remove\', \'another-class-to-remove\' );
        $class = array_diff( $class, $remove );

        // add these classes
        $add = array( \'custom-paper\', \'my-paper-class\' );
        $class = array_merge( $add, $class );
    }
    return $class;
}
这样,您就可以删除不需要的CSS类paper 条目并添加您想要的新CSS类paper 不修改主题文件的条目。然后使用这些CSS类更改paper 根据需要输入。

选项3:修改模板(&H);模板部分如果仅针对CSS类无法更改所需的样式,那么也可以从插件中更改模板部分。但是,由于替换模板零件添加了get_template_part() 使用挂钩是不可能的,您必须以某种方式更改模板,以便可以修改get_template_part() 从插件内部调用。

为此,您可以针对pre_get_posts 挂钩功能和使用template_include 过滤器挂钩可修改主页模板,如下所示:

function add_custom_post_types_to_query( $query ) {
    if ( is_home() && $query->is_main_query() ) {
        $query->set( \'post_type\', array( \'post\', \'paper\' ) );

        // now also change the home page template, but only when this condition matches
        add_filter( \'template_include\', \'wpse258844_custom_template\', 999 );
    }
}
add_action( \'pre_get_posts\', \'add_custom_post_types_to_query\' ); 
然后使用以下代码(根据需要进行修改):

// for better file management, use a separate "theme-{$theme_name_slug}" directory within your plugin directory
// so if the active theme name is "OnePress", the directory name will be "theme-onepress"
// This will save you a ton of headache if you change the theme,
// as different themes may use different function calls within the templates, which other themes may not have
define( \'wpse258844_TEMPLATE_DIR\', plugin_dir_path( __FILE__ ) . sprintf( \'theme-%s/\', sanitize_title( wp_get_theme() ) ) );

function wpse258844_custom_template( $template ) {
    // this file will replace your homepage template file
    $index_paper = wpse258844_TEMPLATE_DIR . \'custom-home.php\';

    // file_exists() check may need clearing stat cache if you change file name, delete the file etc.
    // Once you are done, comment out or remove clearstatcache(); in production
    clearstatcache();

    if ( file_exists( $index_paper ) ) {
        $template = $index_paper;
    }
    return $template;
}

function wpse258844_get_template_part( $slug, $name = null ) {
    if( get_post_type() === \'paper\' ) {

        // just to be consistant with get_template_part() function
        do_action( "get_template_part_{$slug}", $slug, $name );

        $located = \'\';
        $name = (string) $name;

        // file_exists() check may need clearing stat cache if you change file name, delete the file etc.
        // Once you are done, comment out or remove clearstatcache(); in production
        clearstatcache();

        if ( \'\' !== $name && file_exists( wpse258844_TEMPLATE_DIR . "{$slug}-{$name}.php" ) ) {
            $located = wpse258844_TEMPLATE_DIR . "{$slug}-{$name}.php";
        }
        else if ( file_exists( wpse258844_TEMPLATE_DIR . "{$slug}.php" ) ) {
            $located = wpse258844_TEMPLATE_DIR . "{$slug}.php";
        }

        if ( \'\' != $located ) {
            load_template( $located, false );
            return;
        }
    }

    get_template_part( $slug, $name );
}
一旦插件中包含了上述代码(请阅读代码中的注释):

在插件目录中创建一个新目录,以保存主题模板文件,例如。theme-onepress. 如果您想用不同的主题测试设计更改(我想这是所有这些混乱的主要目的),这将在将来对您有所帮助。

在新的theme-onepress 目录中,创建一个名为custom-home.php. 从主题复制主页模板的代码(可能是index.php, 或home.php 或者您的主题用于主页模板的任何内容)。

现在在custom-home.php 更改的所有调用get_template_partwpse258844_get_template_part. 无需更改参数,只需更改函数名。wpse258844_get_template_part() 上述代码中的函数与相同get_template_part() 如果在插件中找不到自定义模板部分,则返回默认行为theme-{$theme_name_slug} (例如。theme-onepress) 目录

最后,在插件的theme-{$theme_name_slug} 目录

例如,如果最初的调用是get_template_part( \'template-parts/content\', get_post_format() ) 你想替换content.php 模板部分,然后只需放置一个名为content.php 在插件中theme-onepress/template-parts 目录也就是说theme-onepress 目录的行为类似于模板部件的子主题,即简单的插入式替换。

SO网友:David Lee

为其创建自己的功能:

function get_template_part_custom($content_name) {
     global $post;
     //its paper type?
     if ($post->post_type == \'paper\') {
         //get template-parts/content-paper.php
         get_template_part( \'template-parts/content\', $content_name );
     }else{
         //fallback to the default
         get_template_part( \'template-parts/content\', get_post_format($post) );
     }
}
使用方法如下:

get_template_part_custom(\'paper\');
如果该职位属于paper 它将尝试加载一个名为content-paper.php 在名为template-parts 在根主题文件夹中,如果文件不存在,它将尝试加载content.php, 检查template-hierarchy, 我认为您不需要注册模板single-paper.php, WordPress会帮你拿的。

EDIT:

要从插件加载模板,请执行以下操作:

//load a custom file from the plugin
add_filter( \'template_include\', \'return_our_template\');

//Returns template file
function return_our_template( $template ) {

    // Post ID
    $post_id = get_the_ID();

    // For all other Types that arent ours
    if ( get_post_type( $post_id ) != \'paper\' ) {
        return $template;
    }

    // Else use our custom template
    if ( is_single() ) {
        return get_custom_template( \'single\' );
    }

}

//Get the custom template if is set
function get_custom_template( $template ) {

    // Get the slug
    $template_slug = rtrim( $template, \'.php\' );
    $template = $template_slug . \'.php\';

    // Check if a custom template exists in the theme folder, if not, load the plugin template file
    if ( $theme_file = locate_template( array( \'plugin_template/\' . $template ) ) ) {
        $file = $theme_file;
    }
    else {
        //here path to \'/single-paper.php\'
        $file = PATH_TO_YOUR_BASE_DIR . $template;
    }
    //create a new filter so the devs can filter this
    return apply_filters( \'filter_template_\' . $template, $file );
}

SO网友:Ihor Vorotnov

据我从源代码中看到的,你不能过滤get_template_part() 就像你平时做的那样。请参见this thread 对于一些解决方案。

SO网友:cgogolin

下面是另一个实现要求的“黑客”:

可以使用post format (不要与post type!) 为自定义帖子类型使用自定义模板。记住这句台词

get_template_part( \'template-parts/content\', get_post_format() );
index.php 在问题中描述。通过这种构造,大多数标准主题根据post format.

可以声称,给定自定义帖子类型的所有帖子paper 有一定的帖子格式,比如aside 通过调用

set_post_format( $post_id, \'aside\' )
在连接到\'save_post\' 通过添加以下内容

function paper_format_terms( $terms, $post_id, $tax ) {
    if ( \'post_format\' === $tax && empty( $terms ) && \'paper\' === get_post_type( $post_id ) ) {
        $aside = get_term_by( \'slug\', \'post-format-aside\', $tax );
        $terms = array( $aside );
    }
    return $terms;
}
add_filter( \'get_the_terms\', \'paper_format_terms\', 10, 3 );
\'get_the_terms\'

不能定义自定义的post格式,但通常至少有一种post格式不用于其他任何内容。“旁白”格式可能是一个不错的选择。

然后,每当遇到paper 发布模板的查找.../template-parts/content-aside.php. 剩下要做的就是在主题目录中安装一个合适的模板,这样在主题更新时它就不会被覆盖或删除(我们希望插件中包含所有代码!)。这可以通过在现场放置合适的模板来完成content-aside.php 在plugins目录中,连接到“upgrader\\u process\\u complete”,如下所示

function install_aside_template_on_upgrade( $upgrader_object, $options ) {
    if ($options[\'type\'] == \'theme\' ) {
        $content_aside = plugin_dir_path( __FILE__ ) . \'content-aside.php\';
        $template_parts_content_aside = get_stylesheet_directory() . \'/template-parts/content-aside.php\';

        copy($content_aside, $template_parts_content_aside);
    }
}
add_action( \'upgrader_process_complete\', \'install_aside_template_on_upgrade\',10, 2);
这样content-aside.php 在每次更新主题后复制到主题目录。

请记住,这是一个相当可怕的黑客,它可能会有不必要的副作用!尽管如此,我认为这对一些。。。

SO网友:cgogolin

虽然这并不是对发布的问题的确切答案,但有另一种非常干净和简单的方法来实现类似的目标,我怀疑,对于许多人来说,这将是最终出现在这个页面上的实际最佳解决方案。对我来说,在我问了这个问题之后,需求发生了轻微的变化,最终允许我使用这个解决方案。

在许多情况下,当主题的模板调用时,只需更改自定义帖子类型返回的内容即可,而不用使用自定义主题the_excerpt(). 这可以通过钩住\'the_excerpt\' 过滤如下:

function paper_get_the_excerpt ($content) {
    global $post;
    $post_id = $post->ID;
    if ( get_post_type($post_id) === \'paper\' ) {
        [do whatever you want with $content]
    }
    return $content;
}
add_filter( \'the_excerpt\', \'paper_get_the_excerpt\' );
漂亮、干净、简单,允许几乎与更改模板一样多的灵活性。