single - {$ post_type} - {slug} .php para tipos de postagem personalizados

20

Minha parte favorita da hierarquia de modelos do Wordpress é a capacidade de criar rapidamente arquivos de modelo para páginas por lesma, sem ter que editar a página no Wordpress para selecionar um modelo.

Atualmente, podemos fazer isso:

page- {slug} .php

Mas eu gostaria de poder fazer isso:

single- {post_type} - {slug} .php

Para que, por exemplo, em um tipo de postagem chamado review, eu pudesse criar um modelo para uma postagem chamada "Minha Grande Revisão" emsingle-review-my-great-review.php

Alguém já configurou isso antes? single-{post_type}-{slug}.php

super verdadeiro
fonte
Nunca usei essa configuração antes, mas se for muito complicada, por que não criar um arquivo de modelo e associá-lo à revisão em questão.
Shane
O WP 3.4 é buscado automaticamente single-{post_type}-{slug}.php; portanto, a atualização para o WP 3.4 é outra opção.
2141212

Respostas:

19

A) A base no núcleo

Como você pode ver na explicação da Hierarquia de modelos do Codex , single-{$post_type}.phpjá é suportado.


B) Estendendo a hierarquia principal

Agora há alguns filtros e ganchos dentro /wp-includes/template-loader.php.

  • do_action('template_redirect');
  • apply_filters( 'template_include', $template )
  • AND: um filtro específico dentro get_query_template( $type, ... )chamado:"$type}_template"

B.1) Como funciona

  1. Dentro do arquivo de carregador de modelo, o modelo é carregado por uma consulta var / wp_query condicional: is_*().
  2. O condicional é acionado (no caso de um modelo "único"): is_single() && $template = get_single_template()
  3. Isso dispara get_query_template( $type, $templates ), então , onde $typeestásingle
  4. Então nós temos o "{$type}_template"filtro

C) A solução

Como queremos apenas estender a hierarquia com um modelo carregado antes do "single-{$object->post_type}.php"modelo real , interceptaremos a hierarquia e adicionaremos um novo modelo ao início da matriz de modelos.

// Extend the hierarchy
function add_posttype_slug_template( $templates )
{

    $object = get_queried_object();

    // New 
    $templates[] = "single-{$object->post_type}-{$object->post_name}.php";
    // Like in core
    $templates[] = "single-{$object->post_type}.php";
    $templates[] = "single.php";

    return locate_template( $templates );    
}
// Now we add the filter to the appropriate hook
function intercept_template_hierarchy()
{
    add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
}
add_action( 'template_redirect', 'intercept_template_hierarchy', 20 );

NOTA: (Se você quiser usar algo diferente da lesma de objetos padrão), será necessário ajustar de $slugacordo com sua estrutura de ligação permanente. Basta usar o que você precisa do mundo todo (object) $post.

Bilhetes Trac

Como a abordagem acima atualmente não é suportada (você só pode filtrar o caminho localizado absoluto dessa maneira), aqui está uma lista de tickets trac:

kaiser
fonte
Quero testar isso, mas parece que há algo faltando na sua linha add_filter no final.
supertrue
@supertrue Boa captura. :) Encontrou outra falta )dentro do filtro. Fixo. Talvez você queira trocar o traço com um sublinhado antes da lesma dentro do modelo. Apenas para deixar o sufixo se destacar melhor ao examinar os modelos.
Kaiser
Faz com que este erro através local: Aviso: array_unshift () [function.array-unshift]: O primeiro argumento deve ser uma disposição em [linha contendo array_unshift]
supertrue
Ok, mas outra coisa está interceptando os modelos principais. A função funciona bem e $templatesé uma matriz. Veja as principais funções nesta pasta (sem data de validade). Certifique-se de testar isso com uma instalação sem plug-ins e o tema padrão. Em seguida, ative um após o outro e veja se o erro ainda ocorre.
Kaiser #
Sim, depurei isso e recebo o caminho absoluto final do primeiro modelo encontrado de volta como string. Vou ter que conversar com algum desenvolvedor principal sobre isso, antes de alterar a resposta. Além disso: eu misturei algo: slugsó está disponível para termos e taxonomias. Você deve substituir $post->post_namepelo que se adapta à sua estrutura de link permanente. Atualmente, não há como fazer isso automaticamente em todos os casos, recuperando e substituindo o caminho, dependendo da sua estrutura permanente e das regras de reescrita. Espere outra atualização.
Kaiser #
4

