admin管理员组

文章数量:1336563

Today I got a client that wanted custom template for each page and section within. I proposed Laravel custom sh3t, but he wanted WordPress, as it seems more easy to main( not from my experience ).

.. So far so good. I'm a little confused there.

So I decided that my parent entity would be page, so I can apply different template to each page. Then I have a section within this page, which should be not hardcored. User should have the ability to choose section layout ( template ), and remove or reorder sections within the current page. And Finally I have posts as they are the smallest entity in the website. Posts would be rendered as columns like in bootstrap ( col-md-2, col-lg-6 ), etc..

I decided to create a custom post type to use it as a section, but then I read that post types cannot have template, only pages can have ones. This compromised my plan so far and spend 5 hours in researching a solution( no exit ). So I need another strategy for doing this. I need templates for two entities.

Can someone suggest a solution to this problem? ( I will buy you a beer! )

EDIT:

To create my own custom post type, I use plugin in wordpress called 'Custom Post Type UI', of course there's another way, by pasting a short code snippet into your functions.php file, but I'm not covering this here.

Today I got a client that wanted custom template for each page and section within. I proposed Laravel custom sh3t, but he wanted WordPress, as it seems more easy to main( not from my experience ).

.. So far so good. I'm a little confused there.

So I decided that my parent entity would be page, so I can apply different template to each page. Then I have a section within this page, which should be not hardcored. User should have the ability to choose section layout ( template ), and remove or reorder sections within the current page. And Finally I have posts as they are the smallest entity in the website. Posts would be rendered as columns like in bootstrap ( col-md-2, col-lg-6 ), etc..

I decided to create a custom post type to use it as a section, but then I read that post types cannot have template, only pages can have ones. This compromised my plan so far and spend 5 hours in researching a solution( no exit ). So I need another strategy for doing this. I need templates for two entities.

Can someone suggest a solution to this problem? ( I will buy you a beer! )

EDIT:

To create my own custom post type, I use plugin in wordpress called 'Custom Post Type UI', of course there's another way, by pasting a short code snippet into your functions.php file, but I'm not covering this here.

Share Improve this question edited Oct 5, 2015 at 19:47 Dimitrov asked Oct 5, 2015 at 17:14 DimitrovDimitrov 351 gold badge1 silver badge5 bronze badges
Add a comment  | 

4 Answers 4

Reset to default 5

Typically I wouldn't follow such a strong answer like the one @Milo provided with a post like this. Since I already had this code written for another project I thought I'd share.

The code below does everything @Milo summarized in his answer and I've ported this across projects before with great success.

Here's the cheat sheet for what's going on:

1) Hook into the 'add_meta_boxes' action to render a new custom meta box on the edit screen (except on the native 'page' post type).

2) Hook into the 'admin_menu' action to remove the existing 'Page Attributes' meta box (except on the native 'page' post type).

3) Build out the custom meta box to replace the functionality of the native 'Page Attributes' meta box. This includes fields for defining the PARENT, TEMPLATE and ORDER of any custom post type you've initialized.

4) Hook into the 'save_post' action to save your template selection in the post meta.

5) Hook into the 'single_template' filter to load your custom template instead of the default WordPress template.

Here's is the fully functional copy/paste for you:

/** Custom Post Type Template Selector **/
function cpt_add_meta_boxes() {
    $post_types = get_post_types();
    foreach( $post_types as $ptype ) {
        if ( $ptype !== 'page') {
            add_meta_box( 'cpt-selector', 'Attributes', 'cpt_meta_box', $ptype, 'side', 'core' );
        }
    }
}
add_action( 'add_meta_boxes', 'cpt_add_meta_boxes' );

function cpt_remove_meta_boxes() {
    $post_types = get_post_types();
    foreach( $post_types as $ptype ) {
        if ( $ptype !== 'page') {
            remove_meta_box( 'pageparentdiv', $ptype, 'normal' );
        }
    }
}
add_action( 'admin_menu' , 'cpt_remove_meta_boxes' );

