Sử dụng pre_get_posts trên trang thật và trang trước tĩnh


19

Tôi đã thực hiện nghiên cứu khá rộng rãi về cách sử dụng pre_get_poststrên các trang thật và các trang đầu tĩnh và dường như không có phương pháp chứng minh ngu ngốc nào.

Tùy chọn tốt nhất tôi tìm thấy cho đến nay là từ một bài đăng được thực hiện bởi @birgire trên Stackoverflow . Tôi đã viết lại nó thành một lớp demo và làm cho mã động hơn một chút

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();

Điều này hoạt động tốt và trang như mong đợi bằng cách sử dụng chức năng phân trang của riêng tôi .

VẤN ĐỀ:

Do chức năng, tôi mất tính toàn vẹn trang, thứ chứa các chức năng khác dựa vào đối tượng trang được lưu trữ $post. $posttrước khi vòng lặp được đặt thành bài đầu tiên trong vòng lặp và $postđược đặt thành bài cuối cùng trong vòng lặp sau vòng lặp, dự kiến. Những gì tôi cần là $postđược đặt cho đối tượng trang hiện tại, tức là đối tượng được truy vấn.

Ngoài ra, $wp_the_query->post$wp_query->postgiữ bài đăng đầu tiên trong vòng lặp chứ không phải đối tượng được truy vấn như trên một trang bình thường

Tôi sử dụng như sau ( bên ngoài lớp học của tôi ) để kiểm tra toàn cầu của tôi trước và sau vòng lặp

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
}

TRƯỚC KHI LOOP:

Trước vòng lặp, vấn đề được giải quyết một phần bằng cách đặt $injectPageIntoLoopthành true mà tiêm đối tượng trang làm trang đầu tiên trong vòng lặp. Điều này khá hữu ích nếu bạn cần hiển thị thông tin trang trước các bài đăng được yêu cầu, nhưng nếu bạn không muốn điều đó, bạn sẽ bị lừa.

Tôi có thể giải quyết vấn đề trước vòng lặp bằng cách trực tiếp hack toàn cầu, điều mà tôi không thực sự thích. Tôi móc phương thức sau vào wpbên trong preGetPostsphương thức của mình

public function wp()
{
    $page                          = get_post( $this->pageID );
    $GLOBALS['wp_the_query']->post = $page;
    $GLOBALS['wp_query']           = $GLOBALS['wp_the_query'];
    $GLOBALS['post']               = $page;
}

preGetPostsphương pháp bên trong

add_action( 'wp', [$this, 'wp'] );

Từ đó, $wp_the_query->post, $wp_query->post$posttất cả các tổ chức đối tượng trang.

SAU KHI LOOP

Đây là vấn đề lớn của tôi, sau vòng lặp. Sau khi hack toàn cầu thông qua wphook và phương thức,

  • $wp_the_query->post$wp_query->postđược đặt trở lại bài đăng đầu tiên trong vòng lặp, như mong đợi

  • $post được đặt đến bài cuối cùng trong vòng lặp.

Điều tôi cần là cả ba đều được đặt trở lại đối tượng được truy vấn / đối tượng trang hiện tại.

Tôi đã thử nối wpphương thức thành loop_endhành động, không hoạt động. Kết nối wpphương thức để get_sidebarhành động hoạt động, nhưng đã quá muộn.

add_action( 'get_sidebar', [$this, 'wp'] );

Chạy printGlobals()trực tiếp sau vòng lặp trong mẫu xác nhận rằng $wp_the_query->post$wp_query->postvẫn được đặt thành bài đầu tiên và $postbài cuối cùng.

Tôi có thể tự thêm mã bên trong wpphương thức sau vòng lặp bên trong mẫu, nhưng ý tưởng không phải là thay đổi trực tiếp các tệp mẫu vì lớp có thể được chuyển trong một plugin giữa các chủ đề.

Có cách nào thích hợp để giải quyết vấn đề này, nơi một lần chạy thử pre_get_poststrên một trang đúng và trang tĩnh và vẫn giữ sự toàn vẹn của $wp_the_query->post, $wp_query->post$post( có những thiết lập để các đối tượng truy vấn ) trước và sau vòng lặp.

CHỈNH SỬA

Dường như có sự nhầm lẫn về những gì tôi cần và tại sao tôi cần nó

Tôi cân gi

Tôi cần phải giữ lại các giá trị của $wp_the_query->post, $wp_query->post$posttrên mẫu không phân biệt, và giá trị mà nên là đối tượng truy vấn. Ở giai đoạn này, với mã tôi đã đăng, các giá trị của ba biến đó không giữ đối tượng trang, mà là đăng các đối tượng của bài đăng trong vòng lặp. Tôi hy vọng điều đó là đủ rõ ràng.

Tôi đã đăng mã mà bạn có thể sử dụng để kiểm tra các biến này

Tại sao tôi cần nó

