האם ACF Repeater Field באמת פותר את בעיית השדות החוזרים בוורדפרס?

שיתוף

מסך עורך קוד המציג לולאת PHP עם תחביר של ACF Repeater Field בוורדפרס
ACF Repeater Field הוא אחד הכלים החזקים ביותר בוורדפרס לניהול תוכן דינמי וחוזר. מדריך PHP מעשי לשימוש נכון, לולאות, שגיאות נפוצות ואופטימיזציה.
תוכן עניינים
10 דקות קריאה

מה זה ACF Repeater Field ולמה הוא קיים

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

ACF Repeater Field, חלק מהתוסף Advanced Custom Fields Pro, פותר בדיוק את הבעיה הזו. הוא מאפשר להגדיר קבוצת שדות שחוזרת על עצמה מספר פעמים בלתי מוגבל. מנהל האתר מוסיף שורות דרך ממשק ידידותי, והמפתח מציג אותן בתבנית PHP עם לולאה פשוטה. התוצאה: תוכן דינמי ומובנה שניתן לנהל בלי לגעת בקוד.

הפופולריות של ACF Pro נובעת מכך שהוא מגשר על הפער בין מה שוורדפרס מציע כברירת מחדל לבין מה שפרויקטים אמיתיים דורשים. לפי נתוני WordPress.org, ACF מותקן על יותר מ-6 מיליון אתרים פעילים, מה שהופך אותו לאחד התוספים הנפוצים ביותר בפלטפורמה.

הגדרת Repeater Field בממשק ACF

לפני שכותבים שורת PHP אחת, צריך להגדיר את השדה נכון בממשק. ב-ACF Pro, יוצרים קבוצת שדות חדשה, מוסיפים שדה מסוג "Repeater", ובתוכו מגדירים את תת-השדות שיחזרו בכל שורה.

לדוגמה, עבור רשימת חברי צוות, תת-השדות יכולים להיות:

  • שם (Text)
  • תפקיד (Text)
  • תמונה (Image)
  • קישור לפרופיל (URL)
  • ביוגרפיה קצרה (Textarea)

שם השדה שמגדירים ב-ACF הוא המפתח שישמש בקוד PHP. אם קוראים לשדה team_members, זה בדיוק המחרוזת שתעבירו ל-have_rows(). שגיאת כתיב קטנה כאן גורמת ל-have_rows() להחזיר false, ומפתחים מתחילים מבלים שעות בחיפוש אחרי הבאג.

חשוב גם להגדיר את ה-Location Rules נכון, כלומר באיזה סוג פוסט, עמוד, או הקשר יופיע השדה. שדה שמוגדר רק לפוסטים מסוג "post" לא יופיע בעמודים רגילים, גם אם הקוד נכון לחלוטין.

ממשק ניהול ACF בוורדפרס עם שורות חוזרות של Repeater Field

כיצד כותבים לולאת PHP בסיסית עם have_rows

הלולאה הבסיסית של ACF Repeater Field ב-PHP בנויה על שלוש פונקציות: have_rows() בודקת אם יש שורות, the_row() מתקדמת לשורה הבאה, ו-the_sub_field() מציגה את ערך תת-השדה.

קוד בסיסי נראה כך:


if( have_rows('team_members') ):
    while( have_rows('team_members') ): the_row();
        echo '
'; echo '

' . get_sub_field('name') . '

'; echo '

' . get_sub_field('role') . '

'; echo '
'; endwhile; endif;

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

אם הלולאה רצה מחוץ ל-Loop הרגיל של וורדפרס, צריך להעביר את ה-Post ID כפרמטר שני: have_rows('team_members', get_the_ID()). בתבניות כמו single.php זה בדרך כלל לא נדרש, אך ב-WP_Query מותאמת זה הכרחי.

קבלת נתוני Repeater כמערך PHP

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

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


$team = get_field('team_members');
if( $team ) {
    usort($team, function($a, $b) {
        return strcmp($a['name'], $b['name']);
    });
    foreach( $team as $member ) {
        echo $member['name'] . ' - ' . $member['role'];
    }
}

שיטה זו שימושית במיוחד כשמייבאים נתונים ממקור חיצוני, כמו API או קובץ CSV, ורוצים לשמור אותם ישירות לשדה Repeater. הפונקציה update_field() מקבלת מערך בפורמט זהה ושומרת את כל השורות בבת אחת.

