Returnează postarea părinte cu copiii săi folosind WP_Query?

23 mar. 2016, 12:57:14
Vizualizări: 26.7K
Voturi: 8

În anumite cazuri poate fi util să utilizezi mai mulți parametri pentru posturi și pagini în obiectul tău WP_Query. În cazul meu, aș dori să afișez copiii unei pagini părinte inclusiv pagina părinte în sine.

Vizualizarea a ceea ce vreau să obțin. Imaginează-ți următoarele pagini sortate ierarhic astfel:

  • pagina A
  • pagina B
    • Pagina copil A
    • Pagina copil B
    • Pagina copil C
  • pagina C

Elementele din listă îngroșate sunt postările/paginile pe care vreau să le preiau.

Primele mele gânduri se îndreaptă spre utilizarea acestor doi parametri pentru WP_Query:

$args = array(
   'post_id' => $parent->ID,
   'post_parent' => $parent->ID,
);

Din păcate, aici va folosi doar un parametru. Cu $args de mai sus (corectați-mă dacă greșesc) va afișa toate postările copil ale postării părinte și nu va include și postarea părinte în sine.

Această problemă ar putea fi rezolvată prin colectarea tuturor postărilor necesare și punerea lor în parametrul post__in astfel:

$args = array(
   'post__in' => $children_and_parent_ids,
);

Totuși există wp_list_pages() care îți permite să include o postare/postări și să specifici postarea de la care vrei să incluzi copiii (child_of). De ce nu este posibil acest lucru cu WP_Query?

Un exemplu a ceea ce încerc să realizez folosind wp_list_pages():

wp_list_pages(array(
    'include' => $parent->ID,
    'child_of' => $parent->ID,
));

Aruncă o privire la documentația pentru WP_Query.

0
Toate răspunsurile la întrebare 6
0

Dacă tot ce doriți sunt rezultate din post_type "page", atunci procedați așa cum a sugerat @birgire.

Alternativ, puteți adapta următorul cod pentru a obține un rezultat similar nu doar pentru page post_type ci și pentru orice alt tip de postare personalizată.

$parent = 2;      //schimbați după necesități
$type   = 'page'; //schimbați după necesități

$child_args = array( 
    'post_type'   => $type, 
    'post_parent' => $parent 
);

$ids = array( $parent );
$ids = array_merge( $ids, array_keys( get_children( $child_args ) ));

$query = new WP_Query( 
    array( 
        'post_type'      => 'page', 
        'post_status'    => 'publish', 
        'post__in'       => $ids, 
        'posts_per_page' => -1 
    ) 
);

Codul de mai sus este în esență același lucru cu conectarea la filtrul posts_where și parsarea clauzei SQL, însă acest lucru obține exact același rezultat.

23 mar. 2016 14:10:26
7

Putem filtra clauza posts_where din SQL-ul generat pentru a returna și postul/pagina părinte, nu doar copiii acestuia. Aici vom seta un argument personalizat numit wpse_include_parent, care, când este setat la true, va modifica SQL-ul generat în consecință.

Tot ce trebuie să facem în interiorul filtrului nostru posts_where este să verificăm dacă argumentul nostru personalizat este setat și dacă argumentul post_parent este setat. Apoi obținem acea valoare și o transmitem filtrului pentru a extinde interogarea noastră SQL. Ceea ce este frumos aici este că post_parent acceptă o singură valoare întreagă, așa că trebuie doar să validăm valoarea ca întreg.

INTEROGAREA

$args = [
    'wpse_include_parent' => true,
    'post_parent'         => 256,
    'post_type'           => 'page'
    // Adaugă argumente suplimentare
];
$q = new WP_Query( $args );

După cum puteți vedea, am setat 'wpse_include_parent' => true pentru a "activa" filtrul nostru.

FILTRUL

add_filter( 'posts_where', function ( $where, \WP_Query $q ) use ( &$wpdb )
{
    if ( true !== $q->get( 'wpse_include_parent' ) )
        return $where;

    /**
     * Obține valoarea transmisă pentru post_parent și o validează
     * post_parent acceptă doar o valoare întreagă, așa că trebuie doar să validăm
     * valoarea ca întreg
     */
    $post_parent = filter_var( $q->get( 'post_parent' ), FILTER_VALIDATE_INT );
    if ( !$post_parent )
        return $where;

    /** 
     * Să includem și părintele în interogarea noastră
     *
     * Deoarece am validat deja valoarea $post_parent, nu este nevoie
     * să folosim metoda prepare() aici
     */
    $where .= " OR $wpdb->posts.ID = $post_parent";

    return $where;
}, 10, 2 );

