나는 pre_get_posts
실제 페이지와 정적 프론트 페이지에서 사용하는 방법에 대해 상당히 광범위한 연구를 해왔 으며 바보 같은 방법이없는 것 같습니다.
내가 지금까지 찾은 가장 좋은 옵션 은 @birgire의 Stackoverflow 게시물 입니다. 데모 클래스로 다시 작성하고 코드를 좀 더 역동적으로 만들었습니다.
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();
이것은 내 자신의 페이지 매김 기능 을 사용하여 예상대로 잘 작동 합니다 .
문제 :
함수로 인해에 저장된 페이지 객체에 의존하는 다른 함수를 채우는 페이지 무결성을 느슨하게합니다 $post
. $post
before 루프는 루프의 첫 번째 포스트로 $post
설정되고 루프 후 루프의 마지막 포스트로 설정됩니다. 내가 필요한 것은 $post
현재 페이지 객체, 즉 쿼리 된 객체로 설정되어 있다는 것 입니다.
또한, $wp_the_query->post
및 $wp_query->post
일반 페이지와 루프 제 포스트 아니라 질의 물체를 보유
루프 전후에 전역을 확인 하기 위해 다음과 같은 클래스를 사용합니다.
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
}
루프하기 전에 :
루프 전에 $injectPageIntoLoop
페이지 객체를 루프의 첫 번째 페이지로 삽입하는 true 로 설정하면 문제가 부분적으로 해결됩니다 . 요청한 게시물 앞에 페이지 정보를 표시해야하는 경우에 매우 유용하지만 원하지 않는 경우 문제가 발생할 수 있습니다.
글로벌을 직접 해킹하여 루프 전에 문제를 해결할 수 있습니다. wp
내 preGetPosts
방법 안에 다음 방법을 연결합니다.
public function wp()
{
$page = get_post( $this->pageID );
$GLOBALS['wp_the_query']->post = $page;
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
$GLOBALS['post'] = $page;
}
그리고 내부 preGetPosts
방법
add_action( 'wp', [$this, 'wp'] );
이로부터 $wp_the_query->post
, $wp_query->post
그리고 $post
모든 페이지 개체를 보유하고 있습니다.
루프 후
이것은 루프 후 내 큰 문제입니다. wp
후크와 방법을 통해 글로벌을 해킹 한 후
$wp_the_query->post
그리고$wp_query->post
예상대로 루프에서 첫 번째 게시물에 세트 다시이며,$post
루프의 마지막 포스트로 설정됩니다.
내가 필요한 것은 세 가지 모두 쿼리 된 객체 / 현재 페이지 객체로 다시 설정된다는 것입니다.
나는 wp
방법을 loop_end
행동에 연결하려고 시도했지만 작동하지 않습니다. wp
메소드를 get_sidebar
조치에 연결하는 것은 효과가 있지만 너무 늦습니다.
add_action( 'get_sidebar', [$this, 'wp'] );
실행 printGlobals()
으로하는 템플릿 확인한다에서 루프 직후 $wp_the_query->post
와 $wp_query->post
여전히 첫 번째 게시물 및 설정 $post
마지막 게시물로.
wp
템플릿 내부의 루프 후에 메소드 내부에 코드를 수동으로 추가 할 수 있지만 테마는 플러그인 사이에서 클래스를 전송할 수 있으므로 템플릿 파일을 직접 변경하지 않는 것이 좋습니다.
하나의 실행이 문제를 해결할 수있는 적절한 방법이 pre_get_posts
진정한 페이지와 정적 첫 페이지에 여전히이의 무결성 유지 $wp_the_query->post
, $wp_query->post
및 $post
( 조회 된 객체에 그 설정을 갖는 전에 루프 후이).
편집하다
내가 필요한 것과 왜 필요한지에 대해 혼란스러워하는 것 같습니다
내가 필요한 것
나는의 값을 유지해야하는 $wp_the_query->post
, $wp_query->post
그리고 $post
관계없이 템플릿에서, 그 값은 쿼리 개체를해야합니다. 이 단계에서 내가 게시 한 코드 로이 세 변수의 값은 페이지 객체를 보유하지 않고 루프에 게시물의 객체를 게시합니다. 나는 그것이 분명해지기를 바랍니다.
이 변수를 테스트하는 데 사용할 수있는 코드를 게시했습니다
내가 필요한 이유
pre_get_posts
전체 페이지 기능을 변경하지 않고 페이지 템플리트 및 정적 프론트 페이지 에 게시물을 추가 할 수있는 안정적인 방법이 필요 합니다. 이 단계에서 문제의 코드가 서서 $post
"잘못된"포스트 오브젝트를 보유하고 있기 때문에 루프 후 내 이동 경로 기능 및 관련 페이지 기능을 중단 합니다.
무엇보다도, 페이지 템플릿을 직접 변경하고 싶지 않습니다. 나는없이 페이지 템플릿에 게시물을 추가 할 수 있도록 모든 템플릿에 수정