אחת הטעויות שאנחנו רואים לעתים קרובות: מפתחים מנסים לשמור נתונים עם update_field() כשהמערך לא בנוי בפורמט הנכון. ACF מצפה למערך שטוח של שורות, לא למערך מקונן עם מפתח נוסף. כדאי תמיד לבדוק עם var_dump() לפני שמנסים לשמור.

קוד PHP המציג לולאה מקוננת לעיבוד מערכי נתונים בפיתוח וורדפרס

לולאות מקוננות: Repeater בתוך Repeater

ACF Pro תומך בקינון של שדות Repeater עד 3 רמות עומק. תרחיש נפוץ: רשימת קטגוריות, שבכל אחת יש רשימת פריטים, ובכל פריט יש רשימת תכונות. בפועל, קינון מעבר לשתי רמות הופך את הקוד לקשה לתחזוקה ואת הביצועים לבעייתיים.

קוד לשתי רמות קינון:


if( have_rows('categories') ):
    while( have_rows('categories') ): the_row();
        echo '

' . get_sub_field('category_name') . '

'; if( have_rows('items') ): while( have_rows('items') ): the_row(); echo '

' . get_sub_field('item_title') . '

'; endwhile; endif; endwhile; endif;

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

נקודה טכנית חשובה: בתוך לולאה מקוננת, have_rows() ו-the_sub_field() פועלות על הרמה הנוכחית אוטומטית. אין צורך להעביר פרמטרים נוספים, ACF עוקב אחרי ה"מיקום" הנוכחי בלולאה.

שימוש ב-Repeater Field בעמוד אפשרויות

ACF Options Page מאפשר להגדיר שדות גלובליים שאינם שייכים לפוסט ספציפי, כמו פרטי יצירת קשר, שעות פעילות, או רשימת סניפים. שדות Repeater על עמוד אפשרויות עובדים בדיוק אותו דבר, עם שינוי אחד: מעבירים 'option' כפרמטר שני.


if( have_rows('branch_list', 'option') ):
    while( have_rows('branch_list', 'option') ): the_row();
        echo get_sub_field('branch_name');
    endwhile;
endif;

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

אם האתר שלכם משתמש ב-WordPress Multisite, שימו לב שעמוד האפשרויות ברירת המחדל הוא per-site. יש אפשרות להגדיר options page גלובלי לכל הרשת עם הפרמטר 'show_in_menu' ו-capability מתאים, אך זה דורש הגדרה מפורשת.

אופטימיזציית ביצועים בשאילתות Repeater

כל שורה ב-Repeater Field נשמרת כרשומה נפרדת בטבלת wp_postmeta. שדה Repeater עם 50 שורות ו-5 תת-שדות בכל שורה = 251 רשומות במסד הנתונים (1 לספירת השורות + 250 לנתונים). על אתר עם מאות פוסטים, זה מצטבר במהירות.

כמה אסטרטגיות לשיפור ביצועים:

  • שימוש ב-Object Cache (Redis או Memcached) כדי לשמור תוצאות get_field() בזיכרון
  • הימנעות מ-Repeater Fields בלולאות WP_Query גדולות, במיוחד בארכיב עם עשרות פוסטים
  • שקילת שימוש ב-Transients API לשמירת תוצאות מעובדות לזמן קצוב
  • שימוש ב-get_field() פעם אחת ושמירה במשתנה, במקום קריאות חוזרות לאותו שדה

ברוב המקרים, אתר עם תנועה בינונית לא יחוש בהבדל. הבעיה מתחילה כשיש עמוד ארכיב שמציג 20 פוסטים, כל אחד עם Repeater של 30 שורות. שם כבר שווה להשקיע בקאשינג.

שגיאות נפוצות ואיך להימנע מהן

ברוב הפרויקטים שאנחנו רואים, הבאגים ב-ACF Repeater מגיעים מאותן 4-5 סיבות חוזרות. הנה הנפוצות ביותר:

שימוש ב-get_field() במקום get_sub_field() בתוך הלולאה. get_field() מחפשת שדה ראשי, לא תת-שדה. בתוך לולאת have_rows() חייבים להשתמש ב-get_sub_field() או the_sub_field().

אי-בדיקה אם have_rows() מחזיר true לפני הלולאה. אם השדה ריק, הלולאה פשוט לא תרוץ, אבל קוד שמניח שיש נתונים עלול לגרום ל-PHP notices מיותרים.

