Site Migration

Pro Feature

Overview

Site Migration moves a competition (and all its connected data — clubs, players, staff, referees, matches, lineups, standings) from one Football Leagues site to another. The flow runs competition-by-competition: pick a competition on the donor, the recipient pulls it over the WP REST API using an Application Password, and recreates everything on the destination.

Use cases:

  • Moving a staging build to production
  • Consolidating multiple league sites into one master site
  • Offering a pre-configured league setup to a new customer
  • Cloning a competition between sister sites

The system is idempotent — re-running the same migration UPDATEs existing rows rather than creating duplicates. Safe to retry.

Prerequisites

RequirementWhy it matters
Both sites on FL+ Premium 0.18.0 or laterThe donor and recipient need matching schema (custom tables: anwpfl_clubs, anwpfl_competitions, anwpfl_standings). Earlier versions used postmeta — the table-to-table flow doesn’t downgrade.
Both sites fully migratedThe Toolbox Updater migration tasks (clubs, competitions, standings, squad) must show “Done” on both sides. Migration assumes data is in the new tables.
Application Password supportStandard WordPress REST authentication. Available since WP 5.6, no plugin needed.
Network reachabilityThe recipient pulls data over HTTPS from the donor. The donor must be publicly reachable (or at least reachable from the recipient’s network).
Donor REST API not blockedSome hosts/security plugins block /wp-json/. If REST is disabled or the anwpfl/v1/site-export/* routes return 404, migration can’t start.
Sufficient PHP timeout / memoryLarge competitions ship many matches and players. Default max_execution_time=30 is usually fine because the importer batches, but memory_limit < 256M can OOM on very large bundles.

🚀 Donor Setup

The donor is the source site. Site Migration on the donor is passive — it doesn’t push, it just exposes read-only endpoints that the recipient pulls from.

Step 1: Generate an Application Password

The recipient needs to authenticate to the donor’s REST API as an admin user.

  1. Go to Users → Profile on the donor site
  2. Scroll to Application Passwords
  3. Enter a name (e.g., “Site Migration Recipient”)
  4. Click Add New Application Password
  5. Copy the generated password — it shows once and never again
Application Passwords section on the donor site after generating a new password

Step 2: That’s it

No menu page on the donor side. The export endpoints register automatically when FL+ Premium 0.18.0 is active. The recipient discovers them when you provide the donor URL.

🚀 Recipient Setup

The recipient is where you want the data to land.

Step 1: Open the Site Migration page and connect to the donor

Navigate to Football Leagues → Settings & Tools → Site Migration (1, 2 in the screenshot).

Fill in the donor details:

  • Donor Site URL (3) — full URL with scheme, e.g., https://donor-site.com
  • WordPress Username (4) — admin user on donor whose Application Password you generated
  • Application Password (5) — paste the password from donor setup
  • Click Test Connection (6)
Site Migration connection form on the recipient with numbered annotations for menu, fields, and Test Connection button

Step 2: Confirm connection and fetch competitions

A successful connection shows the donor’s site name and plugin version. Click Fetch Competitions (1) to pull the list of available competitions from the donor.

Connected state on the recipient showing donor site name, plugin version, and the Fetch Competitions button

Step 3: Pick a competition

Click Select (1) next to the competition you want to migrate. Each competition shows ID, title, season, league, match count, and type (round-robin / knockout / multistage).

Competitions list pulled from the donor with Select buttons next to each competition row

Step 4: Check IDs

After selecting a competition, click Check IDs to run a pre-import scan. This compares donor post IDs against the recipient’s existing posts and shows what will conflict (if anything).

Check IDs button after a competition is selected, with arrow annotation pointing to the button

If the recipient has overlapping post IDs (typically WP attachments from earlier media uploads), an ID Conflicts panel lists them.

ID Conflicts panel listing donor post IDs that already exist on the recipient, with per-entity counts and the Import Competition button below

This is normal and safe

The importer assigns fresh IDs to anything that conflicts and tracks the mapping in anwpfl_import_mapping so future re-runs update the right rows.

When to pause and investigate:

  • The conflicts list shows your old FL data (not WP attachments). That means previous FL migration data is still on the recipient. Decide whether to keep it (re-running will UPDATE existing rows) or reset for a clean import.
  • The conflict count is very high (hundreds). On a busy recipient, the random remap is fine, but expect new IDs everywhere — bookmarks and external links to old recipient post URLs will break.

Step 5: Import the competition

Once the ID check is clean (or you’ve accepted the conflicts), the Import Competition button appears with a summary of what’s coming over (clubs, players, staff, matches, etc.).

Pre-import summary showing entity counts and the Import Competition button

Click Import Competition to start. The importer runs through 21 batched steps in order: taxonomies → media → stadiums → clubs → players → staff → referees → competitions → standings → matches → custom-table data → standings recalc → lineups → formations → club history. Each step shows progress, a per-step result count, and a live log.

Migration mid-flight showing progress bar, current step (Clubs), per-step result table, and live log entries
Final success state showing 'Import completed! All 21 steps finished.' banner with per-step result counts

Step 6: Verify on the recipient

Open the imported competition page on the recipient frontend. Standings, fixtures, clubs, and players should all render.

What Gets Migrated

Migrated

EntityWhere it lands on recipient
Competition (main + secondary stages)anwpfl_competitions + wp_posts (anwp_competition)
Clubs (incl. subteam clubs)anwpfl_clubs + wp_posts (anwp_club)
Playerswp_posts (anwp_player) + anwpfl_players + anwpfl_player_data
Staffwp_posts (anwp_staff)
Refereeswp_posts (anwp_referee)
Matchesanwpfl_matches
Lineups + match eventsanwpfl_lineups
Standings (with auto-recalculation)anwpfl_standings
Seasons + leagues + tagsCustom taxonomies
Logos + photosUploaded to recipient’s media library

After import, the recipient automatically recalculates standings against locally-imported matches — so totals reflect what the recipient actually has, not a frozen snapshot from the donor.

Not migrated (by design)

EntityWhy not
User accountsUser IDs don’t map between sites. Recipient admins keep their existing accounts.
Competition supervisor assignmentsDonor user IDs would point to nonexistent recipient users. Re-assign supervisors manually after migration.
WordPress posts/pages outside FLSite Migration only handles FL data. Use a generic WP migration tool for everything else.
Plugin settings (Customizer, FL Settings, Configurator)Settings stay on each site. Configure recipient settings to match if needed.
API-Football wizard mappingsDonor’s external API IDs don’t map cleanly. The wizard has its own re-import flow.

Idempotency: Re-Running Is Safe

Running the same migration a second time:

  • UPDATEs existing competitions, clubs, players (matched by external ID / donor post ID, tracked in anwpfl_import_mapping)
  • Adds new matches that appeared on the donor since the last run
  • Does NOT create duplicate clubs or competitions
  • Does NOT touch entities that already exist on the recipient under different IDs (you’d see those as separate rows)

This means you can:

  • Run a full migration, then incrementally re-run after adding more matches on the donor
  • Recover from a partial failure by simply re-running

How to update (re-import a competition)

The expected workflow when donor data has changed (new fixtures added, results updated, lineups filled, players transferred, etc.):

  1. On the donor, edit/import as usual. Save changes.
  2. On the recipient, open Football Leagues → Settings & Tools → Site Migration.
  3. The donor connection (URL + Application Password) is remembered — click Refresh under Competitions to pull the latest list from the donor. No need to disconnect/reconnect.
  4. Find the competition you migrated previously and click Migrate again.
  5. The importer detects existing rows via anwpfl_import_mapping and runs UPDATE statements instead of INSERTs. Fresh matches appear, modified results update in place.
  6. The post-import standings recalc runs automatically against the now-current matches.

Things to know about updates:

  • Donor post IDs that already mapped to recipient IDs stay mapped — your post URLs don’t change.
  • Competition supervisors (if you assigned them on the recipient) are preserved — supervisor data is excluded from import.
  • Custom recipient data NOT in the donor (e.g., shortcodes you added to a club’s description) survives the update.
  • If you renamed a competition on the donor between runs, the recipient’s post title gets the new name.
  • Manual ID re-numbering on the recipient (highly unusual) breaks the mapping. Avoid editing post IDs directly.

When NOT to use update:

  • If the donor data has been fundamentally restructured (e.g., a competition got split into multiple stages, or stages were renumbered) — reset the recipient and start fresh.
  • If you’re moving to a brand-new recipient site and want pristine data with no leftovers from earlier test runs.

Failure Modes & Recovery

Connection test fails

SymptomCauseFix
401 UnauthorizedWrong username or App PasswordRe-generate Application Password on donor, paste fresh
403 ForbiddenUser lacks manage_optionsUse an admin account on donor
404 on the export endpointFL+ Premium not active on donor, or REST API blockedCheck donor plugin status; check security plugins blocking /wp-json/
Timeout / no responseDonor unreachable, slow host, or Cloudflare blocking RESTCheck donor URL in browser; whitelist recipient IP if firewalled

Migration runs but data missing on recipient

SymptomCauseFix
Subteam clubs not visibleDonor subteam list never re-saved through the Vue editorRe-save affected parent clubs on donor, re-run migration
Standings show wrong totalsRecipient has fewer matches than donor (transient errors during import)Re-run migration — the standings recalc step recomputes against locally-imported matches
Player photos missingImage optimization plugin on recipient filtering attachment URLsDisable image optimization plugin temporarily, re-run player imports
Logo URLs point to donor domainBundle synthesis didn’t fully rewrite media URLsRe-run import to retrigger media copy step

Mid-migration interruption

If the browser tab closes or network drops mid-flight:

  • Re-open the Site Migration page
  • Click the same competition again — the importer skips already-migrated entities and resumes
  • No manual cleanup required

After-Migration Verification Checklist

Run through this list once each migration completes:

CheckHow
Competition page renders on recipient frontendVisit the imported competition URL
Standings populatedStanding widget shows clubs, points, GD
Fixtures populatedMatch list shows scheduled and finished matches
Club pages renderClick into a club from the standings — squad, fixtures, history all show
Player pages renderClick into a player from a club squad — stats, match history visible
Subteam links workIf parent clubs have subteams, click a subteam in the parent’s profile — should land on subteam page, not 404
Standings totals match donorSpot-check 2-3 standings rows against the donor site
Re-supervise competitionsRe-assign supervisor users (excluded from migration by design)
Update competition tags / featuredIf donor used WP post_tag taxonomy or featured flags, those need re-tagging on recipient

🆘 Troubleshooting

Migration finishes but no progress shown

Problem: UI shows “complete” immediately with no per-phase output.

Solution:

  • Open browser DevTools Network tab
  • Look for failed REST calls to /wp-json/anwpfl/...
  • Check recipient debug log for fatal errors
  • Verify both sites are on FL+ Premium 0.18.0+

“Migration Required” notice appears

Problem: After installing 0.18.0, recipient shows a Migration Required banner.

Solution:

  • Open Football Leagues → Settings & Tools → Toolbox → Updater
  • Run any pending migration tasks (clubs, competitions, standings, squad) to completion
  • Re-open Site Migration page — banner should be gone

Re-running creates new rows instead of updating

Problem: Each re-run adds duplicate clubs / competitions instead of updating.

Solution: This shouldn’t happen — file a bug report with site URLs and competition ID. As a workaround, delete the duplicate rows and the anwpfl_import_mapping rows for affected entities, then re-run.

Reset before re-running

If you want a completely clean recipient before re-running migration (e.g. you migrated test data and now want production data), the safest path is to start with a fresh recipient site:

  • On a brand-new WordPress install (no FL content yet), just install FL Core + Premium and run the migration — nothing to clean up.
  • On an existing site that already has FL content from earlier tests, take a full database backup, then ask your host (or use a plugin like WP Reset) to wipe all posts and uploaded media before re-running migration.

Warning

Always take a database backup before any reset operation. If you’re unsure how to clean an existing recipient safely, contact support before doing anything destructive.

📚 Related Documentation