Saya telah melakukan penelitian yang cukup luas tentang cara menggunakan pre_get_posts
pada halaman benar dan halaman depan statis, dan tampaknya tidak ada metode bukti bodoh.
Opsi terbaik yang saya temukan sampai saat ini adalah dari pos yang dilakukan oleh @birgire di Stackoverflow . Saya telah menulis ulang menjadi kelas demo dan membuat kodenya sedikit lebih dinamis
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();
Ini bekerja dengan baik dan halaman seperti yang diharapkan dengan menggunakan fungsi pagination saya sendiri .
MASALAH:
Karena fungsinya, saya kehilangan integritas halaman yang memuat fungsi lain yang bergantung pada objek halaman yang disimpan $post
. $post
sebelum loop diatur ke posting pertama di loop dan $post
diatur ke posting terakhir di loop setelah loop, yang diharapkan. Yang saya butuhkan adalah yang $post
diatur ke objek halaman saat ini, yaitu objek yang di-query.
Juga, $wp_the_query->post
dan $wp_query->post
pegang posting pertama di loop dan bukan objek yang diminta seperti pada halaman normal
Saya menggunakan yang berikut (di luar kelas saya ) untuk memeriksa global saya sebelum dan sesudah 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
}
SEBELUM LOOP:
Sebelum loop, masalahnya sebagian diselesaikan dengan menyetel $injectPageIntoLoop
ke true yang menyuntikkan objek halaman sebagai halaman pertama dalam loop. Ini cukup berguna jika Anda perlu menampilkan info halaman sebelum posting yang diminta, tetapi jika Anda tidak menginginkannya, Anda kacau.
Saya dapat memecahkan masalah sebelum loop dengan langsung meretas global, yang saya tidak suka. Saya mengaitkan metode berikut ke wp
dalam preGetPosts
metode saya
public function wp()
{
$page = get_post( $this->pageID );
$GLOBALS['wp_the_query']->post = $page;
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
$GLOBALS['post'] = $page;
}
dan preGetPosts
metode di dalam
add_action( 'wp', [$this, 'wp'] );
Dari ini, $wp_the_query->post
, $wp_query->post
dan $post
semua memegang halaman objek.
SETELAH LOOP
Di sinilah masalah besar saya, setelah loop. Setelah meretas global melalui wp
hook dan metode,
$wp_the_query->post
dan$wp_query->post
diatur kembali ke pos pertama dalam loop, seperti yang diharapkan$post
diatur ke pos terakhir di loop.
Yang saya butuhkan adalah ketiga diatur kembali ke objek tanya / objek halaman saat ini.
Saya telah mencoba mengaitkan wp
metode ke loop_end
tindakan, yang tidak berhasil. Mengaitkan wp
metode dengan get_sidebar
tindakan berhasil, tetapi sudah terlambat.
add_action( 'get_sidebar', [$this, 'wp'] );
Berjalan printGlobals()
langsung setelah loop di templat mengonfirmasi bahwa as $wp_the_query->post
dan $wp_query->post
masih diatur ke pos pertama dan $post
ke pos terakhir.
Saya dapat secara manual menambahkan kode di dalam wp
metode setelah loop di dalam template, tetapi idenya bukan untuk mengubah file template secara langsung karena kelas harus dapat ditransfer dalam plugin antar tema.
Apakah ada cara yang tepat untuk memecahkan masalah ini di mana satu run pre_get_posts
pada halaman yang benar dan halaman depan statis dan masih menjaga integritas $wp_the_query->post
, $wp_query->post
dan $post
( memiliki set mereka ke objek tanya ) sebelum dan sesudah loop.
EDIT
Tampaknya ada kebingungan tentang apa yang saya butuhkan dan mengapa saya membutuhkannya
Apa yang saya butuhkan
Saya perlu mempertahankan nilai $wp_the_query->post
, $wp_query->post
dan $post
di seluruh template, dan nilai itu harus menjadi objek yang ditanyakan. Pada tahap ini, dengan kode yang telah saya posting, nilai-nilai dari ketiga variabel tersebut tidak menampung objek halaman, melainkan memposting objek tulisan dalam loop. Saya harap itu cukup jelas.
Saya telah memposting kode yang dapat Anda gunakan untuk menguji variabel-variabel ini
Kenapa saya membutuhkannya?
Saya membutuhkan cara yang dapat diandalkan untuk menambahkan posting pre_get_posts
ke templat halaman dan halaman depan statis tanpa mengubah fungsionalitas halaman penuh. Pada tahap ini, seperti kode yang dimaksud berdiri, itu merusak fitur breadcrumb saya dan fitur halaman terkait setelah loop karena $post
yang memegang objek posting "salah".
Yang paling penting, saya tidak ingin mengubah template halaman secara langsung. Saya ingin dapat menambahkan posting ke templat halaman tanpa modifikasi APAPUN pada templat