שכחת wp_reset_postdata() אחרי WP_Query. כשמשתמשים ב-Repeater בתוך לולאת WP_Query מותאמת, חייבים לאפס את הלולאה הגלובלית בסיום. אחרת, have_rows() בחלקים אחרים של הדף עלול לפעול על הפוסט הלא נכון.

הצגת תמונות מ-Repeater בפורמט שגוי. שדה תמונה ב-ACF יכול להחזיר ID, URL, או מערך מלא, תלוי בהגדרת "Return Format". אם מצפים למערך ומקבלים ID, הקוד ייכשל בשקט.

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

Repeater Field עם JavaScript ו-REST API

ACF Pro כולל תמיכה ב-WordPress REST API. שדות Repeater נחשפים אוטומטית ב-endpoint של הפוסט כשמפעילים את האפשרות "Show in REST API" בהגדרות קבוצת השדות. הנתונים מגיעים כמערך JSON מובנה, מה שמאפשר שימוש בהם ב-React, Vue, או כל JavaScript framework.

דוגמה לגישה לנתוני Repeater דרך REST API:


fetch('/wp-json/wp/v2/posts/123')
  .then(res => res.json())
  .then(post => {
    const members = post.acf.team_members;
    members.forEach(member => {
      console.log(member.name, member.role);
    });
  });

חשוב לדעת: שדות תמונה ב-Repeater דרך REST API מחזירים את ה-ID של הקובץ, לא את ה-URL. צריך לבצע קריאה נוספת ל-/wp-json/wp/v2/media/{id} כדי לקבל את ה-URL, או לשנות את ה-Return Format ל-"Image Array" בהגדרות ACF.

לאתרים שמשלבים WordPress כ-headless CMS עם פרונטאנד נפרד, Repeater Fields דרך REST API הם כלי עוצמתי. הם מאפשרים לצוות התוכן לנהל מבנה מורכב דרך ממשק מוכר, בעוד הפרונטאנד מקבל JSON נקי ומובנה.

Repeater Field בבלוקים מותאמים של Gutenberg

ACF Blocks מאפשרים לרשום בלוקים מותאמים אישית ב-Gutenberg שמשתמשים בשדות ACF, כולל Repeater. הבלוק מוגדר ב-PHP עם acf_register_block_type(), ותבנית ה-PHP שלו יכולה להשתמש בכל פונקציות ACF הרגילות.

ההבדל המשמעותי: בתוך תבנית בלוק ACF, הקשר השדה הוא הבלוק עצמו, לא הפוסט. לכן have_rows() עובד ישירות ללא צורך ב-Post ID. זה מפשט את הקוד אך גם אומר שנתוני הבלוק נשמרים בתוך תוכן הפוסט כ-JSON, לא ב-postmeta.

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

מגבלה שכדאי להכיר: עריכה ויזואלית של שדות Repeater ישירות בעורך Gutenberg, כלומר הוספה והסרה של שורות בתצוגה חיה, עדיין לא חלקה לחלוטין. ממשק ה-ACF הקלאסי בסרגל הצד של Gutenberg עדיין נוח יותר לעריכת Repeaters מורכבים.

בדיקות ו-Debugging של Repeater Fields

כשלולאת Repeater לא עובדת כצפוי, יש סדר בדיקות שחוסך זמן. קודם כל, מוסיפים var_dump(get_field('field_name')) לפני הלולאה. אם מקבלים null, הבעיה היא בשם השדה או בהקשר. אם מקבלים מערך ריק, השדה קיים אך אין נתונים. אם מקבלים מערך עם נתונים, הבעיה היא בלולאה עצמה.

כלי שימושי: התוסף Query Monitor מציג את כל שאילתות מסד הנתונים שנוצרו על ידי ACF, כולל כמה שאילתות כל Repeater מייצר. זה עוזר לזהות צווארי בקבוק בביצועים.

לסביבת פיתוח, כדאי להפעיל WP_DEBUG ו-WP_DEBUG_LOG ב-wp-config.php. ACF כותב אזהרות ל-debug log כשמשתמשים בפונקציות בצורה שגויה, למשל קריאה ל-the_sub_field() מחוץ ללולאה פעילה. הודעות אלו לא מופיעות בפרונטאנד בסביבת production, מה שהופך אותן לקשות לאיתור ללא debug mode.

