Um site de nicho é um site voltado a um conteúdo específico, com particularidades próprias do assunto que ele aborda. Nesse artigo você verá como criar, através da programação, um Tema de Nicho no WordPress.

Esse projeto é um bom exemplo que nem sempre adaptar o modelo de organização do conteúdo no WordPress, ou seja, aplicar o conteúdo em formato de páginas e posts e classificá-los somente com tags e categorias; é a melhor opção para seus trabalhos.

Será criado um Site de Receitas que tem como especificações:

  • Exibir as últimas receitas na home do site
  • Permitir que a quantidade de receitas da Home seja controlada via Dashboard
  • Possibilitar ao visitante navegar entre as receitas através da classificação das mesmas em tipos de pratos; ex.: massas, saladas, sobremesas, etc.
  • Classificar as receitas por dificuldade e valor calórico de cada uma
  • Manter um blog do site que trate sobre eventos, dicas e outras notícias do assunto em geral
Tema WordPress desenolvido para o site Receitas.com

Tema WordPress desenvolvido para o site Receitas.com

A integração do Template com os recursos do WordPress não precisa obedecer uma ordem específica de trabalho. Você, assim como cada desenvolvedor, pode criar um cronograma próprio para realizar esse serviço da maneira que melhor se enquadrar em seu perfil. Para o site de receitas inicialmente será trabalhada a parte administrativa do mesmo com a configuração da quantidade de posts exibir na tela inicial.

Option API

A tabela de opções possui dentro de si todas as configurações do WordPress como a personalização das telas administrativas do sistema, dados do site e de modo geral, todos as opções que permitem edição do administrador. Com intuito de prover um fácil acesso a essa tabela e também permitir a inclusão, alteração e exclusão de seus conteúdos existe a API de Opções.

Trata-se de alguns recursos que servem para tratar de opções. Entenda por opções valores genéricos de um site que possuem nome e valor editável; como por exemplo o nome do site, descrição do site e o email do administrador, são opções para o WordPress. As funções da API são:

Funções da API de opções possibilitam a completa manipulação das opções armazenadas em banco de dados

Funções da API de opções possibilitam a completa manipulação das opções armazenadas em banco de dados

Novamente, tenha cuidado ao especificar o nome de suas opções para que não ocorram conflitos com as opções do sistema. Adote a postura dos prefixos e será pouco provável a ocorrência de sobreposição de valores ou a exclusão e recuperação de valores inesperados.

Páginas Administrativas (Admin Pages)

De posse do conhecimento acerca das opções, você desenvolverá um ambiente próprio dentro do painel de controle do WordPress capaz de gerenciar a informação que o projeto demanda; a quantidade de posts a serem exibidos na Home do site.

add_action( 'admin_menu', 'pw_custom_menu' );

Com essa ação você poderá atribuir ao menu do painel novos itens ou remover e personalizar os itens existentes. No caso a intenção é de acrescentar um link para a tela personalizada de opções, para isso especifique na função referenciada pela ação a seguinte instrução:

function pw_custom_menu()
{
    add_menu_page( 'Página de opções', 'Opções do Tema', 'manage_options', 'custom-options', 'pw_custom_options_page' );
}

Ao utilizar add_menu_page você especifica a criação de um novo item de nível primário ao menu do Dashboard. Se preferir, ou se o projeto demandar mais de uma página de opções, você pode adicionar submenus com add_submenu_page e especificar o identificador do item de menu no qual o submenu será inserido.

Dashboard com opções personalizadas

Página de opções personalizada dentro do Dashboard

Feito isso, basta a você criar um formulário em HTML com o envio dos campos para o endereço da página administrativa personalizada; e no escopo da função responsável por apresentar o conteúdo desse página, especifique o tratamento que será dado aos valores recebidos pela submissão do form.

function pw_custom_options_page()
{
    ?>
    <div>
    <h1>Opções do Tema</h1>

    <?php
    if ( !empty( $_POST ) ) {
        $home_recipe = (int) $_POST[ 'pw_home_recipe' ];
        update_option( 'pw_home_recipe', $home_recipe );
        echo '<div><p>Opção atualizada com sucesso!</p></div>';
    } else {
        $home_recipe = (int) get_option( 'pw_home_recipe' );
    } ?>

    <form method="post" action="<?php echo admin_url( 'admin.php?page=custom-options' ); ?>">
    <table>
        <tr>
            <th>Receitas na Home</th>
            <td>
                <select name="pw_home_recipe">
                    <option>Escolha....</option>
                    <?php
                    for ( $i=3; $i<=12; $i+=3 ) {
                        echo '<option value="' . $i . '"';

                        if ( $i == $home_recipe )
                            echo ' selected="true"';

                        echo ">{$i}</option>";
                    } ?>
                </select>
            </td>
        </tr>
    </table>
    <p><input type="submit" value="Atualizar opções"></p>
    </form></div>
    <?php
}