Seguindo a imagem da Hierarquia de modelos , não vejo essa opção.

Então aqui está como eu iria sobre isso:

Solução 1 (melhor na minha opinião)

Crie um arquivo de modelo e associe-o à revisão

 <?php
 /*
 Template Name: My Great Review
 */
 ?>

Adicionando o arquivo php do modelo no diretório do tema, ele aparecerá como uma opção de modelo na página de edição da sua postagem.

Solução 2

Provavelmente isso poderia ser conseguido usando o template_redirectgancho.

No arquivo functions.php:

 function my_redirect()
 {
      global $post;

      if( get_post_type( $post ) == "my_cpt" && is_single() )
      {
           if( file_exists( get_template_directory() . '/single-my_cpt-' . $post->post_name . '.php' ) )
           {
                include( get_template_directory() . '/single-my_cpt-' . $post->post_name . '.php' );
                exit;
           }
      }
 }
 add_action( 'template_redirect', 'my_redirect' );

EDITAR

file_existsVerificação adicionada

Shane
fonte
Por que você está exit;aí?
Kaiser #
@kaiser Deve estar em qualquer tutorial que eu segui na época, se não for necessário, eu o removerei.
Shane
1
@ Kaiser: O exit()necessário para evitar o carregamento do modelo padrão.
scribu 3/02/12
A solução 1 funcionará apenas para páginas, não para postagens.
IXN 7/11
2

A resposta principal (de 4 anos atrás) não funciona mais, mas o codex do WordPress tem a solução aqui :

<?php
function add_posttype_slug_template( $single_template )
{
    $object = get_queried_object();
    $single_postType_postName_template = locate_template("single-{$object->post_type}-{$object->post_name}.php");
    if( file_exists( $single_postType_postName_template ) )
    {
        return $single_postType_postName_template;
    } else {
        return $single_template;
    }
}
add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
?>
skladany
fonte
1

Usar modelos de página

Outra abordagem para escalabilidade seria duplicar a funcionalidade suspensa do modelo de página no pagetipo de postagem para o seu tipo de postagem personalizado.

Código reutilizável

A duplicação de código não é uma boa prática. As horas extras podem causar inchaço grave em uma base de código, quando tornam muito difícil para um desenvolvedor gerenciar. Em vez de criar um modelo para cada lesma, você provavelmente precisará de um modelo um para muitos que possa ser reutilizado em vez de um para um pós-modelo.

O código

# Define your custom post type string
define('MY_CUSTOM_POST_TYPE', 'my-cpt');

/**
 * Register the meta box
 */
add_action('add_meta_boxes', 'page_templates_dropdown_metabox');
function page_templates_dropdown_metabox(){
    add_meta_box(
        MY_CUSTOM_POST_TYPE.'-page-template',
        __('Template', 'rainbow'),
        'render_page_template_dropdown_metabox',
        MY_CUSTOM_POST_TYPE,
        'side', #I prefer placement under the post actions meta box
        'low'
    );
}

/**
 * Render your metabox - This code is similar to what is rendered on the page post type
 * @return void
 */
function render_page_template_dropdown_metabox(){
    global $post;
    $template = get_post_meta($post->ID, '_wp_page_template', true);
    echo "
        <label class='screen-reader-text' for='page_template'>Page Template</label>
            <select name='_wp_page_template' id='page_template'>
            <option value='default'>Default Template</option>";
            page_template_dropdown($template);
    echo "</select>";
}

/**
 * Save the page template
 * @return void
 */
function save_page_template($post_id){

    # Skip the auto saves
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        return;
    elseif ( defined( 'DOING_AJAX' ) && DOING_AJAX )
        return;
    elseif ( defined( 'DOING_CRON' ) && DOING_CRON )
        return;

    # Only update the page template meta if we are on our specific post type
    elseif(MY_CUSTOM_POST_TYPE === $_POST['post_type'])
        update_post_meta($post_id, '_wp_page_template', esc_attr($_POST['_wp_page_template']));
}
add_action('save_post', 'save_page_template');