לאתרים שמשתמשים ב-ACF בשילוב עם תוספי SEO, כדאי לבדוק את השימוש בתוספי SEO בוורדפרס כדי לוודא שהתוכן הדינמי שנוצר דרך Repeater Fields נסרק ומאונדקס כראוי.

שאלות נפוצות

איך מציגים ACF repeater field ב-PHP בתבנית וורדפרס?
משתמשים בפונקציה have_rows() בתוך לולאת while, ובתוכה the_sub_field() להצגת כל שדה. חשוב לוודא שהשדה מוגדר כ-Repeater ב-ACF ושהמפתח תואם בדיוק לשם שהוגדר בממשק. שגיאה נפוצה היא שימוש ב-get_field() במקום get_sub_field() בתוך הלולאה, מה שמחזיר ערך ריק.
האם ACF Repeater Field זמין בגרסה החינמית של ACF?
לא. Repeater Field הוא תכונה בלעדית של ACF Pro, שעולה כ-49 דולר לשנה לאתר יחיד. הגרסה החינמית של ACF תומכת בשדות בסיסיים בלבד כמו טקסט, תמונה ובחירה, אך לא בשדות מורכבים כמו Repeater או Flexible Content.
כיצד מקבלים את ערכי ה-Repeater כמערך PHP ולא דרך לולאה?
משתמשים ב-get_field('field_name') שמחזיר מערך של שורות. כל שורה היא מערך אסוציאטיבי עם מפתחות לפי שמות תת-השדות. שיטה זו שימושית כשצריך לעבד את הנתונים לפני הצגתם, למשל למיון, סינון או העברה ל-JavaScript.
האם אפשר לקנן Repeater בתוך Repeater ב-ACF?
כן, ACF Pro תומך בקינון של עד 3 רמות. בקוד PHP משתמשים בלולאות while מקוננות עם have_rows() ו-the_sub_field() בכל רמה. חשוב לשים לב שקינון עמוק מדי עלול לפגוע בביצועים, במיוחד כשיש הרבה שורות בכל רמה.
למה have_rows() מחזיר false למרות שיש נתונים בשדה?
הסיבות הנפוצות הן: שם השדה שגוי, הפוסט ID לא מועבר נכון, או שהנתונים נשמרו בהקשר שונה (כמו options page). כדאי לבדוק עם var_dump(get_field('field_name')) לפני הלולאה. אם מדובר בשדה של עמוד אפשרויות, יש להוסיף 'option' כפרמטר שני.
כיצד מציגים ACF Repeater Field בתוך WP_Query מותאמת?
לאחר לולאת WP_Query הרגילה, בתוך the_post() משתמשים ב-have_rows() עם get_the_ID() כפרמטר שני. חשוב לאפס את הלולאה עם wp_reset_postdata() בסיום. אי-ביצוע האיפוס הוא אחת הטעויות הנפוצות שגורמות לבאגים קשים לאיתור בדפים עם מספר שאילתות.
האם ACF Repeater Field עובד עם Gutenberg?
כן, אך עם מגבלות. ניתן להציג שדות Repeater בבלוקים מותאמים אישית של Gutenberg באמצעות acf_register_block_type(). עם זאת, עריכה ויזואלית של שדות Repeater ישירות בעורך Gutenberg דורשת ACF Blocks, שזמין רק ב-ACF Pro. הצגת הנתונים בפרונטאנד עובדת זהה ללא קשר לעורך.
מה ההבדל בין the_sub_field לבין get_sub_field ב-ACF?
the_sub_field() מדפיסה את הערך ישירות לדף (echo), בעוד ש-get_sub_field() מחזירה את הערך כמשתנה PHP לשימוש בקוד. כלל האצבע: משתמשים ב-the_sub_field() כשרוצים להציג ישירות ב-HTML, וב-get_sub_field() כשצריך לעבד את הערך, להשוות אותו, או להעביר אותו לפונקציה אחרת.
כיצד מוסיפים שורות ל-Repeater Field באמצעות PHP ולא דרך הממשק?
משתמשים ב-update_field() עם מערך מובנה. קודם מקבלים את הנתונים הקיימים עם get_field(), מוסיפים שורה חדשה למערך, ואז שומרים חזרה עם update_field(). שיטה זו שימושית לייבוא נתונים אוטומטי, למשל ממיגרציה של מסד נתונים או סנכרון עם API חיצוני.

היי 😊

רגע לפני שנדבר

נשמח להראות לכם חלק קטן מהרפויקטים שלנו