실제 페이지 및 정적 프론트 페이지에서 pre_get_posts 사용


19

나는 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. $postbefore 루프는 루프의 첫 번째 포스트로 $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 로 설정하면 문제가 부분적으로 해결됩니다 . 요청한 게시물 앞에 페이지 정보를 표시해야하는 경우에 매우 유용하지만 원하지 않는 경우 문제가 발생할 수 있습니다.

글로벌을 직접 해킹하여 루프 전에 문제를 해결할 수 있습니다. wppreGetPosts방법 안에 다음 방법을 연결합니다.

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"잘못된"포스트 오브젝트를 보유하고 있기 때문에 루프 후 내 이동 경로 기능 및 관련 페이지 기능을 중단 합니다.

무엇보다도, 페이지 템플릿을 직접 변경하고 싶지 않습니다. 나는없이 페이지 템플릿에 게시물을 추가 할 수 있도록 모든 템플릿에 수정


무엇을하려고합니까, 목표 또는 기능적 요구 사항입니까? 내가 말할 수있는 한 어디에도 언급하지 않습니다.
adelval

답변:


13

마침내 작동하지만 내 질문의 코드로는 작동하지 않습니다. 나는 그 아이디어를 완전히 없애고 새로운 방향으로 나아 가기 시작했다.

노트:

누구나 내 질문에있는 문제를 정리할 수 있다면 언제든지 답변을 게시하십시오. 또한 다른 해결책이 있으면 언제든지 답변을 게시하십시오.

개선 된 수업 및 솔루션 :

여기서 시도한 것은 기본 쿼리를 완전히 변경하고 (a) 전역을 직접 변경하고, (b) 글로벌 가치 문제를 겪고, (c) 페이지 템플릿 재 할당

포스트 분사를 사용하여, 나는 전체 게시물의 무결성, 그래서 계속 할 수있어 $wp_the_query->post, $wp_query->post, $posts$post템플릿에 걸쳐 체류 일정. 이러한 각 변수는 실제 페이지와 마찬가지로 현재 페이지 객체를 참조합니다. 이런 방식으로 사이트 이동 경로와 같은 기능은 현재 페이지가 일종의 아카이브가 아니라 실제 페이지라는 것을 알고 있습니다.

페이지 매김을 조정하기 위해 기본 쿼리를 약간 ( 필터 및 작업을 통해) 변경해야 했지만 우리는 그 결과를 보게 될 것입니다.

주입 후 쿼리

주입 후 작업을 수행하기 위해 사용자 지정 쿼리를 사용하여 주입에 필요한 기둥을 반환했습니다. 또한 사용자 지정 쿼리의 $found_pages속성을 사용하여 기본 쿼리의 페이지 매김을 수행하기 위해 기본 쿼리의 속성을 조정했습니다. loop_end조치를 통해 게시물이 기본 쿼리에 삽입됩니다 .

클래스 외부에서 사용자 지정 쿼리에 액세스하고 사용할 수 있도록하기 위해 몇 가지 작업을 소개했습니다.

  • 페이지 매김 함수를 연결하기위한 페이지 매김 후크 :

    • pregetgostsforgages_before_loop_pagination

    • pregetgostsforgages_after_loop_pagination

  • 루프에서 게시물을 계산하는 사용자 정의 카운터 . 이 조치는 포스트 번호에 따라 루프 내에서 포스트가 표시되는 방식을 변경하는 데 사용될 수 있습니다.

    • pregetgostsforgages_counter_before_template_part

    • pregetgostsforgages_counter_after_template_part

  • 쿼리 개체 및 현재 게시물 개체에 액세스하기위한 일반 후크

    • pregetgostsforgages_current_post_and_object

이 훅은 페이지 템플릿 자체에서 아무것도 바꿀 필요가 없기 때문에 처음부터 원래 의도였던 전체적인 경험을 제공합니다. 플러그인 또는 함수 파일에서 페이지를 완전히 변경할 수 있으므로이 솔루션을 매우 동적으로 만들 수 있습니다.

또한 get_template_part()게시물을 표시하는 데 사용되는 템플릿 부분을로드하는 데 사용했습니다. 오늘날 대부분의 테마는 템플릿 부분을 사용하므로 수업에서 매우 유용합니다. 테마가 사용하는 경우 load로 content.php전달 content하기 만하면 됩니다.$templatePartcontent.php

