Migration V0 18 0


Football Leagues 0.18.0 moves three entities from WordPress postmeta into dedicated custom tables: clubs, standings, and competitions. This page is for developers who maintain theme template overrides or custom code that reads plugin data directly.

Who should read this

You only need this page if you have theme template overrides in yourtheme/anwp-football-leagues/ or custom PHP that calls get_post_meta() with plugin keys like _anwpfl_city, _anwpfl_table_main, or _anwpfl_multistage. Standard installs without code customizations do not need any changes.

🎯 What changed

  • Clubs moved to wp_anwpfl_clubs. The CMB2 metabox was replaced with a Vue editor.
  • Standings moved to wp_anwpfl_standings. 32 postmeta keys were consolidated. meta_query lookups became indexed WHERE queries.
  • Competitions moved to wp_anwpfl_competitions. 30 postmeta keys migrated. The CMB2 metabox was split into smaller sidebar metaboxes.

Upgrade timeline

The old postmeta keys still exist after the upgrade. Code that calls get_post_meta() against the old keys keeps working, so you don’t have to rewrite overrides the day 0.18.0 ships. But the postmeta is no longer kept in sync with new writes going through the custom tables, and the keys will be removed when you run the cleanup action:

Football Leagues > Settings & Tools > Toolbox > Cleanup β€” separate batch actions for clubs, competitions, and standings. After cleanup runs, the old postmeta is gone and any remaining get_post_meta() calls against the old keys return empty.

Recommended upgrade path

Audit your overrides against the tables below before running the Toolbox cleanup. Keep the postmeta around as a safety net until you’ve verified your theme still renders correctly with the new data access patterns.

How to check what needs updating on your site

Open Football Leagues > Settings & Tools > Template Status (FL Premium 0.18.0+). The page scans every FL template override in your active theme and any third-party plugins that hook into anwpfl_template_paths, compares @version docblock tags against the plugin defaults, and flags the exact lines using postmeta patterns that will break after Toolbox cleanup. Start there β€” it tells you which specific files need the changes described below, instead of reading the tables line-by-line.

Stable public API (no changes needed)

These helpers kept the same signatures and return values. If your override only uses these, you have nothing to change:

  • Clubs: get_club_logo_by_id(), get_club_link_by_id(), get_club_abbr_by_id(), get_club_title_by_id(), is_national_team()
  • Standings: get_standings(), tmpl_get_competition_standings() (same signature, now backed by an indexed query)
  • Competitions: get_competition_title(), get_competition_data_full() (primary accessor β€” returns formatted data with groups and rounds decoded)

Clubs

Replace get_post_meta() calls with the custom-table accessors. Use get_club_list_row() for common scalar fields (title, logo, main color), and get_row() when you need the full row including JSON-blob columns like club_details, club_social, or club_trophies.

Old patternNew pattern
get_post_meta( $id, '_anwpfl_city', true )anwp_fl()->club->get_row( (int) $id )['city'] ?? ''
$club->{$prefix . 'city'}$club_row['city'] ?? '' (from get_row())
get_all_clubs_data()[ $id ]anwp_fl()->club->get_club_list_row( (int) $id )
get_post_meta( $id, '_anwpfl_main_color', true )anwp_fl()->club->get_club_list_row( (int) $id )['main_color'] ?? ''
get_post_meta( $id, '_anwpfl_address', true )json_decode( anwp_fl()->club->get_row( (int) $id )['club_details'] ?? '', true )['address'] ?? ''
get_post_meta( $id, '_fl_pro_trophies', true )json_decode( anwp_fl()->club->get_row( (int) $id )['club_trophies'] ?? '', true )

get_all_clubs_data() still works but emits a deprecation notice. Replace it with warm_clubs() + get_club_list_row() as shown in the loop pattern below.

Standings

Standings used to spread across 32 postmeta keys per standing. They now live in a single row with JSON columns for the tables themselves and a consolidated columns_config JSON for display settings. If you loaded a standing by running a meta_query against _anwpfl_competition and _anwpfl_competition_group, use tmpl_get_competition_standings() instead β€” it takes the same arguments and runs an indexed query.