/**
 * Set the page template
 * @param string $template The determined template from the WordPress brain
 * @return string $template Full path to predefined or custom page template
 */
function set_page_template($template){
    global $post;
    if(MY_CUSTOM_POST_TYPE === $post->post_type){
        $custom_template = get_post_meta($post->ID, '_wp_page_template', true);
        if($custom_template)
            #since our dropdown only gives the basename, use the locate_template() function to easily find the full path
            return locate_template($custom_template);
    }
    return $template;
}
add_filter('single_template', 'set_page_template');

Essa é uma resposta tardia, mas achei que seria valiosa, já que ninguém na Web documentou essa abordagem até onde sei. Espero que isso ajude alguém.

Brian Fegter
fonte
1

No meu caso, tenho tipos de postagem personalizados de Álbum e Faixa vinculados por uma taxonomia de Álbum. Eu queria poder usar diferentes modelos únicos para as postagens de álbuns e faixas, dependendo da taxonomia deles.

Com base na resposta de Kaiser acima, escrevi este código. Isso funciona bem.
Nota. Eu não precisava do add_action ().

// Add an additional template option to the template hierarchy
add_filter( 'single_template', 'add_albumtrack_taxslug_template', 10, 1 );
function add_albumtrack_taxslug_template( $orig_template_path )
{
    // at this point, $orig_template_path is an absolute located path to the preferred single template.

    $object = get_queried_object();

    if ( ! (
        // specify another template option only for Album and Track post types.
        in_array( $object->post_type, array( 'gregory-cpt-album','gregory-cpt-track' )) &&
        // check that the Album taxonomy has been registered.
        taxonomy_exists( 'gregory-tax-album' ) &&
        // get the Album taxonomy term for the current post.
        $album_tax = wp_get_object_terms( $object->ID, 'gregory-tax-album' )
        ))
        return $orig_template_path;

    // assemble template name
    // assumption: only one Album taxonomy term per post. we use the first object in the array.
    $template = "single-{$object->post_type}-{$album_tax[0]->slug}.php";
    $template = locate_template( $template );
    return ( !empty( $template ) ? $template : $orig_template_path );
}

Agora posso criar modelos chamados single-gregory-cpt-track-tax-serendipity.php e single-gregory-cpt-album-tax-serendipity.php e o WP os usará automaticamente; 'serendipidade fiscal' é a lesma do primeiro termo de taxonomia do álbum.

para referência, o gancho de filtro 'single_template' é declarado em:
/wp-includes/theme.php:get_query_template()

Obrigado Kaiser pelo código de exemplo.

Cheers, Gregory

Gregory
fonte
Olá Greg - bem-vindo ao WPSE. Poste apenas as respostas como respostas às perguntas - não as perguntas de acompanhamento. Se você tem uma pergunta que não foi respondida por uma resposta existente e é muito grande para um comentário, por favor, abra outra questão :)
Stephen Harris
1
a questão de cordas / matriz foi removido :-)
Gregory
1
"Obrigado Kaiser pelo código de amostra." - De nada.
Kaiser #
Funciona para você? Em primeiro lugar, '$ template' não deve ser comentado no seu código ... e acho que, em vez de '$ album_tax [0] -> slug', deve haver '$ object-> post_name', não é?
gregmatys
corrigiu a linha do modelo $. obrigado. $ object-> post_name? não. isso retornaria a lesma da postagem, mas eu preciso da lesma do álbum à qual a postagem está vinculada.
Gregory
0

Atualização para o código Brians, descobri que quando a caixa suspensa não estava sendo usada, a opção do modelo "padrão" estava sendo salva em wp_page_template, o que fez com que tentasse encontrar um modelo chamado padrão. essa alteração apenas verifica a opção "padrão" ao salvar e exclui a meta de postagem (útil se você alterou a opção de modelo de volta ao padrão)

elseif (MY_CUSTOM_POST_TYPE === $ _POST ['post_type']) {

if (esc_attr ($ _ POST ['_ wp_page_template']) === "padrão"):
    delete_post_meta ($ post_id, '_wp_page_template');
outro :
    update_post_meta ($ post_id, '_wp_page_template', esc_attr ($ _ POST ['_ wp_page_template']));
fim se;
}
Marca
fonte