Tôi cần một cách đáng tin cậy để thêm bài đăng qua các pre_get_postsmẫu trang và trang trước tĩnh mà không thay đổi chức năng trang đầy đủ. Ở giai đoạn này, khi mã trong câu hỏi đứng, nó phá vỡ tính năng Breadcrumb của tôi và tính năng trang có liên quan sau vòng lặp do $postchứa đối tượng bài "sai".

Trên hết, tôi không muốn thay đổi mẫu trang trực tiếp. Tôi muốn có thể thêm bài viết vào một mẫu trang mà không cần sửa đổi bất kỳ mẫu nào


Bạn đang cố gắng làm gì, mục tiêu hay yêu cầu chức năng của bạn là gì? Bạn không nói nó ở bất cứ đâu xa như tôi có thể nói.
adelval

Câu trả lời:


13

Cuối cùng tôi đã làm cho nó hoạt động, nhưng không phải với mã trong câu hỏi của tôi. Tôi hoàn toàn loại bỏ toàn bộ ý tưởng đó và khởi động lại đi theo một hướng mới.

CHÚ THÍCH:

Nếu bất cứ ai cũng có thể sắp xếp các vấn đề trong câu hỏi của tôi, vui lòng gửi câu trả lời. Ngoài ra, nếu bạn có bất kỳ giải pháp nào khác, vui lòng gửi câu trả lời.

LỚP VÀ GIẢI PHÁP:

Những gì tôi đã cố gắng làm ở đây là sử dụng bài tiêm, thay vì thay đổi hoàn toàn truy vấn chính và bị mắc kẹt với tất cả các vấn đề trên, bao gồm (a) thay đổi trực tiếp toàn cầu, (b) chạy vào vấn đề giá trị toàn cầu và (c) chỉ định lại các mẫu trang.

Bằng việc sử dụng bài thuốc tiêm, tôi có thể để giữ cho bài trọn vẹn, vì vậy $wp_the_query->post, $wp_query->post, $posts$postnghỉ liên tục trong suốt mẫu. Mỗi biến này tham chiếu đến đối tượng trang hiện tại (như trường hợp của các trang thật). Bằng cách này, các chức năng như mẩu bánh mì biết rằng trang hiện tại là một trang thực sự chứ không phải là một loại lưu trữ.

Tôi đã phải thay đổi truy vấn chính một chút ( thông qua các bộ lọc và hành động ) để điều chỉnh phân trang, nhưng chúng ta sẽ đến đó.

BÀI VIẾT BẮT ĐẦU

Để thực hiện bài tiêm, tôi đã sử dụng một truy vấn tùy chỉnh để trả về các bài đăng cần thiết để tiêm. Tôi cũng đã sử dụng thuộc $found_pagestính của truy vấn tùy chỉnh để điều chỉnh truy vấn chính để phân trang hoạt động từ truy vấn chính. Bài viết được đưa vào truy vấn chính thông qua loop_endhành động.

Để làm cho truy vấn tùy chỉnh có thể truy cập và có thể sử dụng bên ngoài lớp, tôi đã giới thiệu một vài hành động.

  • Móc nối để nối các chức năng phân trang:

    • pregetgostsforgages_before_loop_pagination

    • pregetgostsforgages_after_loop_pagination

  • Bộ đếm tùy chỉnh đếm các bài viết trong vòng lặp. Những hành động này có thể được sử dụng để thay đổi cách bài viết được hiển thị bên trong vòng lặp theo số bài đăng.

    • pregetgostsforgages_counter_before_template_part

    • pregetgostsforgages_counter_after_template_part

  • Móc chung để truy cập đối tượng truy vấn và đối tượng bài hiện tại

    • pregetgostsforgages_current_post_and_object

Những cái móc này mang lại cho bạn trải nghiệm thực tế, vì bạn không cần phải thay đổi bất cứ điều gì trong chính mẫu trang, đó là ý định ban đầu của tôi từ đầu. Một trang hoàn toàn có thể được thay đổi từ một plugin hoặc một tệp chức năng, điều này làm cho giải pháp này rất năng động.

Tôi cũng đã sử dụng get_template_part()để tải một phần mẫu, sẽ được sử dụng để hiển thị các bài viết. Hầu hết các chủ đề ngày nay sử dụng các phần mẫu, điều này làm cho điều này rất hữu ích trong lớp. Nếu sử dụng chủ đề của bạn content.php, bạn chỉ có thể vượt qua contentđể $templatePartđến tải content.php.

Nếu bạn cần hỗ trợ định dạng bài đăng cho các phần mẫu, thật dễ dàng - bạn chỉ cần chuyển contentđến $templatePartvà đặt $postFormatSupportthành true. Do đó, phần mẫu content-video.phpsẽ được tải cho một bài đăng có định dạng bài đăng video.

SỐ LƯỢNG CHÍNH