function cpt_meta_box( $post ) {
    $post_meta = get_post_meta( $post->ID );
    $templates = wp_get_theme()->get_page_templates();

    $post_type_object = get_post_type_object($post->post_type);
    if ( $post_type_object->hierarchical ) {
        $dropdown_args = array(
            'post_type'        => $post->post_type,
            'exclude_tree'     => $post->ID,
            'selected'         => $post->post_parent,
            'name'             => 'parent_id',
            'show_option_none' => __('(no parent)'),
            'sort_column'      => 'menu_order, post_title',
            'echo'             => 0,
        );

        $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post );
        $pages = wp_dropdown_pages( $dropdown_args );

        if ( $pages ) { 
            echo "<p><strong>Parent</strong></p>";
            echo "<label class=\"screen-reader-text\" for=\"parent_id\">Parent</label>";
            echo $pages;
        }
    }

    // Template Selector
    echo "<p><strong>Template</strong></p>";
    echo "<select id=\"cpt-selector\" name=\"_wp_page_template\"><option value=\"default\">Default Template</option>";
    foreach ( $templates as $template_filename => $template_name ) {
        if ( $post->post_type == strstr( $template_filename, '-', true) ) {
            if ( isset($post_meta['_wp_page_template'][0]) && ($post_meta['_wp_page_template'][0] == $template_filename) ) {
                echo "<option value=\"$template_filename\" selected=\"selected\">$template_name</option>";
            } else {
                echo "<option value=\"$template_filename\">$template_name</option>";
            }
        }
    }
    echo "</select>";

    // Page order
    echo "<p><strong>Order</strong></p>";
    echo "<p><label class=\"screen-reader-text\" for=\"menu_order\">Order</label><input name=\"menu_order\" type=\"text\" size=\"4\" id=\"menu_order\" value=\"". esc_attr($post->menu_order) . "\" /></p>";
}

function save_cpt_template_meta_data( $post_id ) {

    if ( isset( $_REQUEST['_wp_page_template'] ) ) {
        update_post_meta( $post_id, '_wp_page_template', $_REQUEST['_wp_page_template'] );
    }
}
add_action( 'save_post' , 'save_cpt_template_meta_data' );

function custom_single_template($template) {
    global $post;

    $post_meta = ( $post ) ? get_post_meta( $post->ID ) : null;
    if ( isset($post_meta['_wp_page_template'][0]) && ( $post_meta['_wp_page_template'][0] != 'default' ) ) {
        $template = get_template_directory() . '/' . $post_meta['_wp_page_template'][0];
    }

    return $template;
}
add_filter( 'single_template', 'custom_single_template' );
/** END Custom Post Type Template Selector **/

The only assumption I've made here is that your templates follow a naming convention best practice of:

posttype-templatename.php

As an example you could define some custom templates for an "Event" custom post type using the following naming convention within your theme:

event-standard.php
event-allday.php
event-recurring.php

This code is smart enough to only allow "event" templates to be applied to the Event post type. In other words, a template called "section-video.php" template would never be visible to the Event post type. That template would instead appear as an option on the "Section" post type.

To remove this feature you simply need to remove the conditional logic from the code above:

if ( $post->post_type == strstr( $template_filename, '-', true) ) {  }

As of WordPress 4.7, support for multiple templates has been given to Custom Post Types.

In order to make a template available for your Custom Post Type, you add this header to meta portion of the template file:

Template Post Type: post, foo, bar 

For example, we'll assume your Custom Post Type is labelled "my_events", and you'd like to make a template called "Fullwidth" available for both Pages and your Custom Post Type.

This:

/**
 * Template Name: Fullwidth
 * 
 * Template Description...
 **/

Becomes This:

/**
 * Template Name: Fullwidth
 * Template Post Type: page, my_events
 * 
 * Template Description...
 **/

More Info: Post Type Templates in 4.7 from WordPress Core

I know this is an old question, but I wanted to add my answer as to what helped me apply templates to custom post types.

I made my custom post type manually (with my own plugin) rather than using the Custom Post Type UI plugin.

