Eine tolle Funktion in WordPress sind Sticky Posts oder auch Featured Posts. Im deutschen heißt es etwas seltsam “Beitrag auf der Startseite halten” und das beschreibt die Funktion ganz gut. Ein Sticky Post ist ein Beitrag der als erste Beitrag auf der Startseite angezeigt wird. Selbst wenn du einen neuen Beitrag schreibst, bleibt der Sticky Post auf der Startseite ganz oben. Das ist praktisch, wenn du z.B. eine Bekanntmachung hast oder eine Veranstaltung planst und die Einladung nicht von der Startseite verschwinden soll. Leider funktioniert das nur auf der Startseite. Wirklich?
Sticky Posts wären nicht nur auf der Startseite praktisch
Je größer ein Blog ist, desto mehr Kategorien und Beiträge sammeln sich an. Wäre es da nicht praktisch wenn deine wichtigen Informationen einer Kategorie nicht nur auf der Startseite oben sind, sondern auch in der Kategorie selbst, oder im Archive? Um herauszufinden wie ich die Sticky Posts auf die Kategorie-Seite bekommen, musste ich erst wissen wieso Sticky Posts nur auf der Startseite funktionieren und wo ich eingreifen könnte, damit die Beiträge auch in der Kategorie hervorgehoben und dargestellt werden. Um dieses Problem zu lösen habe ich ein wenig googeln müssen und hab Pieters Antwort auf Stackexchange gefunden.
add_action( 'pre_get_posts', function ( $q ) {
if ( !is_admin()
&& $q->is_main_query()
&& !$q->is_home()
&& !$q->is_page() ) {
$q->set( 'post__not_in', get_option( 'sticky_posts' ) );
if ( !$q->is_paged() ) {
add_filter( 'the_posts', function ( $posts ) {
$stickies = get_posts( ['post__in' => get_option( 'sticky_posts' ), 'nopaging' => true] );
$posts = array_merge( $stickies, $posts );
return $posts;
}, 10, 2);
}
}
});
Beiträge (Posts) werden in WordPress in einer Schleife, dem sogenannten Loop, ausgegeben. Beim Seitenaufruf von einem Besucher wird die Schleife gestartet, es erfolgt eine Anfrage an die Datenbank. Zurück kommen die Beiträge die zu der Anfrage passen und werden dann ausgegeben. In der Datenbank steht auch, ob ein Beitrag auf der Startseite gehalten werden soll. Pieters Antwort schlägt vor bei der Abfrage zuerst nur die Sticky Posts auszugeben und damit die Beiträge nicht doppelt ausgegeben werden, werden die Sticky Posts später aus der eigentlichen Schleife herausgenommen.
Es ist aber nicht genug nur zu googeln 😉
Die erste Idee von Pieter war super, aber nicht perfekt. Jetzt waren die Sticky Posts auf allen Seiten, auf den Suchseiten, auf der Schlagwortseite und selbst auf der Seite eines einzelnen Beitrags. Und es kamen immer alle Sticky Posts, nicht nur die der ausgewählten Kategorie. Leider kamen auch alle Beiträge auf einmal, anstatt nach 10 Beiträgen einen Seitenumbruch hervorzurufen.
Das erste Problem ließ sich sehr einfach lösen: die erste if-Abfrage habe ich erweitert, damit die Anfrage erst gar nicht auf der Suchseite, der Tagseite und bei einem einzelnen Beitrag aufgerufen wird !$q->is_tag() && !$q->is_search() && !$q->is_single()
. Aber für die Kategorieproblematik habe ich etwas länger gebraucht. In Zeile 10 werden die Sticky Posts herausgefiltert und der Seitenumbruch ausgeschaltet (wieder ein Problem gelöst 😉 ). An dieser Stelle musste ich eingreifen um nur die Sticky Posts aus der dazugehörigen Kategorie zu bekommen.
Bei der Funktion get_ posts();
kann man nur mit der Kategorie-ID die richtige Kategorie herausfiltern. Also musste ich vorher die ID der Kategorie herausfinden und dann in der Funktion abfragen. Und Endlich kamen nur die Sticky Posts der jeweiligen Kategorie und auch nur auf den Kategorie-Seiten. Leider habe ich mir dabei selber ein Ei gelegt 😀
add_action( 'pre_get_posts', function ( $q ) {
if ( !is_admin()
&& $q->is_main_query()
&& !$q->is_home()
&& !$q->is_tag()
&& !$q->is_search()
&& !$q->is_page()
&& !$q->is_single() ) {
if ( $q->is_category() && !$q->is_paged() ) {
$q->set( 'post__not_in', get_option( 'sticky_posts' ) );
add_filter( 'the_posts', function ( $posts ) {
$catName = get_category(get_query_var('cat'))->name;
$catID = get_cat_ID($catName);
$stickies = get_posts( [ 'category' => $catID, 'post__in' => get_option( 'sticky_posts' ) ] );
$posts = array_merge( $stickies, $posts );
return $posts;
}, 10, 2);
}
}
});
Die Finale Lösung
Beim testen der Funktion ist meinem Kollegen aufgefallen, dass bei einer der Kategorien der eine enthaltene Beitrag doppelt ausgegeben wird. Wir haben alles Möglichkeiten ausgeschlossen und ich hab das Problem auf Zeile 17 zurückführen konnte. Nach mindestens 1 Stunde suchen hab ich endlich den Grund gefunden:
NOTE: Ticket #28099 Passing an empty array to post__in will return has_posts() as true (and all posts will be returned). Logic should be used before hand to determine if WP_Query should be used in the event that the array being passed to post__in is empty. Quelle
Und das Problem hab ich dann auch gelöst und hab meine Lösung bei Stackexchange geposted.