당신은 템플릿 부품 포스트 형식 지원이 필요한 경우, 그것은 간단합니다 - 당신은 단순히 전달할 수 content$templatePart및 설정 $postFormatSupport에를 true. 결과적으로 content-video.php게시물 형식이 게시물 인 경우 템플릿 부분 이로드됩니다 video.

메인 쿼리

각 필터와 작업을 통해 기본 쿼리가 다음과 같이 변경되었습니다.

  • 기본 쿼리를 페이지 매기기 :

    • 인젝터 쿼리의 $found_posts속성 값은 found_posts필터를 통해 기본 쿼리 개체 의 속성 값으로 전달됩니다 .

    • 사용자가 전달한 매개 변수의 값 posts_per_page은를 통해 기본 쿼리로 설정됩니다 pre_get_posts.

    • $max_num_pages포스트에서의 양을 사용하여 계산 $found_posts 하고 posts_per_page. is_singular페이지에서 true 이므로 LIMIT절이 설정되지 않습니다. 단순히 is_singularfalse로 설정 하면 몇 가지 문제가 발생하므로 필터를 LIMIT통해 절 을 설정하기로 결정했습니다 post_limits. 나는 유지 offsetLIMIT에 절 세트를 0켜 매김있는 페이지에 404 개의를 피하기 위해.

이것은 페이지 매김과 사후 주입에서 발생할 수있는 모든 문제를 처리합니다.

페이지 객체

현재 페이지 객체는 페이지에서 기본 루프를 사용하여 주입 된 게시물의 상단과 분리 된 게시물로 표시 할 수 있습니다. 필요하지 않은 경우 간단히 $removePageFromLooptrue로 설정 하면 페이지 내용이 표시되지 않습니다.

이 단계에서는 CSS를 사용하여 다른 방법을 찾을 수 없으므로 loop_startloop_end작업을 통해 페이지 객체를 숨기고 있습니다. 이 방법의 단점 the_post은 기본 쿼리 내부의 작업 후크에 연결된 모든 항목 도 숨겨집니다.

클래스

PreGetPostsForPages클래스는 개선 될 수 있고 제대로뿐만 아니라 네임 스페이스되어야한다. 테마의 함수 파일에 간단히 넣을 수는 있지만 사용자 정의 플러그인에 놓는 것이 좋습니다.

적합하다고 생각되면 사용, 수정 및 남용하십시오. 코드는 잘 주석 처리되어 있으므로 따르기 쉽고 조정하기 쉬워야합니다

class PreGetPostsForPages
{
    /**
     * @var string|int $pageID
     * @access protected     
     * @since 1.0.0
     */
    protected $pageID;

    /**
     * @var string $templatePart
     * @access protected     
     * @since 1.0.0
     */
    protected $templatePart;

    /**
     * @var bool $postFormatSupport
     * @access protected     
     * @since 1.0.0
     */
    protected $postFormatSupport;

    /**
     * @var bool $removePageFromLoop
     * @access protected     
     * @since 1.0.0
     */
    protected $removePageFromLoop;

    /**
     * @var array $args
     * @access protected     
     * @since 1.0.0
     */
    protected $args;

    /**
     * @var array $mergedArgs
     * @access protected     
     * @since 1.0.0
     */
    protected $mergedArgs = [];

    /**
     * @var NULL|\stdClass $injectorQuery
     * @access protected     
     * @since 1.0.0
     */
    protected $injectorQuery = NULL;

    /**
     * @var int $validatedPageID
     * @access protected     
     * @since 1.0.0
     */
    protected $validatedPageID = 0;

    /** 
     * Constructor method
     *
     * @param string|int $pageID The ID of the page we would like to target
     * @param string $templatePart The template part which should be used to display posts
     * @param string $postFormatSupport Should get_template_part support post format specific template parts
     * @param bool $removePageFromLoop Should the page content be displayed or not
     * @param array $args An array of valid arguments compatible with WP_Query
     *
     * @since 1.0.0
     */      
    public function __construct( 
        $pageID             = NULL,
        $templatePart       = NULL,
        $postFormatSupport  = false,
        $removePageFromLoop = false,
        $args               = [] 
    ) {
        $this->pageID             = $pageID;
        $this->templatePart       = $templatePart;
        $this->postFormatSupport  = $postFormatSupport;
        $this->removePageFromLoop = $removePageFromLoop;
        $this->args               = $args;
    }

    /**
     * Public method init()
     *
     * The init method will be use to initialize our pre_get_posts action
     *
     * @since 1.0.0
     */
    public function init()
    {
        // Initialise our pre_get_posts action
        add_action( 'pre_get_posts', [$this, 'preGetPosts'] );
    }