Lembre-se que você pode sempre aplicar as chaves Nonce para validação do envio do formulário. No lado administrativo, ao receber o envio dos dados, você também pode verificar se a nonce é válida com check_admin_referer.

if ( !check_admin_referer( 'nonce_action', 'nonce_field' ) )
    wp_die( 'Trapaceando, é?!' );

Mantendo o valor armazenado nas opções, no momento propício bastará recuperá-lo com get_option e limitar a quantidade de resultados na Home.

Custom Post Types

Perceba que até agora cada área dos projetos desenvolvidos tem sido referenciada por palavras como tela, seção e termos semelhantes ao invés de página. Esse comportamento foi adotado para deixar claro que uma tela de um site (ou página da web) é bem diferente daquilo que o WordPress chama de página.

Dentro do CMS, por padrão existem dois tipos de conteúdos: os posts e as páginas. Os posts são organizados por tags e categorias, enquanto que as páginas possuem níveis de hierarquia entre si e também uma ordem de exibição que pode ser atribuída para cada uma delas.

As páginas remetem ao conteúdo mais estático; informação essa que não deverá sofrer alterações em seu contexto com frequência. Além disso, esse modelo permite agrupar as páginas mediante seus relacionamentos diretos. Como exemplo de página é possível destacar os textos de apresentação das empresas, formulários de contato, entre outros.

Por outro lado, o modelo de gerenciamento e classificação atribuído aos posts, definem esse tipo de conteúdo como mais dinâmico; sendo ele organizado com base nas datas de publicação, estabelece um vínculo com o tempo em que o conteúdo é publicado. Justamente por tal razão os posts são amplamente tratados pelos blogs, sites de notícias e afins.

As categorias e tags usadas para identificar e catalogar os posts possuem comportamentos um pouco distintos. Enquanto uma categoria estabelece um termo capaz de agrupar boa quantidade de posts e possuir um forte significado para os visitantes do site; uma tag serve para marcar um ponto chave do texto que eventualmente será tratado em outras ocasiões.

Adicionar um Novo Tipo de Conteúdo Personalizado

A cada tipo de projeto você pode adequar o conteúdo para ser editado como post ou página, porém isso nem sempre é o melhor a ser feito. Como o projeto atual trata de receitas não convém adicionar as receitas como posts e nem como páginas, essa atitude tornaria mais difícil e também causaria certa confusão no gerenciamento das informações.

Receitas serão Receitas, é isso que o registro de um novo tipo de conteúdo propõe.

Adicionando um novo Custom Post Type no WordPress

Inserindo um novo tipo de conteúdo ao WordPress. Tela para inserir o novo conteúdo personalizado

O registro de um novo CPT (Custom Post Type) é feito na inicialização do sistema, fazendo uso do gancho init.

add_action( 'init', 'pw_init' );

function pw_init()
{
    $attr = array(
        'public'        => true,
        'has_archive'   => true,
        'supports'      => array( 'title', 'editor', 'excerpt', 'thumbnail' ),
        'rewrite'       => array( 'slug' => 'receitas' ),
        'labels'        => array(
            'name'                  => 'Receitas',
            'add_new'               => 'Adicionar receita',
            'add_new_item'          => 'Adicionar nova receita',
            'edit_item'             => 'Editar receita',
            'new_item'              => 'Nova receita',
            'view_item'             => 'Visualizar receita',
            'search_items'          => 'Pesquisar receitas',
            'not_found'             => 'Nenhuma receita foi encontrada',
            'not_found_in_trash'    => 'Nenhuma receita foi encontrada na lixeira',
            'all_items'             => 'Todas as receitas'
        )
    );
    register_post_type( 'recipe', $attr );
    flush_rewrite_rules();
}

