Sticky Posts nicht nur auf der Startseite?

Sticky Posts nicht nur auf der Startseite?

Von 01.08.16

Eine tolle Funk­tion in Word­Press sind Sticky Posts oder auch Featured Posts. Im deut­schen heißt es etwas seltsam “Beitrag auf der Start­seite halten” und das beschreibt die Funk­tion ganz gut. Ein Sticky Post ist ein Beitrag der als erste Beitrag auf der Start­seite ange­zeigt wird. Selbst wenn du einen neuen Beitrag schreibst, bleibt der Sticky Post auf der Start­seite ganz oben. Das ist prak­tisch, wenn du z.B. eine Bekannt­ma­chung hast oder eine Veran­stal­tung planst und die Einla­dung nicht von der Start­seite verschwinden soll. Leider funk­tio­niert das nur auf der Start­seite. Wirk­lich?

Sticky Posts wären nicht nur auf der Startseite praktisch

Je größer ein Blog ist, desto mehr Kate­go­rien und Beiträge sammeln sich an. Wäre es da nicht prak­tisch wenn deine wich­tigen Infor­ma­tionen einer Kate­gorie nicht nur auf der Start­seite oben sind, sondern auch in der Kate­gorie selbst, oder im Archive? Um heraus­zu­finden wie ich die Sticky Posts auf die Kate­gorie-Seite bekommen, musste ich erst wissen wieso Sticky Posts nur auf der Start­seite funk­tio­nieren und wo ich eingreifen könnte, damit die Beiträge auch in der Kate­gorie hervor­ge­hoben und darge­stellt werden. Um dieses Problem zu lösen habe ich ein wenig googeln müssen und hab Pieters Antwort auf Stackex­change 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 Word­Press in einer Schleife, dem soge­nannten Loop, ausge­geben. Beim Seiten­aufruf von einem Besu­cher wird die Schleife gestartet, es erfolgt eine Anfrage an die Daten­bank. Zurück kommen die Beiträge die zu der Anfrage passen und werden dann ausge­geben. In der Daten­bank steht auch, ob ein Beitrag auf der Start­seite gehalten werden soll. Pieters Antwort schlägt vor bei der Abfrage zuerst nur die Sticky Posts auszu­geben und damit die Beiträge nicht doppelt ausge­geben werden, werden die Sticky Posts später aus der eigent­li­chen Schleife heraus­ge­nommen.

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 Such­seiten, auf der Schlag­wort­seite und selbst auf der Seite eines einzelnen Beitrags. Und es kamen immer alle Sticky Posts, nicht nur die der ausge­wählten Kate­gorie. Leider kamen auch alle Beiträge auf einmal, anstatt nach 10 Beiträgen einen Seiten­um­bruch hervor­zu­rufen.

Das erste Problem ließ sich sehr einfach lösen: die erste if-Abfrage habe ich erwei­tert, damit die Anfrage erst gar nicht auf der Such­seite, der Tagseite und bei einem einzelnen Beitrag aufge­rufen wird !$q->is_tag() && !$q->is_search() && !$q->is_single(). Aber für die Kate­go­rie­pro­ble­matik habe ich etwas länger gebraucht. In Zeile 10 werden die Sticky Posts heraus­ge­fil­tert und der Seiten­um­bruch ausge­schaltet (wieder ein Problem gelöst 😉 ). An dieser Stelle musste ich eingreifen um nur die Sticky Posts aus der dazu­ge­hö­rigen Kate­gorie zu bekommen.

Bei der Funk­tion get_ posts(); kann man nur mit der Kate­gorie-ID die rich­tige Kate­gorie heraus­fil­tern. Also musste ich vorher die ID der Kate­gorie heraus­finden und dann in der Funk­tion abfragen. Und Endlich kamen nur die Sticky Posts der jewei­ligen Kate­gorie und auch nur auf den Kate­gorie-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 Funk­tion ist meinem Kollegen aufge­fallen, dass bei einer der Kate­go­rien der eine enthal­tene Beitrag doppelt ausge­geben wird. Wir haben alles Möglich­keiten ausge­schlossen und ich hab das Problem auf Zeile 17 zurück­führen konnte. Nach mindes­tens 1 Stunde suchen hab ich endlich den Grund gefunden:

NOTETicket #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 deter­mine 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 Stackex­change geposted.

Kommentar verfassen