    /**
     * 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;
    }

    /**
     * Private method mergedArgs()
     *
     * Merge the default args with the user passed args
     *
     * @since 1.0.0
     */
    private function mergedArgs()
    {
        // Set default arguments
        if ( get_query_var( 'paged' ) ) {
            $currentPage = get_query_var( 'paged' );
        } elseif ( get_query_var( 'page' ) ) {
            $currentPage = get_query_var( 'page' );
        } else {
            $currentPage = 1;
        }
        $default = [
            'suppress_filters'    => true,
            'ignore_sticky_posts' => 1,
            'paged'               => $currentPage,
            'posts_per_page'      => get_option( 'posts_per_page' ), // Set posts per page here to set the LIMIT clause etc
            'nopaging'            => false
        ];    
        $mergedArgs = wp_parse_args( (array) $this->args, $default );
        $this->mergedArgs = $mergedArgs;
    }

    /**
     * Public method preGetPosts()
     *
     * This is the callback method which will be hooked to the 
     * pre_get_posts action hook. This method will be used to alter
     * the main query on the page specified by ID.
     *
     * @param \stdClass WP_Query The query object passed by reference
     * @since 1.0.0
     */
    public function preGetPosts( \WP_Query $q )
    {
        if (    !is_admin() // Only target the front end
             && $q->is_main_query() // Only target the main query
             && $q->is_page( filter_var( $this->validatedPageID, FILTER_VALIDATE_INT ) ) // Only target our specified page
        ) {
            // Remove the pre_get_posts action to avoid unexpected issues
            remove_action( current_action(), [$this, __METHOD__] );

            // METHODS:
            // Initialize our method which will return the validated page ID
            $this->validatePageID();
            // Initiale our mergedArgs() method
            $this->mergedArgs();
            // Initiale our custom query method
            $this->injectorQuery();

            /**
             * We need to alter a couple of things here in order for this to work
             * - Set posts_per_page to the user set value in order for the query to
             *   to properly calculate the $max_num_pages property for pagination
             * - Set the $found_posts property of the main query to the $found_posts
             *   property of our custom query we will be using to inject posts
             * - Set the LIMIT clause to the SQL query. By default, on pages, `is_singular` 
             *   returns true on pages which removes the LIMIT clause from the SQL query.
             *   We need the LIMIT clause because an empty limit clause inhibits the calculation
             *   of the $max_num_pages property which we need for pagination
             */
            if (    $this->mergedArgs['posts_per_page'] 
                 && true !== $this->mergedArgs['nopaging']
            ) {
                $q->set( 'posts_per_page', $this->mergedArgs['posts_per_page'] );
            } elseif ( true === $this->mergedArgs['nopaging'] ) {
                $q->set( 'posts_per_page', -1 );
            }

            // FILTERS:
            add_filter( 'found_posts', [$this, 'foundPosts'], PHP_INT_MAX, 2 );
            add_filter( 'post_limits', [$this, 'postLimits']);

            // ACTIONS:
            /**
             * We can now add all our actions that we will be using to inject our custom
             * posts into the main query. We will not be altering the main query or the 
             * main query's $posts property as we would like to keep full integrity of the 
             * $post, $posts globals as well as $wp_query->post. For this reason we will use
             * post injection
             */     
            add_action( 'loop_start', [$this, 'loopStart'], 1 );
            add_action( 'loop_end',   [$this, 'loopEnd'],   1 );
        }    
    }    

    /**
     * Public method injectorQuery
     *
     * This will be the method which will handle our custom
     * query which will be used to 
     * - return the posts that should be injected into the main
     *   query according to the arguments passed
     * - alter the $found_posts property of the main query to make
     *   pagination work 
     *
     * @link https://codex.wordpress.org/Class_Reference/WP_Query
     * @since 1.0.0
     * @return \stdClass $this->injectorQuery
     */
    public function injectorQuery()
    {
        //Define our custom query
        $injectorQuery = new \WP_Query( $this->mergedArgs );

        // Update the thumbnail cache
        update_post_thumbnail_cache( $injectorQuery );

        $this->injectorQuery = $injectorQuery;

        return $this->injectorQuery;
    }