The code of the plugin looks like this:

<?php
/**
 * Plugin Name: [Your post type plugin]
 */

defined('ABSPATH') or die(''); // Prevents access to the file through its URL in the browser

function yourPostTypeFunction() {
    register_post_type('your-post-type', array(
            'labels'=>array(
                'name'=>__('Post type pages'),
                'singular_name'=>__('Post type page')
            ),
            'description'=>'Your description',
            'public'=>true,
            'hierarchical'=>true, // Allows parent–child relationships
            'show_in_rest'=>true,
            'supports'=>array( // Features the post type should support
                'title',
                'editor',
                'thumbnail',
                'revisions',
                'page-attributes' /* Necessary to show the metabox for setting parent–child
relationships if you are not using the 'Template Post Type' header field method */
            ),
            'has_archive'=>true,
        )
    );
}
add_action('init', 'yourPostTypeFunction');

function CPTFlushPermalinks() {
    yourPostTypeFunction();
    flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'CPTFlushPermalinks');

?>

See here for details about all the options for register_post_type() (and why there's a flush_rewrite_rules() at the end). Many are set as defaults that you don't need to add.

The important thing for me was that 'hierarchical' was set to true and 'supports' included 'page-attributes'.

After this, I created my template file. The code looks something like this:

<?php
/*
 * Template Name: [Your template]
 */

// ... page content (mostly copied from page.php)

?>

Notice how I omitted the post-WordPress-4.7 "Template Post Type" field? That's because this field is for allowing the visibility of a template in the 'Page Attributes' metabox to specific post types. The post types listed will be able to choose the template in their Page Attributes metabox. (If you do this, you won't need 'page-attributes' support in your post type – the metabox will be created automatically.) Without it, only posts of the 'page' type can choose different templates in their 'Page Attributes' metabox.

This is not what I wanted, as I wanted my template to be immediately applied to all pages with the post type. This is achieved by naming the template "single-[post type name].php".

There are other naming schemes you can use for other purposes, listed here.

This will work for single pages of the post type. I don't know about subsections, like the original question asked; there, you would probably need conditional statements in your other templates, such as "if ('your-post-type' === get_post_type()) { ...".

You can freely rename the custom template through its "Template Name" field without affecting any pages. If you want to rename the file, you can do so, but you must then go into your database, search for the filename and rename the instances to the new name.

Also, the Post Type Switcher plugin is helpful for switching posts between post types, including in bulk. It worked seamlessly for me with no bugs.

dswebsme's answer was an appropriate answer from before WordPress allowed non-'pages' to choose custom templates in the 'Page Attributes' box.

Custom post types can have selectable templates, you'll just have to implement them yourself. The way WordPress does it internally with the page post type is by saving a template slug into post meta data, then checking if a value exists there when the template is loaded on the front end according to the hierarchy.

The basic process would be to add a meta box to your custom post type to let users select a template (maybe using get_page_templates to build a list).

The second step is to add a filter to single_template to load the selected template when that object is viewed on the front end.

If you look inside the core files wp-includes/template.php and wp-includes/post-template.php, you can see the code WordPress uses (and where the filter is applied) and adapt it to your needs. get_queried_object will give you the object's post_type within the filter, as well as the ID to enable you to fetch the post's meta data.

EDIT -

Here is an example filter for the post post type that loads whatever is in the meta key my_template (like whatever.php). You can test it by creating a new post and entering a filename under that key using the native Custom Fields meta box. You can modify this for your custom type (change 'post'), and whatever scheme you're using to store and name files.

function wpd_post_type_template( $template ){
    $object = get_queried_object();
    if( ! empty( $object->post_type )
        && 'post' == $object->post_type
        && $slug = get_post_meta( $object->ID, 'my_template', true ) ){
        if( $custom_template = locate_template( $slug, false ) ){
            $template = $custom_template;
        }
    }
    return $template;
}
add_filter( 'single_template', 'wpd_post_type_template' ) ;

本文标签: Apply template to custom post type