A aplicação de register_post_type acrescenta o novo tipo de conteúdo ao WordPress de acordo com as especificações passadas. Estabeleça um nome para o novo tipo de post e em seguida informe seus atributos no parâmetro da função. As configurações public e has_archive permitirão visualizar o conteúdo em ambiente público com as definições do template e realizar a listagem, paginação de resultados respectivamente. Será possível editar título, conteúdo, resumo e imagem destacada tal como descreve a opção supports; o endereço responsável por exibir esse conteúdo está determinado em rewrite e labels estabelece quais termos serão empregados nas telas do painel administrativo. Já flush_rewrite_rules atualiza as opções de reescrita de endereços e também as instruções para URL amigáveis desse novo tipo.

Criado um novo tipo de conteúdo, você pode ainda atribuir algum tipo de organização a ele; seja seguindo o modelo das páginas ou dos posts. Se optar por utilizar o esquema de gerenciamento das páginas poderá acrescentar a opção hierarchical como verdadeira e em supports, o item page-attributes.

$attr = array(
    'hierarchical'  => true,
    'public'        => true,
    'has_archive'   => true,
    'supports'      => array( 'title', 'editor', 'excerpt', 'thumbnail', 'page-attributes' ),
    // ....

Taxonomias

Diferentemente da organização baseada no mesmo conceito das páginas, você tem a possibilidade de adicionar taxonomias ao seu conteúdo. São termos responsáveis por classificar e organizar seu conteúdo; podem possuir o comportamento das categorias, como também o das tags.

No site das receitas não seria condizente acrescentar Categorias ou Tags para o novo conteúdo, certamente haveria conflito de entendimento entre as Categorias e Tags dos Posts e das Receitas. Analisando o projeto, sabe-se que as receitas se dividem em tipos de pratos, ou somente pratos, como massas, sobremesas, saladas, etc. Logo a classificação a ser adotada não será Tag, nem Categoria; mas sim Pratos, uma nova taxonomia. Para isso, dentro da mesma função de inicialização, antes de aplicar a reescrita de url com flush_rewrite_rules, insira:

$attr = array(
    'public'                => true,
    'show_in_nav_menus'     => true,
    'hierarchical'          => true,
    'rewrite'               => array( 'slug' => 'pratos' ),
    'labels'                => array(
        'name'              => 'Prato',
        'singular_name'     => 'Prato',
        'search_items'      => 'Pesquisar pratos',
        'all_items'         => 'Todos os pratos',
        'parent_item'       => 'Prato acima',
        'parent_item_colon' => 'Prato acima:',
        'edit_item'         => 'Editar pratos',
        'update_item'       => 'Atualizar prato',
        'add_new_item'      => 'Adicionar novo prato',
        'new_item_name'     => 'Novo prato',
        'menu_name'         => 'Pratos'
    )
);
register_taxonomy( 'recipe_types', 'recipe', $attr );

De modo semelhante a register_post_type, a função register_taxonomy incorpora taxonomias personalizadas ao WordPress. Estabeleça o comportamento de categorias a nova taxonomia com a opção hierarchical sendo verdadeira e falso para obter o mesmo tratamento das tags.

A atualização das condições de reescrita de endereços, referenciada com flush_rewrite_rules, é feita uma única vez e logo após todos os registros de tipos de posts e taxonomias; assim todas as novas configurações surtem efeito com somente uma única requisição.

Reescrita de URL (rewrite rules)

Dentro de um Tema WordPress a função flush_rewrite_rules deverá ser disparada apenas quando houver alteração na estrutura de conteúdo, ou seja, quando forem adicionados ou alterados custom post types e/ou taxonomias. Fazendo uso das opções, controle o momento em que a função deverá ser executada.

if ( !get_option( 'pw_cpt_and_tax' ) ) {
    add_option( 'pw_cpt_and_tax', true );
    flush_rewrite_rules();
}

Verifica-se a existência da opção no banco de dados e caso não exista, a insere e dispara a função. Se existir nada acontece. Aprimore essa instrução de modo que você não tenha que criar uma opção em sua base para cada nova atualização; prefira um simples controle de versão.

$version = '1.2';
if ( get_option( 'pw_cpt_and_tax_version' ) !== $version ) {
    update_option( 'pw_cpt_and_tax', $version );
    flush_rewrite_rules();
}

Com esse código, toda vez que precisar alterar a estrutura dos registros feitos, basta alterar o valor da variável $version que a função será executada.

Gerenciamento de Custom Post Types

Esse site que você está desenvolvendo pede ainda que seja feito o gerenciamento de algumas informações especificas das receitas: a dificuldade de cada receita e a quantidade de calorias. Para ambas informações será usado o recurso dos meta dados e a edição dos mesmos em um novo metabox.

function pw_admin_init()
{
    add_meta_box( 'box_recipe', 'Detalhes da Receita', 'pw_box_recipe', 'recipe', 'normal', 'high' );
}

function pw_box_recipe()
{
    global $post; ?>
    <table>
        <tr>
            <th>Dificuldade</th>
            <td>
                <select name="pw_level">
                    <option>Escolha...</option>
                    <?php
                    $levels = get_levels();
                    $l = get_post_meta( $post->ID, 'pw_level', true );
                    foreach ( $levels as $level => $label ) {
                        echo '<option value="' . $level . '"';

                        if ( $l == $level )
                            echo ' selected="true"';

                        echo ">{$label}</option>";
                    }
                    ?>
                </select>
            </td>
        </tr>
        <tr>
            <th>Calorias</th>
            <td><input type="text" name="pw_kcal" value="<?php echo get_post_meta( $post->ID, 'pw_kcal', true ); ?>" /></td>
        </tr>
    </table>
    <?php
}

 function get_levels()
 {
     return array(
         'easy'     => 'Fácil',
         'medium'   => 'Médio',
         'hard'     => 'Difícil'
     );
 }

function pw_box_model_save( $id )
{
    if ( isset( $_POST[ 'pw_level' ] ) && isset( $_POST[ 'pw_kcal' ] ) ) {

        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
            return $id;

        update_post_meta( $id, 'pw_level', $_POST[ 'pw_level' ] );
        update_post_meta( $id, 'pw_kcal', $_POST[ 'pw_kcal' ] );
    }
}

No caso dos níveis de dificuldade, foi setado um select ao invés de um input de texto direto para evitar que o gestor de conteúdo digite termos distintos daqueles que devem ser trabalhados. A definição dos níveis é feita por uma função, pois desse modo você centraliza a definição desses valores em um único local e facilita futuras atualizações.

A manipulação dos tipos de posts personalizados completa com exibição de custom fields, taxonomias e imagem destacada

A manipulação dos tipos de posts personalizados completa com exibição de custom fields, taxonomias e imagem destacada

Poderia também ter sido adotada uma nova taxonomia para definição das dificuldades, tratamento esse que tornaria o projeto ainda mais customizável; no entanto a escolha pelo custom field foi feita para apresentar novos recursos a seguir.

Novas colunas para as tabelas de conteúdo

Dentro do Dashboard, abra a listagem de resultados do novo tipo de conteúdo. Note que as informações listadas são poucas e isso acaba por não favorecer a agilidade e praticidade da manutenção do site. A exemplo do que ocorre com os posts, crie colunas adicionais para exibir as informações principais de cada novo conteúdo.

add_filter( 'manage_posts_columns', 'pw_custom_cols', 10, 2 );

function pw_custom_cols( $columns, $post_type )
{
    if ( $post_type == 'recipe' ) {
        $columns[ 'tag' ] = 'Pratos';
        $columns[ 'level' ] = 'Dificuldade';
        $columns[ 'kcal' ]  = 'Calorias (Kcal)';
        $columns[ 'thumb' ] = 'Imagem destacada';
    }
    return $columns;
}

Repare que o filtro pelo tipo de conteúdo não permite que as novas colunas apareçam em qualquer tipo, mas apenas naquele que fora estabelecido. Caso tenha especificado outro nome, não se esqueça de alterar o valor nessa condição. Outro detalhe importante é que ao apagar algum valor já existente da variável a ser retornada você consegue remover colunas de sua tabela. Do mesmo modo, ao renomear seus valores é possível também trocar seus títulos.

Com esse código acima as novas colunas foram criadas. Agora será preciso que você especifique ao sistema quais informações serão apresentadas para cada coluna em questão.

add_action( 'manage_recipe_posts_custom_column', 'pw_custom_cols_content' );

O gatilho da ação é manage_{tipo-de-conteúdo}_posts_custom_column. Do mesmo modo que a condição acima, se você escolheu outro nome para o CPT, altere o valor também nesse gancho.

function pw_custom_cols_content( $column )
{
    global $post;
    $value = get_post_meta( $post->ID, 'pw_' . $column, true );
    switch ( $column ) {
        case 'tag':
            echo get_the_term_list( $post->ID, 'recipe_types', '', ', ' );
            break;
        case 'level':
            $levels = get_levels();
            echo ( isset( $levels[ $value ] ) ) ? $levels[ $value ] : 'Não definido';
            break;
        case 'thumb':
            echo get_the_post_thumbnail( $post->ID, array( 64, 64 ) );
            break;
        default:
            echo $value;
            break;
    }
}

De acordo com o nome atribuído a cada coluna na chave do array onde foram criadas, você verifica qual informação está sendo pedida e a exibe. As taxonomias são recuperadas, os níveis de dificuldade passam por uma validação básica, a imagem em destaque é apresentada e em quaisquer outras condições (no caso apenas o valor do campo calorias), mostra o meta dado diretamente.

Ordenação personalizada dos resultados

O trabalho de manutenção pode ser melhor realizado caso exista a possibilidade de se ordenar determinadas informações. No projeto em questão é relevante classificar os conteúdos com base na dificuldade e nas calorias de cada um.

add_filter( 'manage_edit-recipe_sortable_columns', 'pw_sort_cols' );

Assim como manage_{tipo-de-conteúdo}_posts_custom_column o gancho em questão é definido por manage_edit-{tipo-de-conteúdo}_sortable_columns;

function pw_sort_cols( $columns )
{
    $cols = array(
        'level' => 'dificuldade',
        'kcal'  => 'calorias'
    );
    $columns = array_merge( $columns, $cols );
    return $columns;
}

O parâmetro passado a função traz as colunas que já possuem a capacidade de ordenação para a referida tabela. Em novos tipos de conteúdo as colunas título e data já vem com esse recurso; justamente na pretensão de manter tais colunas é que as novas colunas são concatenadas as existentes. Caso não queira ordenar por título ou data basta retirar tais valores da variável a ser retornada pela função.

A partir desse momento os títulos especificados já estão clicáveis. No entanto para que o recurso funcione como pretendido é preciso informar ao sistema a ordenação que se deseja fazer.

add_filter( 'request', 'pw_column_orderby' );

function pw_column_orderby( $vars )
{
    if ( isset( $vars[ 'orderby' ] ) && ( in_array( $vars[ 'orderby' ], array( 'dificuldade', 'calorias' ) ) ) ) :
        if ( $vars[ 'orderby' ] == 'dificuldade' ) {
            $key = 'pw_level';
            $ord = 'meta_value';
        } else {
            $key = 'pw_kcal';
            $ord = 'meta_value_num';
        }
        $vars = array_merge( $vars, array(
                'meta_key'  => $key,
                'orderby'   => $ord
            )
        );
    endif;
    return $vars;
}

Dentro do filtro recém-criado você deve verificar quais valores estão definidos para a chave orderby. Se os valores forem aqueles personalizados, comece o tratamento. De acordo com o campo escolhido foi definido a chave do meta dado referente a tal informação e também o tipo de ordenação. Como dificuldade é do tipo texto, a ordenação feita é baseada no valor texto do meta dado; por sua vez o valor armazenado em calorias deverá ser sempre numérico, para tanto emprega-se a ordenação numérica. Lembre-se de retornar o valor das variáveis atualizados ao final da função.

Aplicação de Filtros

Além de apresentar os principais valores do conteúdo e ordená-los, é de grande valia oferecer filtros com intuito de retornar uma quantidade menor de resultados e também agrupar os conteúdos que se deseja trabalhar.

add_action( 'restrict_manage_posts', 'pw_restrict_manage_posts' );

Esse gatilho permite adicionar informações HTML ao lado dos filtros existentes (por padrão, o filtro de datas). Por não se tratar de um filtro, mas sim de uma ação; as instruções devem ser exibidas e não retornadas.

function pw_restrict_manage_posts()
{
    $type = ( isset( $_GET[ 'post_type' ] ) ) ? $_GET[ 'post_type' ] : false;
    if ( $type == 'recipe' ) {
        $levels = get_levels();
        $l = ( isset( $_GET[ 'level' ] ) ) ? $_GET[ 'level' ] : false;
        echo '<select name="level">';
        echo '<option>Todas as Dificuldades</option>';

        foreach ( $levels as $level => $label ) {
            echo '<option value="' . $level . '"';

            if ( $l == $level )
                echo ' selected="true"';

            echo ">{$label}</option>";
        }

        echo '</select>';

        $tag = ( isset( $_GET[ 'group' ] ) ) ? $_GET[ 'group' ] : 0;
        wp_dropdown_categories( array(
                'name'              => 'group',
                'orderby'           => 'title',
                'taxonomy'          => 'recipe_types',
                'selected'          => $tag,
                'show_option_none'  => 'Todos os Pratos'
            )
        );
    }
}

Como as listagens de resultados sempre são feitas com endereços que passam via $_GET o tipo de post que se está gerenciando; aplique o filtro com base nessa informação. Foram então adicionados dois campos de formulário do tipo select, sendo um para as dificuldades e o outro para as novas taxonomias.

Tal como o recurso de ordenar, o desenvolvimento desses filtros de conteúdo também se dividem em duas partes: a primeira referente a exibição dos campos de formulário ao gestor de conteúdo e a outra que trata de receber os valores submetidos e impor isso a consulta realizada pelo sistema. Defina o tratamento a ser dado a consulta pelo mesmo gatilho request usado na ordenação dos posts.

add_filter( 'request', 'pw_restrict_filter' );

function pw_restrict_filter( $vars )
{
    if ( $_GET[ 'level' ] ) {
        $l = get_levels();
        if ( isset( $l[ $_GET[ 'level' ] ] ) ) {
            $vars[ 'meta_query' ] = array(
                array(
                    'key' => 'pw_level',
                    'value' => $_GET[ 'level' ]
                )
            );
        }
    }

    if ( isset( $_GET[ 'group' ] ) ) {
        $group = (int) $_GET[ 'group' ];
        if ( $group ) {
            $tag = get_term_by( $id, $group, 'recipe_types' );
            if ( isset( $tag->slug ) ) {
                $vars[ 'taxonomy' ] = 'recipe_types';
                $vars[ 'term' ] = $tag->slug;
            }
        }
    }

    return $vars;
}

Ao submeter o formulário dos filtros de conteúdo, você pode verificar que as informações são enviadas através do método $_GET. Logo, confira o valor de cada campo criado de forma a verificar se existe mesmo o valor transmitido (é possível que tenha sido removido ou alterado de modo direto via barra de endereços do navegador) e somente a partir dessa confirmação aplique o filtro a consulta que será realizada.

Atente-se ao fato que no filtro dos resultados pela quantidade de caloria das receitas ao invés de especificar um valor para meta_key e outro para meta_value, está sendo atribuído um valor a meta_query. Isso foi feito para não conflitar a ordenação baseada nos meta dados, com o novo filtro a ser aplicado. Com isso além de filtrar os resultados, será possível também ordená-los como você bem pretender.

Consultas avançadas com meta_query

Entre os itens do checklist desse projeto estão as listas do rodapé do site. As listas requerem algumas consultas especiais pois pretendem ligar o tipo de conteúdo com os valores atribuídos aos custom fields dos mesmos. Veja o caso da listagem das receitas simples:

$easies = get_posts( array(
        'post_type'         => 'recipe',
        'posts_per_page'    => 7,
        'meta_key'          => 'pw_level',
        'meta_value'        => 'easy'
    )
);

A consulta realizada determina a chave e o valor pretendido do meta valor, no caso o nível de dificuldade como fácil; e a função get_posts se encarrega de fazer a ligação entre as tabelas e retornar os resultados esperados. Na exibição das listagens são utilizadas normalmente as funções get_permalink e get_the_title para exibir os links e títulos dos resultados, assim como você fez em outras ocasiões.

No entanto, o caso das receitas lights requer algum cuidado com o uso da classe WP_Query na realização das consultas. Ocorre que tais receitas devem possuir o valor calórico inferior a 1000 calorias e utlizando apenas as opções meta_key e meta_value a comparação sempre será de igualdade. Para casos como esse existem as variações de meta_query que possibilita definir o tipo de comparação (no caso, numérica) e a condição da mesma (menor ou igual).

$lights = get_posts( array(
        'post_type'         => 'recipe',
        'posts_per_page'    => 7,
        'meta_query'        => array(
            array(
                'key'       => 'pw_kcal',
                'value'     => 1000,
                'compare'   => '<=',
                'type'      => 'NUMERIC'
            )
        )
    )
);

Arquivos do Tema

A proposta da tela inicial desse projeto é exibir apenas as últimas receitas e um link para listar todas as demais. Assim como você já viu o uso dos filtros para as consultas, aplique através de pre_get_posts as condições necessárias para limitar os resultados do tipo receita e que também possuam imagem destacada.

function pw_pre_get_posts( $q )
{
    if ( $q->is_home ) {
        $posts_per_page = (int) get_option( 'pw_home_recipe' );
        if ( !$posts_per_page ) $posts_per_page = 3;

        $q->set( 'post_type', 'recipe' );
        $q->set( 'posts_per_page', $posts_per_page );
        $q->set( 'meta_key', '_thumbnail_id' );
    }
    return $q;
}

Dentro da tela inicial, o link que levará o visitante a tela com a listagem completa das receitas é definido mediante o uso de get_post_type_archive_link. Tenha cuidado ao aplicar essa função e enviar o visitante para esse endereço. A função somente funcionará como esperado se a opção has_archive de register_post_type estiver definida como verdadeira.

<a href="<?php echo get_post_type_archive_link( 'recipe' ); ?>" title="Todas as receitas">Veja mais receitas</a>

Caso você tenha feito adequadamente a integração inicial do Tema, serão exibidas as receitas no mesmo formato de paginação dos posts. Porém o objetivo desse projeto é manter o mesmo estilo utilizado na tela inicial para apresentar esse conteúdo. Para isso acontecer você fará algumas manobras aproveitando-se da hierarquia do WordPress e evitando também a duplicidade de códigos em seu trabalho.

Renomeie o arquivo home.php para archive-{tipo-de-conteúdo}.php, no exemplo archive-recipe.php. Crie um arquivo do tipo PHP em branco, especifique-o como home.php e dentro dele informe:

<?php get_template_part( 'archive', 'recipe' ); ?>

Tal como foi adiantado, baseado pela hierarquia, você criou um arquivo próprio para apresentação dos resultados desse tipo de conteúdo em específico. Após isso, através da home, você referenciou a utilização desse arquivo com get_template_part.

Include tags

O segundo parâmetro da função permite a você realizar a incorporação de arquivos personalizados para diferentes situações. Inicialmente, ela procura o arquivo de nome parâmetro1-parâmetro2.php; e se não encontrar, localiza o arquivo parâmetro1.php.

Tome cuidado com os arquivos do tema. Nesse caso por exemplo, uma vez que o WordPress encontrou a home do site e nela foi feita a requisição acima, caso não exista nenhum dos dois arquivos a tela ficará vazia. Isso porque o sistema parou de procurar o arquivo responsável por exibir o conteúdo nessa situação, justamente por tê-lo encontrado, e mesmo que os novos arquivos não existam, o index.php não será aberto pois a nova incorporação de arquivos foi feita por você e não de modo automático. Por isso, evite esse tipo de inserção a menos que você tenha certeza que os arquivos estarão no diretório do tema quando requeridos.

Esse recurso é bem interessante de ser utilizado para customizar ainda mais certas áreas do site. Ele pode ser aplicado também com get_header, get_footer e get_sidebar passando uma variação como parâmetro único dessas funções, ou seja, ao chamar get_header( ‘alternate’ ); ao invés de incorporar o header.php diretamente, primeiro será verificado se o arquivo header-alternate.php está presente entre os arquivos do Tema. Caso ele exista é incoporado, do contrário o header.php padrão é inserido normalmente. Ao contrário de get_template_part, caso as funções citadas não encontre nenhum dos arquivos (com sufixo ou não), o WordPress incorpora modelos pré-definidos para cada situação.

Resumo das funções responsáveis pela inclusão de arquivos e suas respectivas aplicações

Resumo das funções responsáveis pela inclusão de arquivos e suas respectivas aplicações

Breadcrumbs

A aplicação dos breadcrumbs dentro de um Tema WordPress é realizável com a utilização das conditional tags. Com tais verificações você consegue saber exatamente qual foi o conteúdo que a consulta atual está exibindo e com isso apresentar seu caminho para o visitante. Em um primeiro momento, determine quais caminhos serão definidos para o projeto:

  • A Home deve ficar sem nenhuma informação
  • Um Post apresenta as categorias nas quais ele está inserido
  • Uma Página deve exibir as páginas níveis acima da hierarquia
  • O novo tipo de conteúdo receitas deve apresentar um link para a listagem de todas as receitas
  • Categorias, Tags, Lista das receitas e Pratos apenas informam qual requisição foi feita
  • Determinar um título para resultados de busca, página não econtrada e demais situações genéricas.

Feita as devidas especificações sobre quais caminhos serão apresentados é hora aplicar as devidas condições para então apresentar as informações desejadas. Crie uma função que será incumbida desse trabalho.

function pw_breadcrumbs( $sep = ' / ' )
{
    if ( is_home() ) return false;
}

Foi passado como parâmetro opcional, o caracter separador dos links; se preferir utilize uma seta, imagem; enfim, o que mais lhe agradar. Ainda no código acima a primeira especificação foi atendida: se a requisição estiver sendo feita a partir da Home, nada acontece. As instruções a seguir deverão constar dentro da função criada, são os devidos complementos.

$links = '<span>Você está aqui: </span>';
$links .= '<a href="' . PW_URL . '" title="' . esc_attr( PW_SITE_NAME ) . '">Home</a>' . $sep;

A variável links armazena toda string a ser apresentada peo breadcrumb; começa com o texto de localização e o link para tela inicial do site.

if ( is_singular() ) {
    global $post;
    $type = get_post_type( $post->ID );
    switch ( $type ) {
        case 'post':
            $categs = get_the_category( $post->ID );
            if ( count( $categs ) > 1 )
                $links .= get_the_category_list( ', ', '', $post->ID ) . $sep;
            else if ( isset( $categs[0]->cat_ID ) )
                $links .= get_category_parents( $categs[0]->cat_ID, true, $sep ) . $sep;

            break;
        case 'page':
            $parent_id = (int) $post->post_parent;
            if ( $parent_id > 0 ) {
                $ancestors = array_reverse( $post->ancestors );
                foreach ( $ancestors as $ancestor ) {
                    $title = get_the_title( $ancestor );
                    $links .= '<a href="' . get_permalink( $ancestor ) .
                        '" title="' . esc_attr( $title ) .
                        '">'. $title . '</a>' . $sep;
                }
            }
            break;
        case 'recipe':
            $type_obj = get_post_type_object( $type );
            $links .= '<a href="' . get_post_type_archive_link( $type ) . '">' . $type_obj->label . '</a>' . $sep;
            break;
    }
    $links .= get_the_title();
}

A condição is_singular engloba todo tipo de conteúdo quando exibido separadamente, isto é, ao exibir um post, uma página ou um tipo personalizado de conteúdo em específico.

Para os posts são verificadas as categorias. Se existir mais de uma caegoria, são exibidas todas elas agrupadas de acordo com o separador informado em get_the_category_list; no exemplo uma vírgula. Caso exista apenas uma, são apresentadas as eventuais categorias níveis acima da categoria em questão.

No tratamento das páginas, o valor de ancestors são as páginas níveis acima da página atual. Com isso é feita a varredura desse array apresentando os devidos links. O novo tipo de conteúdo apenas recupera o link para o arquivo de paginação com o devido rótulo. A função get_post_type_object retorna todas as definições feitas em register_post_type.

Definido o caminho percorrido até o conteúdo com suas devidas ligações, exibe o título do conteúdo atual.

Você pode também especificar estruturas diferentes para o seu breadcrumb. Veja por exemplo que o tipo de conteúdo personalizado pode ser antecedido por suas taxonomias, tal como os posts são pelas categorias. No caso você poderia se utilizar das funções get_the_terms, get_the_term_list e get_terms para realizar esse processo.

} else if ( is_category() ) {
    $links .= single_cat_title( '', false );
} else if ( is_tag() ) {
    $links .= 'Tag "' . single_tag_title( '', false ) . '"';
} else if ( is_post_type_archive( 'recipe' ) ) {
    $links .= 'Receitas';
} else if ( is_tax( 'recipe_types' ) ) {
    $links .= 'Prato: ' . single_term_title( '', false );
} else if ( is_search() ) {
    $links .= 'Resultados da busca';
} else if ( is_404() ) {
    $links .= 'Página não encontrada...';
} else {
    $links .= 'Arquivo';
}

Como você pode verificar cada tipo de requisição possui um meio de recuperar qual informação está sendo buscada. Dentro da última verificação ainda podem ser acrescidas as condições de data, posts agrupados por autor ou mesmo para telas de exibição dos arquivos anexos.

echo '<nav>' . $links . '</nav>';

Concluindo a função dos breadcrumbs, apresente os resultados em seu Tema.

Para não esquecer

  • Estrutura de organização da informação dentro do WordPress
  • Possibilidade de se trabalhar com diferentes tipos de conteúdo e otimização do gerenciamento dos mesmos através de recursos personalizados no painel administrativo
  • API de Opções
  • Consultas a meta dados com meta_query
  • Arquivos do Tema e a relação entre as Conditional e Include Tags na hierarquia do WordPress
  • Uso de breadcrumbs