    /**
     * Public callback method foundPosts()
     * 
     * We need to set found_posts in the main query to the $found_posts
     * property of the custom query in order for the main query to correctly 
     * calculate $max_num_pages for pagination
     *
     * @param string $found_posts Passed by reference by the filter
     * @param stdClass \WP_Query Sq The current query object passed by refence
     * @since 1.0.0
     * @return $found_posts
     */
    public function foundPosts( $found_posts, \WP_Query $q )
    {
        if ( !$q->is_main_query() )
            return $found_posts;

        remove_filter( current_filter(), [$this, __METHOD__] );

        // Make sure that $this->injectorQuery actually have a value and is not NULL
        if (    $this->injectorQuery instanceof \WP_Query 
             && 0 != $this->injectorQuery->found_posts
        )
            return $found_posts = $this->injectorQuery->found_posts;

        return $found_posts;
    }

    /**
     * Public callback method postLimits()
     *
     * We need to set the LIMIT clause as it it is removed on pages due to 
     * is_singular returning true. Witout the limit clause, $max_num_pages stays
     * set 0 which avoids pagination. 
     *
     * We will also leave the offset part of the LIMIT cluase to 0 to avoid paged
     * pages returning 404's
     *
     * @param string $limits Passed by reference in the filter
     * @since 1.0.0
     * @return $limits
     */
    public function postLimits( $limits )
    {
        $posts_per_page = (int) $this->mergedArgs['posts_per_page'];
        if (    $posts_per_page
             && -1   !=  $posts_per_page // Make sure that posts_per_page is not set to return all posts
             && true !== $this->mergedArgs['nopaging'] // Make sure that nopaging is not set to true
        ) {
            $limits = "LIMIT 0, $posts_per_page"; // Leave offset at 0 to avoid 404 on paged pages
        }

        return $limits;
    }

    /**
     * Public callback method loopStart()
     *
     * Callback function which will be hooked to the loop_start action hook
     *
     * @param \stdClass \WP_Query $q Query object passed by reference
     * @since 1.0.0
     */
    public function loopStart( \WP_Query $q )
    {
        /**
         * Although we run this action inside our preGetPosts methods and
         * and inside a main query check, we need to redo the check here aswell
         * because failing to do so sets our div in the custom query output as well
         */

        if ( !$q->is_main_query() )
            return;

        /** 
         * Add inline style to hide the page content from the loop
         * whenever $removePageFromLoop is set to true. You can
         * alternatively alter the page template in a child theme by removing
         * everything inside the loop, but keeping the loop
         * Example of how your loop should look like:
         *     while ( have_posts() ) {
         *     the_post();
         *         // Add nothing here
         *     }
         */
        if ( true === $this->removePageFromLoop )
            echo '<div style="display:none">';
    }   

    /**
     * Public callback method loopEnd()
     *
     * Callback function which will be hooked to the loop_end action hook
     *
     * @param \stdClass \WP_Query $q Query object passed by reference
     * @since 1.0.0
     */
    public function loopEnd( \WP_Query $q )
    {  
        /**
         * Although we run this action inside our preGetPosts methods and
         * and inside a main query check, we need to redo the check here as well
         * because failing to do so sets our custom query into an infinite loop
         */
        if ( !$q->is_main_query() )
            return;

        // See the note in the loopStart method  
        if ( true === $this->removePageFromLoop )
            echo '</div>';

        //Make sure that $this->injectorQuery actually have a value and is not NULL
        if ( !$this->injectorQuery instanceof \WP_Query )
            return; 

        // Setup a counter as wee need to run the custom query only once    
        static $count = 0;    

        /**
         * Only run the custom query on the first run of the loop. Any consecutive
         * runs (like if the user runs the loop again), the custom posts won't show.
         */
        if ( 0 === (int) $count ) {      
            // We will now add our custom posts on loop_end
            $this->injectorQuery->rewind_posts();

            // Create our loop
            if ( $this->injectorQuery->have_posts() ) {

                /**
                 * Fires before the loop to add pagination.
                 *
                 * @since 1.0.0
                 *
                 * @param \stdClass $this->injectorQuery Current object (passed by reference).
                 */
                do_action( 'pregetgostsforgages_before_loop_pagination', $this->injectorQuery );


                // Add a static counter for those who need it
                static $counter = 0;

                while ( $this->injectorQuery->have_posts() ) {
                    $this->injectorQuery->the_post(); 

                    /**
                     * Fires before get_template_part.
                     *
                     * @since 1.0.0
                     *
                     * @param int $counter (passed by reference).
                     */
                    do_action( 'pregetgostsforgages_counter_before_template_part', $counter );

                    /**
                     * Fires before get_template_part.
                     *
                     * @since 1.0.0
                     *
                     * @param \stdClass $this->injectorQuery-post Current post object (passed by reference).
                     * @param \stdClass $this->injectorQuery Current object (passed by reference).
                     */
                    do_action( 'pregetgostsforgages_current_post_and_object', $this->injectorQuery->post, $this->injectorQuery );

                    /** 
                     * Load our custom template part as set by the user
                     * 
                     * We will also add template support for post formats. If $this->postFormatSupport
                     * is set to true, get_post_format() will be automatically added in get_template part
                     *
                     * If you have a template called content-video.php, you only need to pass 'content'
                     * to $template part and then set $this->postFormatSupport to true in order to load
                     * content-video.php for video post format posts
                     */
                    $part = '';
                    if ( true === $this->postFormatSupport )
                        $part = get_post_format( $this->injectorQuery->post->ID ); 

                    get_template_part( 
                        filter_var( $this->templatePart, FILTER_SANITIZE_STRING ), 
                        $part
                    );

                    /**
                     * Fires after get_template_part.
                     *
                     * @since 1.0.0
                     *
                     * @param int $counter (passed by reference).
                     */
                    do_action( 'pregetgostsforgages_counter_after_template_part', $counter );

                    $counter++; //Update the counter
                }

                wp_reset_postdata();

                /**
                 * Fires after the loop to add pagination.
                 *
                 * @since 1.0.0
                 *
                 * @param \stdClass $this->injectorQuery Current object (passed by reference).
                 */
                do_action( 'pregetgostsforgages_after_loop_pagination', $this->injectorQuery );
            }
        }

        // Update our static counter
        $count++;       
    }
}  

