Eu fiz uma pesquisa bastante extensa sobre como usar pre_get_posts
em páginas verdadeiras e nas primeiras páginas estáticas, e parece que não existe um método à prova de idiotas.
A melhor opção que encontrei até hoje foi de um post feito por @birgire no Stackoverflow . Eu o reescrevi em uma classe demo e tornei o código um pouco mais dinâmico
class PreGeTPostsForPages
{
/**
* @var string|int $pageID
* @access protected
* @since 1.0.0
*/
protected $pageID;
/**
* @var bool $injectPageIntoLoop
* @access protected
* @since 1.0.0
*/
protected $injectPageIntoLoop;
/**
* @var array $args
* @access protected
* @since 1.0.0
*/
protected $args;
/**
* @var int $validatedPageID
* @access protected
* @since 1.0.0
*/
protected $validatedPageID = 0;
/**
* Constructor
*
* @param string|int $pageID = NULL
* @param bool $injectPageIntoLoop = false
* @param array| $args = []
* @since 1.0.0
*/
public function __construct(
$pageID = NULL,
$injectPageIntoLoop = true,
$args = []
) {
$this->pageID = $pageID;
$this->injectPageIntoLoop = $injectPageIntoLoop;
$this->args = $args;
}
/**
* Private method validatePageID()
*
* Validates the page ID passed
*
* @since 1.0.0
*/
private function validatePageID()
{
$validatedPageID = filter_var( $this->pageID, FILTER_VALIDATE_INT );
$this->validatedPageID = $validatedPageID;
}
/**
* Public method init()
*
* This method is used to initialize our pre_get_posts action
*
* @since 1.0.0
*/
public function init()
{
// Load the correct actions according to the value of $this->keepPageIntegrity
add_action( 'pre_get_posts', [$this, 'preGetPosts'] );
}
/**
* Protected method pageObject()
*
* Gets the queried object to use that as page object
*
* @since 1.0.0
*/
protected function pageObject()
{
global $wp_the_query;
return $wp_the_query->get_queried_object();
}
/**
* Public method preGetPosts()
*
* This is our call back method for the pre_get_posts action.
*
* The pre_get_posts action will only be used if the page integrity is
* not an issue, which means that the page will be altered to work like a
* normal archive page. Here you have the option to inject the page object as
* first post through the_posts filter when $this->injectPageIntoLoop === true
*
* @since 1.0.0
*/
public function preGetPosts( \WP_Query $q )
{
// Make sure that we are on the main query and the desired page
if ( is_admin() // Only run this on the front end
|| !$q->is_main_query() // Only target the main query
|| !is_page( $this->validatedPageID ) // Run this only on the page specified
)
return;
// Remove the filter to avoid infinte loops
remove_filter( current_filter(), [$this, __METHOD__] );
// METHODS:
$this->validatePageID();
$this->pageObject();
$queryArgs = $this->args;
// Set default arguments which cannot be changed
$queryArgs['pagename'] = NULL;
// We have reached this point, lets do what we need to do
foreach ( $queryArgs as $key=>$value )
$q->set(
filter_var( $key, FILTER_SANITIZE_STRING ),
$value // Let WP_Query handle the sanitation of the values accordingly
);
// Set $q->is_singular to 0 to get pagination to work
$q->is_singular = false;
// FILTERS:
add_filter( 'the_posts', [$this, 'addPageAsPost'], PHP_INT_MAX );
add_filter( 'template_include', [$this, 'templateInclude'], PHP_INT_MAX );
}
/**
* Public callback method hooked to 'the_posts' filter
* This will inject the queried object into the array of posts
* if $this->injectPageIntoLoop === true
*
* @since 1.0.0
*/
public function addPageAsPost( $posts )
{
// Inject the page object as a post if $this->injectPageIntoLoop == true
if ( true === $this->injectPageIntoLoop )
return array_merge( [$this->pageObject()], $posts );
return $posts;
}
/**
* Public call back method templateInclude() for the template_include filter
*
* @since 1.0.0
*/
public function templateInclude( $template )
{
// Remove the filter to avoid infinte loops
remove_filter( current_filter(), [$this, __METHOD__] );
// Get the page template saved in db
$pageTemplate = get_post_meta(
$this->validatedPageID,
'_wp_page_template',
true
);
// Make sure the template exists before we load it, but only if $template is not 'default'
if ( 'default' !== $pageTemplate ) {
$locateTemplate = locate_template( $pageTemplate );
if ( $locateTemplate )
return $template = $locateTemplate;
}
/**
* If $template returned 'default', or the template is not located for some reason,
* we need to get and load the template according to template hierarchy
*
* @uses get_page_template()
*/
return $template = get_page_template();
}
}
$init = new PreGeTPostsForPages(
251, // Page ID
false,
[
'posts_per_page' => 3,
'post_type' => 'post'
]
);
$init->init();
Isso funciona bem e pagina conforme o esperado, usando minha própria função de paginação .
PROBLEMAS:
Por causa da função, perco a integridade da página, em que outras funções dependem do objeto de página armazenado $post
. $post
antes que o loop seja definido como a primeira postagem no loop e $post
definido como a última postagem no loop após o loop, o que é esperado. O que eu preciso é que $post
seja definido como o objeto de página atual, ou seja, o objeto consultado.
Além disso, $wp_the_query->post
e $wp_query->post
ocupa o primeiro posto no circuito e não o objeto consultado como em uma página normal
Eu uso o seguinte ( fora da minha classe ) para verificar minhas globais antes e depois do loop
add_action( 'wp_head', 'printGlobals' );
add_action( 'wp_footer', 'printGlobals' );
function printGlobals()
{
$global_test = 'QUERIED OBJECT: ' . $GLOBALS['wp_the_query']->queried_object_id . '</br>';
$global_test .= 'WP_THE_QUERY: ' . $GLOBALS['wp_the_query']->post->ID . '</br>';
$global_test .= 'WP_QUERY: ' . $GLOBALS['wp_query']->post->ID . '</br>';
$global_test .= 'POST: ' . $GLOBALS['post']->ID . '</br>';
$global_test .= 'FOUND_POSTS: ' . $GLOBALS['wp_query']->found_posts . '</br>';
$global_test .= 'MAX_NUM_PAGES: ' . $GLOBALS['wp_query']->max_num_pages . '</br>';
?><pre><?php var_dump( $global_test ); ?></pre><?php
}
ANTES DO LAÇO:
Antes do loop, o problema é parcialmente resolvido configurando- $injectPageIntoLoop
se true, que injeta o objeto de página como primeira página no loop. Isso é bastante útil se você precisar mostrar as informações da página antes das postagens solicitadas, mas se você não quiser, está ferrado.
Eu posso resolver o problema antes do loop invadindo diretamente os globais, o que realmente não gosto. Eu conecto o método a seguir wp
dentro do meu preGetPosts
método
public function wp()
{
$page = get_post( $this->pageID );
$GLOBALS['wp_the_query']->post = $page;
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
$GLOBALS['post'] = $page;
}
e preGetPosts
método interno
add_action( 'wp', [$this, 'wp'] );
A partir daí, $wp_the_query->post
, $wp_query->post
e $post
tudo mantém o objeto página.
DEPOIS DO LAÇO
É aqui que está o meu grande problema, depois do loop. Depois de hackear os globais através do wp
gancho e método,
$wp_the_query->post
e$wp_query->post
volta à primeira postagem do loop, conforme o esperado$post
é definido como a última postagem no loop.
O que eu preciso é que todos os três sejam retornados ao objeto consultado / objeto de página atual.
Eu tentei ligar o wp
método à loop_end
ação, o que não funciona. Ligar o wp
método à get_sidebar
ação funciona, mas é tarde demais.
add_action( 'get_sidebar', [$this, 'wp'] );
A execução printGlobals()
direta após o loop no modelo confirma que as $wp_the_query->post
e $wp_query->post
ainda estão definidas na primeira postagem e $post
na última postagem.
Posso adicionar manualmente o código dentro do wp
método após o loop dentro do modelo, mas a idéia não é alterar os arquivos do modelo diretamente, pois a classe deve ser transferida em um plug-in entre temas.
Existe alguma maneira adequada para resolver este problema em uma corrida pre_get_posts
em uma verdadeira página e página estática e ainda manter a integridade $wp_the_query->post
, $wp_query->post
e $post
( tendo os set para o objeto consultado ) antes e depois do loop.
EDITAR
Parece haver confusão sobre o que eu preciso e por que eu preciso
O que eu preciso
Eu preciso manter os valores de $wp_the_query->post
, $wp_query->post
e $post
através do modelo, independentemente, e esse valor deve ser o objeto consultado. Nesta fase, com o código que eu publiquei, os valores dessas três variáveis não mantêm o objeto de página, mas publicam objetos de postagens no loop. Espero que esteja claro o suficiente.
Eu publiquei um código que você pode usar para testar essas variáveis
Por que eu preciso disso
Preciso de uma maneira confiável de adicionar postagens pre_get_posts
nos modelos de página e nas primeiras páginas estáticas sem alterar a funcionalidade da página inteira. Nesse estágio, como está o código em questão, ele interrompe meu recurso de trilha de navegação e o recurso de página relacionado após o loop, devido ao $post
qual contém o objeto de postagem "errado".
Acima de tudo, não quero alterar os modelos de página diretamente. Quero poder adicionar postagens a um modelo de página sem QUALQUER modificação no modelo
fonte
Respostas:
Finalmente consegui funcionar, mas não com o código da minha pergunta. Eu descartei totalmente essa ideia e comecei a seguir uma nova direção.
NOTA:
Se alguém conseguir resolver os problemas da minha pergunta, fique à vontade para postar uma resposta. Além disso, se você tiver outras soluções, sinta-se à vontade para postar uma resposta.
CLASSE E SOLUÇÃO REWORKED:
O que tentei fazer aqui foi usar a pós-injeção, em vez de alterar completamente a consulta principal e ficar preso a todos os problemas acima, incluindo (a) alteração direta de globais, (b) execução na questão de valor global e (c) reatribuindo modelos de página.
Usando injeção post, eu sou capaz de manter a integridade post completo, por isso
$wp_the_query->post
,$wp_query->post
,$posts
e$post
estadia constante durante todo o modelo. Cada uma dessas variáveis faz referência ao objeto de página atual (como é o caso de páginas verdadeiras). Dessa forma, funções como trilhas de navegação sabem que a página atual é uma página verdadeira e não algum tipo de arquivo.Eu tive que alterar ligeiramente a consulta principal ( por meio de filtros e ações ) para ajustar a paginação, mas vamos chegar a isso.
CONSULTA PÓS INJEÇÃO
Para realizar a pós-injeção, usei uma consulta personalizada para retornar as postagens necessárias para a injeção. Também usei a
$found_pages
propriedade da consulta personalizada para ajustar a da consulta principal para que a paginação funcione a partir da consulta principal. As postagens são injetadas na consulta principal por meio daloop_end
ação.Para tornar a consulta personalizada acessível e utilizável fora da classe, apresentei algumas ações.
Ganchos de paginação para conectar funções de paginação:
pregetgostsforgages_before_loop_pagination
pregetgostsforgages_after_loop_pagination
Contador personalizado que conta as postagens no loop. Essas ações podem ser usadas para alterar como as postagens são exibidas dentro do loop de acordo com o número da postagem.
pregetgostsforgages_counter_before_template_part
pregetgostsforgages_counter_after_template_part
Gancho geral para acessar o objeto de consulta e o objeto de postagem atual
pregetgostsforgages_current_post_and_object
Esses ganchos oferecem uma experiência completa, pois você não precisa alterar nada no modelo da página, que era minha intenção original desde o início. Uma página pode ser completamente alterada a partir de um plug-in ou arquivo de função, o que torna essa solução muito dinâmica.
Também usei
get_template_part()
para carregar uma parte do modelo, que será usada para exibir as postagens. Atualmente, a maioria dos temas usa partes do modelo, o que torna isso muito útil na classe. Se os seus usos temáticoscontent.php
, você pode simplesmente passarcontent
para$templatePart
a cargacontent.php
.Se você precisar de suporte pós formato para partes de modelos, é fácil - você pode simplesmente passar
content
para$templatePart
e conjunto$postFormatSupport
paratrue
. Como resultado, a parte do modelocontent-video.php
será carregada para uma postagem com um formato de postagem devideo
.A CONSULTA PRINCIPAL
As seguintes alterações foram feitas na consulta principal por meio dos respectivos filtros e ações:
Para paginar a consulta principal:
O
$found_posts
valor da propriedade da consulta do injetor é passado para o objeto de consulta principal através dofound_posts
filtro.O valor do parâmetro passado pelo usuário
posts_per_page
é definido como a consulta principalpre_get_posts
.$max_num_pages
é calculado usando a quantidade de postagens em$found_posts
eposts_per_page
. Poris_singular
ser verdade nas páginas, inibe aLIMIT
cláusula que está sendo definida. Simplesmente definiris_singular
como false causou alguns problemas, então decidi definir aLIMIT
cláusula através dopost_limits
filtro. Eu mantive a cláusulaoffset
of para evitar 404 em páginas com a paginação ativada.LIMIT
0
Isso cuida da paginação e de qualquer problema que possa surgir após a injeção.
O OBJETO DA PÁGINA
O objeto da página atual está disponível para exibição como uma postagem usando o loop padrão na página, separado e por cima das postagens injetadas. Se você não precisar disso, pode simplesmente definir
$removePageFromLoop
como true e isso ocultará o conteúdo da página.Nesta fase, estou usando CSS para ocultar o objeto da página através das ações
loop_start
eloop_end
, pois não consigo encontrar outra maneira de fazer isso. A desvantagem desse método é que qualquer coisa conectada aothe_post
gancho de ação dentro da consulta principal também ficará oculta.A CLASSE
A
PreGetPostsForPages
classe pode ser aprimorada e também deve ter um espaço para nome apropriado. Embora você possa simplesmente soltar isso no arquivo de funções do seu tema, seria melhor soltar isso em um plug-in personalizado.Use, modifique e abuse como achar melhor. O código é bem comentado, por isso deve ser fácil seguir e ajustar
USO
Agora você pode iniciar a classe ( também no seu plugin ou arquivo de funções ) da seguinte forma para direcionar a página com o ID 251, na qual mostraremos 2 postagens por página do
post
tipo de postagem.ADICIONANDO PAGINAÇÃO E ESTILO PERSONALIZADO
Como mencionei anteriormente, existem algumas ações na consulta do injetor para adicionar paginação e / ou estilo personalizado.
No exemplo a seguir, adicionei a paginação após o loop usando minha própria função de paginação da resposta vinculada . Além disso, usando meu contador personalizado, adicionei
<div>
a para exibir minhas postagens em duas colunas.Aqui estão as ações que eu usei
Observe que a paginação é definida pela consulta principal, não pela consulta do injetor, portanto, funções internas como
the_posts_pagination()
também devem funcionar.Este é o resultado final
PÁGINAS DIÁRIAS ESTÁTICAS
Tudo funciona como esperado nas primeiras páginas estáticas, juntamente com a minha função de paginação, sem exigir mais modificações.
CONCLUSÃO
Isso pode parecer um monte de sobrecarga, e pode ser, mas os profissionais superam o grande momento do golpe.
BIG PRO'S
Você não precisa alterar o modelo da página específica de forma alguma. Isso torna tudo dinâmico e pode ser facilmente transferido entre os temas sem fazer modificações no código, desde que tudo seja feito em um plugin.
No máximo, você só precisa criar uma
content.php
parte do modelo no seu tema se o tema ainda não tiver um.Qualquer paginação que funcione na consulta principal funcionará na página sem nenhum tipo de alteração ou qualquer coisa extra da consulta sendo passada para a função.
Há mais profissionais que não consigo pensar agora, mas esses são os mais importantes.
fonte