Old patternNew pattern
get_post_meta( $id, '_anwpfl_table_main', true )anwp_fl()->standing->get_standing_rows( $id ) (decodes JSON to objects)
get_post_meta( $id, '_anwpfl_table_main_home', true )json_decode( anwp_fl()->standing->get_row( $id )['table_main_home'] ?? '', true )
get_post_meta( $id, '_anwpfl_table_main_away', true )json_decode( anwp_fl()->standing->get_row( $id )['table_main_away'] ?? '', true )
get_post_meta( $id, '_anwpfl_table_notes', true )anwp_fl()->standing->get_row( $id )['table_notes'] ?? ''
get_post_meta( $id, '_anwpfl_table_colors', true )json_decode( anwp_fl()->standing->get_row( $id )['table_colors'] ?? '', true )
get_post_meta( $id, '_anwpfl_points_win', true )anwp_fl()->standing->get_row( $id )['points_win'] ?? 3
get_post_meta( $id, '_anwpfl_columns_order', true )json_decode( anwp_fl()->standing->get_row( $id )['columns_config'] ?? '', true )['order'] ?? []
get_post_meta( $id, '_anwpfl_arrows_dynamic_ranking_data', true )json_decode( anwp_fl()->standing->get_row( $id )['arrows_data'] ?? '', true )
meta_query with _anwpfl_competition + _anwpfl_competition_groupanwp_fl()->competition->tmpl_get_competition_standings( $competition_id, $group_id )

Tip β€” JSON decode shape

When the plugin decodes table_main internally (via get_standing_rows()), it decodes to objects, not associative arrays, so theme overrides that use $row->club_id keep working. If you call json_decode() yourself, pass true only if your code expects arrays.

Competitions

Competitions now have a two-tier accessor pattern. get_competition_list_row() returns the 23 most-used scalar fields (title, type, logo, stage info, bracket flags) β€” that’s what you want in a loop. get_row() returns the full row including the large JSON/TEXT columns (stage_groups, stage_rounds, tmpl_layout, custom_content_below, competition_roles). get_competition_data_full() wraps get_row(), applies logo inheritance for secondary stages, and decodes groups and rounds β€” use it when you’d previously read multiple postmeta keys together.

Fields available from get_competition_list_row()

Old patternNew pattern
get_post_meta( $id, '_anwpfl_type', true )anwp_fl()->competition->get_competition_list_row( (int) $id )['type'] ?? ''
get_post_meta( $id, '_anwpfl_multistage', true )...['multistage'] ?? ''
get_post_meta( $id, '_anwpfl_multistage_main', true )...['multistage_main'] ?? 0
get_post_meta( $id, '_anwpfl_stage_title', true )...['stage_title'] ?? ''
get_post_meta( $id, '_anwpfl_stage_order', true )...['stage_order'] ?? 0
get_post_meta( $id, '_anwpfl_competition_status', true )! empty( ...['is_friendly'] ) (column renamed and retyped to tinyint(1): 1 = friendly, 0 = official; old value 'friendly' maps to 1)
get_post_meta( $id, '_anwpfl_competition_order', true )...['competition_order'] ?? 0
get_post_meta( $id, '_anwpfl_logo', true )...['logo'] ?? ''
get_post_meta( $id, '_anwpfl_logo_big', true )...['logo_big'] ?? ''
get_post_meta( $id, '_anwpfl_matchweek_current', true )...['matchweek_current'] ?? 0
get_post_meta( $id, '_anwpfl_lazy', true )...['is_lazy'] ?? 0 (column renamed)
get_post_meta( $id, '_anwpfl_bracket', true )...['bracket'] ?? ''
get_post_meta( $id, '_anwpfl_bracket_layout_active', true )...['bracket_layout_active'] ?? ''
get_post_meta( $id, '_anwpfl_matchweeks_as_slides', true )...['matchweeks_as_slides'] ?? ''