용법

이제 다음과 같이 클래스 ( 플러그인 또는 함수 파일에서 )를 시작하여 ID가 ​​251 인 페이지를 대상으로 할 수 post있습니다. 그러면 게시물 유형 에서 페이지 당 2 개의 게시물이 표시됩니다 .

$query = new PreGetPostsForPages(
    251,       // Page ID we will target
    'content', //Template part which will be used to display posts, name should be without .php extension 
    true,      // Should get_template_part support post formats
    false,     // Should the page object be excluded from the loop
    [          // Array of valid arguments that will be passed to WP_Query/pre_get_posts
        'post_type'      => 'post', 
        'posts_per_page' => 2
    ] 
);
$query->init(); 

페이지 매김 및 맞춤 스타일링 추가

앞에서 언급했듯이 페이지 매김 및 / 또는 사용자 지정 스타일을 추가하기 위해 인젝터 쿼리에 몇 가지 작업이 있습니다.

다음 예제에서는 링크 된 답변에서 내 페이지 매김 함수를 사용하여 루프 후에 페이지 매김을 추가했습니다 . 또한 내 맞춤 카운터를 사용하여 <div>게시물을 두 열로 표시 하기 위해를 추가했습니다 .

내가 사용한 행동은 다음과 같습니다.

add_action( 'pregetgostsforgages_counter_before_template_part', function ( $counter )
{
    $class = $counter%2  ? ' right' : ' left';
    echo '<div class="entry-column' . $class . '">';
});

add_action( 'pregetgostsforgages_counter_after_template_part', function ( $counter )
{
    echo '</div>';
});

add_action( 'pregetgostsforgages_after_loop_pagination', function ( \WP_Query $q )
{
    paginated_numbers();    
});

페이지 매김은 인젝터 쿼리가 아닌 기본 쿼리에 의해 설정되므로 내장 함수 the_posts_pagination()도 작동해야합니다.

이것이 최종 결과입니다

여기에 이미지 설명을 입력하십시오

정적 프론트 페이지

추가 수정없이 페이지 매김 함수와 함께 정적 프론트 페이지에서 모든 것이 예상대로 작동합니다.

결론

이것은 많은 오버 헤드처럼 보일지 모르지만 그럴 수도 있지만 프로는 콘의 큰 시간을 능가합니다.

큰 프로

  • 어떤 방식 으로든 특정 페이지의 페이지 템플리트를 변경할 필요가 없습니다. 이것은 플러그인에서 모든 것이 수행되는 한 코드를 수정하지 않고도 모든 것을 동적으로 만들고 테마간에 쉽게 전송할 수 있습니다.

  • content.php테마에 아직 템플릿이없는 경우 테마에 템플릿 파트 만 작성하면됩니다 .

  • 기본 쿼리에서 작동하는 페이지 매김은 페이지에서 변경되지 않거나 쿼리에서 기능에 전달 된 추가 항목없이 페이지에서 작동합니다.

내가 지금 생각할 수없는 더 많은 프로들이 있지만, 이것들은 중요한 것들입니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.