Puteți extinde acest lucru după cum aveți nevoie și considerați potrivit, dar aceasta este ideea de bază. Acest lucru va returna părintele transmis la post_parent și copiii acestuia.

23 mar. 2016 20:18:49
Comentarii

Codul funcționează, dar din păcate returnează de două ori același post părinte. Când rulez un WP_Query cu atributul personalizat.

luukvhoudt luukvhoudt
30 mar. 2016 12:22:31

Am retestat codul meu și nu pot reproduce problema ta

Pieter Goosen Pieter Goosen
30 mar. 2016 14:58:26

uită-te la codul și rezultatul meu. Sper că asta te va ajuta să înțelegi mai bine problema. Încearcă să pui interogarea direct pe pagina părinte sau copil.

luukvhoudt luukvhoudt
30 mar. 2016 15:13:25

Am analizat var_dump(). Sunt returnate două postări în $q->posts, cu ID-urile 2126 și 2116, ceea ce pare corect, fără duplicate. Cred că ceea ce vezi este postarea 2116 disponibilă și în $q->post, ceea ce este de asemenea corect. Aceasta este valoarea globală $post pentru acea interogare specifică și va fi întotdeauna prima postare din $q->posts în mod implicit.

Pieter Goosen Pieter Goosen
30 mar. 2016 15:54:17

Bine, dar întotdeauna returnează două postări părinte, chiar și când vizualizezi postarea copil cu acest cod al părintelui (deci global $post conține obiectul postării copil). Întrebarea este de ce iterează de 3 ori când există doar 2 postări prin care să itereze?

luukvhoudt luukvhoudt
30 mar. 2016 16:14:33

de ce iterează de 3 ori când există doar 2 postări prin care să itereze Atunci nu are nicio legătură cu interogarea. În obiectul de interogare, există două postări. Dacă există o a treia postare, aceasta este injectată în această interogare prin intermediul unui filtru personalizat. Totul în interogarea ta pare OK, deși interogarea SQL arată ciudat din cauza altor filtre care acționează asupra interogării. Ar trebui să depanezi această problemă, să te asiguri că $post este ceea ce te aștepți să fie, dacă bucla ta este corectă, dacă celelalte filtre sunt folosite corect în context. Pe o instalare vanilla, totul funcționează conform așteptărilor pe partea mea.

Pieter Goosen Pieter Goosen
30 mar. 2016 16:42:38

Din păcate, nu te pot ajuta aici deoarece, după cum am menționat, totul funcționează corespunzător la mine. Începe prin a dezactiva pluginurile, verifică toate variabilele pentru a te asigura că au valorile corecte și treci pe o temă inclusă în pachetul WordPress

Pieter Goosen Pieter Goosen
30 mar. 2016 16:44:07
Arată celelalte 2 comentarii
2
    $args = array(
        'post_type' => 'tribe_events', // Tipul postării - evenimente
        'posts_per_page' => '-1', // Toate postările
        'orderby' => 'ID', // Sortează după ID
        'order' => 'ASC', // Ordine crescătoare
        'post_parent' => $postID, // ID-ul părintelui
    );

    $children = new WP_Query($args); // Interogare pentru postările copil
    $parent[] = get_post($postID); // Obține postarea părinte
    $family = array_merge($parent, $children->get_posts()); // Combină părintele cu copiii

Acest cod pare să funcționeze. Comentarii?

22 oct. 2017 19:31:35
Comentarii

Adevărat, dar nu va fi la fel de eficient ca răspunsul acceptat. Abordarea din răspunsul acceptat necesită doar o singură interogare, în timp ce abordarea ta va rezulta în două interogări (dacă nu sunt deja în cache).

luukvhoudt luukvhoudt
23 oct. 2017 14:21:23

Cred că răspunsul tău este mult mai bun!!! mult mai simplu/lizibil!!! Și pentru a economisi ce... o interogare? din sutele din contextul WordPress... iar asta a fost cache-uită de 30 de ori până a ajuns la codul tău...