Các thay đổi sau đây được thực hiện cho truy vấn chính thông qua các bộ lọc và hành động tương ứng:

  • Để phân trang truy vấn chính:

    • $found_postsGiá trị thuộc tính của truy vấn của bộ truyền được truyền cho giá trị của đối tượng truy vấn chính thông qua found_postsbộ lọc.

    • Giá trị của tham số người dùng đã truyền posts_per_pageđược đặt thành truy vấn chính thông qua pre_get_posts.

    • $max_num_pagesđược tính bằng cách sử dụng số lượng bài viết trong $found_postsposts_per_page. Bởi vì is_singularlà đúng trên các trang, nó ức chế LIMITmệnh đề được đặt. Chỉ đơn giản là đặt is_singularthành false gây ra một vài vấn đề, vì vậy tôi quyết định đặt LIMITmệnh đề thông qua post_limitsbộ lọc. Tôi giữ offsetcủa LIMITbộ khoản để 0tránh 404 của trên các trang có pagination bật.

Điều này quan tâm đến phân trang và bất kỳ vấn đề có thể phát sinh từ tiêm sau.

ĐỐI TƯỢNG TRANG

Đối tượng trang hiện tại có sẵn để hiển thị dưới dạng bài đăng bằng cách sử dụng vòng lặp mặc định trên trang, riêng biệt và trên đầu trang của bài đăng được tiêm. Nếu bạn không cần điều này, bạn có thể chỉ cần đặt $removePageFromLoopthành đúng và điều này sẽ ẩn nội dung trang khỏi hiển thị.

Ở giai đoạn này, tôi đang sử dụng CSS để ẩn đối tượng trang thông qua các hành động loop_startloop_endhành động vì tôi không thể tìm thấy một cách khác để làm điều này. Nhược điểm của phương pháp này là mọi thứ được nối với the_posthook hành động bên trong truy vấn chính cũng sẽ bị ẩn.

LỚP

Các PreGetPostsForPageslớp có thể được cải thiện và cần được namespaced đúng là tốt. Mặc dù bạn chỉ có thể thả cái này trong tệp chức năng của chủ đề, nhưng tốt hơn là thả cái này vào một plugin tùy chỉnh.

Sử dụng, sửa đổi và lạm dụng khi bạn thấy phù hợp. Mã được nhận xét tốt, vì vậy nó phải dễ theo dõi và điều chỉnh

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++;       
    }
}  

SỬ DỤNG

Bây giờ bạn có thể bắt đầu lớp ( cũng trong tệp plugin hoặc hàm của bạn ) như sau để nhắm mục tiêu trang có ID 251, khi đó chúng tôi sẽ hiển thị 2 bài đăng trên mỗi trang từ postloại bài đăng.

$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(); 

THÊM PAGINATION VÀ PHONG CÁCH TÙY CHỈNH

Như tôi đã đề cập trước đây, có một vài hành động trong truy vấn của trình tiêm để thêm phân trang và / hoặc kiểu tùy chỉnh.

Trong ví dụ sau, tôi đã thêm phân trang sau vòng lặp bằng cách sử dụng chức năng phân trang của riêng tôi từ câu trả lời được liên kết . Ngoài ra, bằng cách sử dụng bộ đếm tùy chỉnh của mình, tôi đã thêm một <div>để hiển thị các bài đăng của mình trong hai cột.

Dưới đây là những hành động tôi đã sử dụng

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();    
});

Lưu ý rằng phân trang được đặt bởi truy vấn chính, không phải truy vấn của trình tiêm, do đó, các hàm tích hợp như the_posts_pagination()cũng sẽ hoạt động.

Đây là kết quả cuối cùng

nhập mô tả hình ảnh ở đây

PAGES STATIC

Mọi thứ hoạt động như mong đợi trên các trang đầu tĩnh cùng với chức năng phân trang của tôi mà không yêu cầu sửa đổi gì thêm.

PHẦN KẾT LUẬN

Điều này có vẻ như rất nhiều chi phí, và nó có thể, nhưng chuyên nghiệp vượt xa thời gian lớn.

CHUYÊN NGHIỆP LỚN

  • Bạn không cần thay đổi mẫu trang cho trang cụ thể theo bất kỳ cách nào. Điều này làm cho mọi thứ động và có thể dễ dàng được chuyển giữa các chủ đề mà không cần sửa đổi mã nào, miễn là mọi thứ được thực hiện trong một plugin.

  • Nhiều nhất, bạn chỉ cần tạo một content.phpphần mẫu trong chủ đề của mình nếu chủ đề của bạn chưa có.

  • Bất kỳ phân trang nào hoạt động trên truy vấn chính sẽ hoạt động trên trang mà không có bất kỳ loại thay đổi hoặc bất kỳ điều gì thêm từ truy vấn được chuyển đến chức năng.

Có nhiều pro hơn mà tôi không thể nghĩ ra bây giờ, nhưng đây là những người quan trọng.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.