Shorthand ... above means anwp_fl()->competition->get_competition_list_row( (int) $id ).

Fields that require get_row()

The fields below are large TEXT or JSON columns and are not in the light list. Asking for them via get_competition_list_row() returns null.

Old patternNew pattern
get_post_meta( $id, '_anwpfl_groups', true )json_decode( anwp_fl()->competition->get_row( (int) $id )['stage_groups'] ?? '' ) (objects)
get_post_meta( $id, '_anwpfl_rounds', true )json_decode( anwp_fl()->competition->get_row( (int) $id )['stage_rounds'] ?? '' ) (objects)
get_post_meta( $id, '_anwpfl_tmpl_layout', true )anwp_fl()->competition->get_row( (int) $id )['tmpl_layout'] ?? ''
get_post_meta( $id, '_anwpfl_custom_content_below', true )anwp_fl()->competition->get_row( (int) $id )['custom_content_below'] ?? ''
get_post_meta( $id, '_anwpfl_bracket_options', true )anwp_fl()->competition->get_row( (int) $id )['bracket_options'] ?? ''
get_post_meta( $id, '_anwpfl_role_competition_supervisor', true )anwp_fl()->competition->get_row( (int) $id )['competition_roles'] ?? '' (JSON)
get_post_meta( $id, '_anwpfl_format_robin', true )anwp_fl()->competition->get_row( (int) $id )['format_robin'] ?? ''
get_post_meta( $id, '_anwpfl_format_knockout', true )anwp_fl()->competition->get_row( (int) $id )['format_knockout'] ?? ''

Pitfall β€” list_row vs row

If the plugin warmed the light cache before your template ran (common for loops over matches or standings), calling get_competition_list_row() for a non-light field returns null silently β€” no warning. Use get_row() for anything in the table above, or call get_competition_data_full() when you need the full formatted record.

Warm-up pattern for loops

When rendering a list (matches, standings, player stats), collect the entity IDs first, warm the cache in one query, then call the per-ID accessors inside the loop. Every read after the warm hits the per-request cache with zero extra queries.

Clubs

$club_ids = wp_list_pluck( $games, 'home_club' );
anwp_fl()->club->warm_clubs( $club_ids );

foreach ( $games as $game ) {
    $club = anwp_fl()->club->get_club_list_row( (int) $game->home_club );
    // $club['title'], $club['logo'], $club['main_color'], etc.
}

Competitions (light)

Use warm_competitions() when the loop only needs scalar fields (title, logo, stage info).

$competition_ids = wp_list_pluck( $games, 'competition_id' );
anwp_fl()->competition->warm_competitions( $competition_ids );

foreach ( $games as $game ) {
    $comp = anwp_fl()->competition->get_competition_list_row( (int) $game->competition_id );
    // $comp['title'], $comp['logo'], $comp['multistage'], etc.
}

Competitions (full)

Use warm_competitions_full() when you need stage_groups, stage_rounds, or any of the full-row-only fields.

anwp_fl()->competition->warm_competitions_full( $competition_ids );

foreach ( $competition_ids as $competition_id ) {
    $data = anwp_fl()->competition->get_competition_data_full( (int) $competition_id );
    // $data['title'], $data['groups'], $data['rounds'], + all formatted fields
}

Deprecations

  • anwp_fl()->club->get_all_clubs_data() β€” deprecated since 0.17.3. Emits a runtime deprecation notice. Replacement: warm_clubs() + get_club_list_row().
  • anwp_fl()->competition->get_competition_group_standing() β€” deprecated in 0.18.0. Emits a runtime deprecation notice. Zero internal callers; kept only for theme override compatibility.
  • anwp_fl()->competition->get_competitions() β€” soft-deprecated. Prefer the lighter get_competitions_data(). The runtime notice is deferred until more internal callers migrate.

πŸ“š Related

  • REST API reference β€” the public REST API added in 0.18.0 reads from the same custom tables.