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
sumber
Jawaban:
Saya akhirnya berhasil, tetapi tidak dengan kode dalam pertanyaan saya. Saya benar-benar membatalkan seluruh gagasan itu dan memulai lagi dengan arah yang baru.
CATATAN:
Jika ada yang bisa menyelesaikan masalah dalam pertanyaan saya, jangan ragu untuk mengirim jawaban. Juga, jika Anda memiliki solusi lain, jangan ragu untuk mengirim jawaban.
KELAS DAN SOLUSI YANG DITINJAU KEMBALI:
Apa yang saya coba lakukan di sini adalah menggunakan post injection, daripada sepenuhnya mengubah kueri utama dan terjebak dengan semua masalah di atas, termasuk (a) secara langsung mengubah global, (b) berlari ke masalah nilai global, dan (c) menugaskan kembali template halaman.
Dengan menggunakan pasca injeksi, saya mampu menjaga integritas posting penuh, jadi
$wp_the_query->post
,$wp_query->post
,$posts
dan$post
tinggal konstan sepanjang template. Masing-masing variabel referensi objek halaman saat ini (seperti halnya dengan halaman sebenarnya). Dengan cara ini, fungsi seperti remah roti tahu bahwa halaman saat ini adalah halaman yang benar dan bukan semacam arsip.Saya harus sedikit mengubah kueri utama ( melalui filter dan tindakan ) untuk menyesuaikan pagination, tapi kami akan sampai pada itu.
QUERY INJECTION POST
Untuk menyelesaikan injeksi pos, saya menggunakan kueri khusus untuk mengembalikan posting yang diperlukan untuk injeksi. Saya juga menggunakan
$found_pages
properti kueri khusus untuk menyesuaikan properti dari kueri utama agar pagination berfungsi dari kueri utama. Posting disuntikkan ke dalam kueri utama melaluiloop_end
tindakan.Untuk membuat kueri khusus dapat diakses dan dapat digunakan di luar kelas, saya memperkenalkan beberapa tindakan.
Kait Pagination untuk menghubungkan fungsi pagination:
pregetgostsforgages_before_loop_pagination
pregetgostsforgages_after_loop_pagination
Penghitung kustom yang menghitung pos dalam loop. Tindakan ini dapat digunakan untuk mengubah cara posting ditampilkan di dalam loop sesuai dengan nomor posting.
pregetgostsforgages_counter_before_template_part
pregetgostsforgages_counter_after_template_part
Kait umum untuk mengakses objek permintaan dan objek posting saat ini
pregetgostsforgages_current_post_and_object
Kait ini memberi Anda pengalaman lepas tangan total, karena Anda tidak perlu mengubah apa pun di templat laman itu sendiri, yang merupakan niat awal saya sejak awal. Halaman sepenuhnya dapat diubah dari plugin atau file fungsi, yang membuat solusi ini sangat dinamis.
Saya juga telah menggunakan
get_template_part()
untuk memuat bagian templat, yang akan digunakan untuk menampilkan tulisan. Sebagian besar tema saat ini menggunakan bagian templat, yang membuatnya sangat berguna di kelas. Jika menggunakan tema Andacontent.php
, Anda hanya bisa lewatcontent
ke$templatePart
bebancontent.php
.Jika Anda membutuhkan dukungan pasca format untuk bagian-bagian Template, mudah - Anda hanya bisa lewat
content
ke$templatePart
dan set$postFormatSupport
ketrue
. Akibatnya, bagian templatcontent-video.php
akan dimuat untuk pos dengan format posvideo
.QUERY UTAMA
Perubahan berikut dilakukan untuk kueri utama melalui masing-masing filter dan tindakan:
Untuk menjeda pertanyaan utama:
Nilai
$found_posts
properti kueri injector diteruskan ke nilai objek kueri utama melaluifound_posts
filter.Nilai parameter yang dilewati pengguna
posts_per_page
diatur ke kueri utamapre_get_posts
.$max_num_pages
dihitung menggunakan jumlah posting di$found_posts
danposts_per_page
. Karenais_singular
benar pada halaman, itu menghambatLIMIT
klausa yang ditetapkan. Cukup menetapkanis_singular
ke false menyebabkan beberapa masalah, jadi saya memutuskan untuk mengaturLIMIT
klausa melaluipost_limits
filter. Aku terusoffset
dariLIMIT
klausul set untuk0
menghindari 404 pada halaman dengan pagination dihidupkan.Ini menangani pagination dan masalah apa pun yang mungkin timbul dari injeksi postingan.
TUJUAN HALAMAN
Objek halaman saat ini tersedia untuk ditampilkan sebagai posting dengan menggunakan loop default pada halaman, pisahkan dan di atas posting yang disuntikkan. Jika Anda tidak membutuhkan ini, Anda dapat mengatur
$removePageFromLoop
ke true, dan ini akan menyembunyikan konten halaman agar tidak ditampilkan.Pada tahap ini, saya menggunakan CSS untuk menyembunyikan objek halaman melalui
loop_start
danloop_end
tindakan karena saya tidak dapat menemukan cara lain untuk melakukan ini. Kelemahan dari metode ini adalah segala sesuatu yang dikaitkan denganthe_post
action hook di dalam permintaan utama juga akan disembunyikan.KELAS
The
PreGetPostsForPages
kelas dapat ditingkatkan dan harus benar-namespace juga. Meskipun Anda bisa menjatuhkan ini di file fungsi tema Anda, akan lebih baik untuk memasukkan ini ke dalam plugin khusus.Gunakan, modifikasi, dan penyalahgunaan sesuai keinginan Anda. Kode ini dikomentari dengan baik, sehingga harus mudah diikuti dan disesuaikan
PEMAKAIAN
Anda sekarang dapat memulai kelas ( juga di plugin atau file fungsi ) sebagai tindak lanjut untuk menargetkan halaman dengan ID 251, di mana kami akan menampilkan 2 posting per halaman dari
post
jenis posting.MENAMBAH PAGINASI DAN GAYA KUSTOM
Seperti yang saya sebutkan sebelumnya, ada beberapa tindakan dalam permintaan injector untuk menambahkan pagination dan / atau styling kustom.
Dalam contoh berikut, saya menambahkan pagination setelah loop menggunakan fungsi pagination saya sendiri dari jawaban yang ditautkan . Juga, menggunakan penghitung khusus saya, saya menambahkan
<div>
untuk menampilkan posting saya dalam dua kolom.Berikut adalah tindakan yang saya gunakan
Perhatikan bahwa pagination diatur oleh kueri utama, bukan kueri injektor, jadi fungsi bawaan seperti
the_posts_pagination()
juga harus berfungsi.Ini adalah hasil akhirnya
HALAMAN DEPAN STATIS
Semuanya berfungsi seperti yang diharapkan pada halaman depan statis bersama dengan fungsi pagination saya tanpa memerlukan modifikasi lebih lanjut.
KESIMPULAN
Ini mungkin tampak seperti banyak overhead, dan mungkin saja, tetapi pro lebih besar daripada waktu besar con.
PRO BESAR
Anda tidak perlu mengubah template halaman untuk halaman tertentu dengan cara apa pun. Ini membuat semuanya dinamis dan dapat dengan mudah ditransfer antar tema tanpa membuat modifikasi pada kode apa pun, selama semuanya dilakukan dalam sebuah plugin.
Paling-paling, Anda hanya perlu membuat
content.php
bagian template di tema Anda jika tema Anda belum memilikinya.Setiap pagination yang berfungsi pada kueri utama akan berfungsi pada halaman tanpa jenis perubahan apa pun atau tambahan apa pun dari kueri yang diteruskan ke fungsi.
Ada lebih banyak pro yang tidak dapat saya pikirkan sekarang, tetapi ini adalah yang penting.
sumber