pe deasupra, interogarea economisită este una WHERE ID=123.... 0.0001s

Antony Gibbs Antony Gibbs
28 apr. 2022 09:49:02
1

Utilizarea global $wpdb combinată cu [get_results()][1] este de asemenea o opțiune. Din punct de vedere al performanței, cred că aceasta este cea mai bună soluție, deoarece rulează doar o singură interogare.

Iată codul meu final.

<ul class="tabs"><?php

    global $wpdb, $post;

    $parent = count(get_post_ancestors($post->ID))-1 > 0 ? $post->post_parent : $post->ID;

    $sql = "SELECT ID FROM `{$wpdb->prefix}posts`";
    $sql.= " WHERE ID='{$parent}' OR post_parent='{$parent}' AND post_type='page'";
    $sql.= " ORDER BY `menu_order` ASC";

    $tabs = $wpdb->get_results($sql);

    $output = '';
    foreach ($tabs as $tab) {
        $current = $post->ID == $tab->ID ? ' class="active"' : '';

        $output .= '<li'.$current.'>';
        $output .= empty($current) ? '<a href="'.get_permalink($tab->ID).'">' : '';
        $output .=   get_the_post_thumbnail($tab->ID, 'menu-24x24');
        $output .=   '<span>'.get_the_title($tab->ID).'</span>';
        $output .= empty($current) ? '</a>' : '';
        $output .= '</li>';
    }
    print $output;

?></ul>
23 mar. 2016 14:45:45
Comentarii

S-ar putea crede că așa stau lucrurile, însă WP_Query în spate stochează în cache rezultatele interogărilor, așadar de exemplu, un apel către get_children() sau get_post_ancestors() în plus față de WP_Query nu ar trebui să aducă nicio penalizare suplimentară de performanță.

Adam Adam
23 mar. 2016 14:56:22
1

Dacă te înțeleg corect, vrei să obții ID-urile atât ale paginilor părinte, cât și ale oricăror pagini copil ulterioare. WordPress are funcții care preiau copiii paginilor, cum ar fi aceasta:

https://codex.wordpress.org/Function_Reference/get_page_children

Din câte înțeleg, deoarece efectuezi o interogare WP_Query, deja preiei ID-urile paginilor părinte, deci tot ce trebuie să faci este să transmiți ID-ul relevant funcției menționate mai sus pentru a obține ceea ce dorești.

Notă: Trebuie să menționez că această funcție nu efectuează o interogare în baza de date, deci este mai eficientă din punct de vedere al performanței, deoarece faci o singură interogare în baza de date.

23 mar. 2016 21:11:04
Comentarii

Înțeleg de ce menționezi această funcție, dar acest răspuns este mai degrabă un comentariu decât un răspuns. Oricum, nu găsesc un motiv pentru care această funcție ar putea fi utilă, deoarece WP_Query deja îți permite să specifici o listă de articole și copiii acestora cu argumentul: post_parent. Deci, de ce nu este învechită această funcție?

luukvhoudt luukvhoudt
23 mar. 2016 21:37:12
5
-2

Puteți utiliza funcția get_pages() cu parametrul child_of astfel:

<?php
   get_pages( array(
     'child_of' => $parent_page_id;
   ) );
?>
23 mar. 2016 13:45:41
Comentarii

Nu asta încerc să obțin, nu vreau să folosesc wp_list_pages deoarece rezultatul nu îmi satisface nevoile.

luukvhoudt luukvhoudt
23 mar. 2016 13:48:30

Vezi răspunsul meu actualizat. Sper că te va ajuta.

Lalji Nakum Lalji Nakum
23 mar. 2016 13:54:49

Mulțumesc pentru actualizare, din păcate acest lucru nu include o explicație despre cum să includ postarea părinte.

luukvhoudt luukvhoudt
23 mar. 2016 14:00:39

puteți consulta https://codex.wordpress.org/Function_Reference/get_pages pentru o explicație detaliată

Lalji Nakum Lalji Nakum
23 mar. 2016 14:06:38

Conform documentației: Parametrul child_of nu este aplicat în interogarea SQL pentru pagini. Este aplicat pe rezultatele interogării. De asemenea, nu va returna părintele.

luukvhoudt luukvhoudt
23 oct. 2017 14:17:34