אדר א

הצגת פוסטים בדף קטגוריה, מחולקים לתתי קטגוריות

בפרויקט שעבדתי עליו לאחרונה התבקשתי שבדף קטגוריה מסוימת בעלת תתי קטגוריות, יוצגו כל הפוסטים של תתי קטגוריות (עד כאן הכל בנוהל של וורדפרס) באופן כזה ששמות תתי הקטגוריות יופיעו (אתגר אחד), ותחת כל שם יופיעו הפוסטים של אותה תת קטגוריה (אתגר שני).

מדובר היה בקטגוריה של בתי הספר של אורט, תתי הקטגוריות היו ערים, והפוסטים היו שמות בתי הספר של אורט באותה עיר (כשבפוסט עצמו יש פרטים ופירוט על בית הספר).

קטגוריית בתי ספר, ובתוכה רשימת ערים שתחת כל אחת מהן מופיעים בית הספר של אורט באותה עיר

  1. הבה נביא את כל פוסטי בתי הספר

    התחלתי עם הדבר הפשוט – להביא את כל הפוסטים של בתי הספר, בלי דפדוף. לשם כך כתבתי פונקציה שיושבת על ה-hook של pre_get_posts, והעברתי -1 בתור מספר הפוסטים בפונקציה שמורצת ע”י :

    /*** Schools list ***/
    if ( term_exists( $this->schools_and_colleges_cat_id, 'category' ) && $query->is_category( $this->schools_and_colleges_cat_id ) ) {
          $query->set( 'posts_per_page', - 1 );
    }
  2. הבה נביא את תתי הקטגוריות, ונשייך את הפוסטים אליהן

    האתגר הבא שלי היה כפול: קודם כל בדף קטגוריה אין בכלל זכר לתת הקטגוריות. אם אני רוצה איזשהו מידע עליהן (אפילו הדבר הכי בסיסי כמו השמות שלהם), אני צריכה לשלוף מה-DB. הדבר השני הוא שבמערך הפוסטים שיש לי בדף קטגוריה, אין באוביקט של כל פוסט שום שיוך לקטגוריה. כאילו הוא שייך לאוויר.

    לכן הבאתי מה-DB רשימה של תתי הקטגוריות של קטגוריית בתי הספר (שבמקרה שלנו הן שמות ערים שיש בהם בתי ספר של אורט), ולכל עיר – רשימה, מופרדת בפסיקים, של בתי הספר בעיר הזאת.
    איך עושים את זה? כתבתי פונקציה שמקבלת ID של קטגוריה, ומחזירה מערך של תתי קטגוריות, שבכל איבר בו יש אוביקט עם השדות הבאים: שם תת הקטגוריה, ה-ID של תצ הקטגוריה, ורשימה של ה-ID-ים של הפוסטים ששייכים אליה, מופרדים בפסיקים.

    האתגר הראשון נענה ע”י שתי טבלאות: בטבלת <a href="https://codex.wordpress.org/Database_Description#Table:_wp_term_taxonomy">wp_term_taxonomy</a> השתמשתי כדי לדעת מי הן תתי הקטגוריות של הקטגוריה המבוקשת, כי שם ישנה עמודת parent. טבלת <a href="https://codex.wordpress.org/Database_Description#Table:_wp_terms">wp_terms</a> שנתנה לי את שמות תתי הקטגוריות וה-ID-ים שלהם.
    בשביל האתגר השני השתמשתי בטבלת <a href="https://codex.wordpress.org/Database_Description#Table:_wp_term_relationships">wp_term_relationships</a> שמכילה שורה עבור כל אוביקט ו-term שמיוחס אליו. במקרה שלי האוביקט היה פוסט. לפי זה, יכולתי אמנם להחזיר כך מערך עם שורה עבור כל פוסט ומי הקטגוריה שלה, אבל לא רציתי שהשאילתה שלי תחזיר שורה עבור כל פוסט, אלא שורה עבור כל תת קטגוריה, ושהשורה הזאת תכיל עמודה עם כל מזהי הפוסטים שלה. לכן השתמשתי ב-GROUP BY של ה-term_id-ים, ובפונקציה GROUP_CONCAT שיוצרת רשימה מופרדת בפסיקים (או כל separator אחר לבחירתכם) על ה-id-ים של הפוסטים.

     $childCategories = $wpdb->get_results( "SELECT {$wpdb->prefix}terms.name, {$wpdb->prefix}terms.term_id, GROUP_CONCAT({$wpdb->prefix}term_relationships.object_id SEPARATOR ' , ') AS post_ids 
    FROM {$wpdb->prefix}term_taxonomy
    INNER JOIN {$wpdb->prefix}term_relationships on {$wpdb->prefix}term_relationships.term_taxonomy_id = {$wpdb->prefix}term_taxonomy.term_id
    INNER JOIN {$wpdb->prefix}terms on {$wpdb->prefix}terms.term_id = {$wpdb->prefix}term_taxonomy.term_id
    WHERE wp_term_taxonomy.parent=$cat
    GROUP BY {$wpdb->prefix}terms.term_id
    ORDER BY {$wpdb->prefix}terms.name;" );

    הפונקציה המרנינה שמכילה את השאילתה הזאת יושבת לי במחלקה, ומחכה שיקראו לה. כדי שיהיה נוח למי שקורא לה לעבור על הפוסטים, הפכתי את ה-post_ids למערך.

    foreach ( $childCategories as $child_category ) {
          $child_category->post_ids = explode( ',', $child_category->post_ids );
    }
    
  3. נקרא לפונקציה מתוך קובץ tempalte

    יצרתי קובץ PHP עם ה-slug של הקטגוריה הראשית (קוראים לקובץ category-schools-and-colleges.php), כך שהוא מה שיופעל כשגולשים לקטגוריה הזאת. שם יצרתי UL, והאיברים ברשימה היו תתי הקטגוריות. מאחר שבמקרה שלנו תתי הקטגוריות הן ערים, קראתי לכל איבר $city, ושלחתי אותה לקובץ template נוסף.

    מאמר מוסגר: אוביקט המחלקה שיצרתי, $catClass, נוצר מיד בסוף הגדרת המחלקה. הוא זמין אמנם לכל קבצי ה-template הראשיים, אבל לא לקבצים פנימיים, ולכן מועבר אליהם באמצעות הפונקציה set_query_var)(, שסיפרתי עליה לפני כמה שנים.

    <ul class="city-list">
       <?php
       global $cat, $post;
    
       // get an array of category ids, names, and a list of each category's posts
       $citiesList = $catClass->getChildCategoriesAndPosts( $cat );
    
       /* Loop over the cities, and for each city print the title and link of its posts.
       * The posts are retrieved from wp_query, by post id
       */
       foreach ( $citiesList as $city ) {
          ?>
          <?php
          set_query_var( 'city', $city );
          set_query_var( 'catClass', $catClass );
          get_template_part( 'template-parts/content', 'category-schools' );
    
          ?>
       <?php } ?>
    </ul> 
  4. נפרק את רשימת הפוסטים של כל תת קטגוריה

    הקובץ הפנימי שלו קראנו יוצר LI ובתוכו מציג את כותרות הפוסטים של כל עיר, עם קישור לפוסט.

    בקוד יצרנו לולאת foreach שעוברת על מערך ה-id-ים של הפוסטים וקוראת לפונקציה שיצרנו בשם getCurrentPostFromCategoryList שמביאה את אותו הפוסט ממערך הפוסטים. פה מוצג הקוד של הלולאה, ואילו הפונקציה getCurrentPostFromCategoryList מוצגת בסעיף הבא.

    <li id="cat-<?php echo $city->term_id; ?>" class="city-category-wrapper closed">
          <h2 class="city-category-title"><?php echo $city->name; ?></h2>
          <ul class="school-list">
          <?php
    
          // Each city has an array of post ids. loop through them and retrieve the posts and print their title and link
          foreach ( $city->post_ids as $post_id ) {
                // assign the current post to the global $post, so the template can use the loop's functions
                $post = $catClass->getCurrentPostFromCategoryList( $post_id ); ?>
                <?php the_title( sprintf( '<li class="entry-title school-name"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></li>' ); ?>
          <?php } ?>
          </ul>
    </li><!-- #post-<?php the_ID(); ?> -->
  5. נציג את המידע על כל פוסט

    איך נציג את הפרטים של כל פוסט לפי ה-id שלו? כזכור, כל דף קטגוריה בוורדפרס מקבל מערך של כל הפוסטים ששייכים לאותה קטגוריה או לתת קטגוריה של אותה קטגוריה באוביקט גלובלי בשם $posts. מבאס אמנם שהאוביקטים במערך לא מכילים שום מידע על הקטגוריה, אבל התגברנו על זה ע”י השאילתה לעיל ששידכה בין פוסטים לקטגוריות. לכן ניגשים בקוד למערך הפוסטים ושולפים פוסט בעל id מסוים בעזרת פונקציית array_filter)(. מיישרים את המערך באמצעות array_values ומחזירים את האיבר הראשון (שיש לקוות שהוא גם האיבר היחיד):

    function getCurrentPostFromCategoryList( $post_id ) {
    global $posts;
    $ret = null;
    $curr_post = array_filter( $posts, function ( $obj ) use ( $post_id ) {
    return $obj->ID == $post_id;
    } );
    $curr_post = array_values( $curr_post );
    if ( count( $curr_post ) > 0 ) {
    $ret = $curr_post[0];
    }
    
    return $ret;
    }

     

וכך, די בפשטות, קיבלנו רשימת בתי ספר של אורט עם שייכות לערים שלהם, בלי יותר מדי גישות ל-DB.

אשמח לשמוע את דעתכם בנושא – האם הייתם צריכים לעשות משהו דומה אי פעם, האם יש דרכים פשוטות יותר לעשות את זה שלא חשבתי עליהן, או כל דבר שיש לכם לשאול או להוסיף.

עדכון (19/8/2020): תרגמתי את הפוסט הזה והוא התפרסם באתר של WPShout תחת הכותרת: How to Display Posts on a Category Page, Divided Into Their Respective Subcategories. מעבר לתרגום, עיביתי אותו שם במידע נוסף – ניתוח ההחלטה שלי להשתמש בשאילתת SQL מתוך הקוד במרום להשתמש בפונקציות רגילות של WP, והסבר על הטבלאות שבהן השתמשתי בשאילתה,

כתבו תגובה

כתובת הדוא"ל שלכם לא תוצג.