MdMasud

WordPress, Laravel, Flutter

Automating Postcode Area Coverage in WordPress with ACF and CSV Import

In a recent project, I had to bulk-import postcode data into a WordPress site using Advanced Custom Fields (ACF). Each postcode had to be linked to a custom taxonomy term (location), and each term had a repeater field named postcode_area_coverage, which stores multiple entries under a subfield called code.

The data came from a simple CSV file provided by the business manager. Instead of manually entering data term by term, I wrote a script that matched postcodes to taxonomy terms based on email addresses and updated everything programmatically.

    The Data Format

    The CSV I received looked like this:

    EH1,,branch1@email.com  
    EH2,,branch1@email.com  
    AB10,,branch2@email.com  
    

    The logic was straightforward: match each postcode to the correct location using the associated email, then populate the repeater field accordingly.

    I wrote a PHP script that handled the following:

    • Loaded all location terms from the taxonomy.
    • Built a reference map using each term’s ACF email field.
    • Read the CSV file row by row.
    • Matched each row’s email to a location term.
    • Grouped the postcodes by term ID.
    • Updated the postcode_area_coverage repeater using update_field().

    The Code.

    <?php
    
    include('wp-load.php'); // adjust path as needed to load WordPress context
    
    $location_ref = [];
    $taxonomy = 'location';
    
    // Step 1: Build reference map of emails to term IDs
    $locations = get_terms([
        'taxonomy' => $taxonomy,
        'hide_empty' => false,
    ]);
    
    if (!empty($locations) && !is_wp_error($locations)) {
        foreach ($locations as $location) {
            $email_field = get_field('email', 'location_' . $location->term_id);
            if ($email_field) {
                $location_ref[trim($email_field)] = [
                    'term' => $location->term_id,
                    'email' => trim($email_field)
                ];
            }
        }
    }
    
    $postcodes_csv = 'postcode.csv'; // path to your CSV file
    $data_array = [];
    $orphan_postcode = [];
    
    if (($fh = fopen($postcodes_csv, 'r')) !== false) {
        while (($data = fgetcsv($fh, 1000, ',')) !== false) {
            $postcode = trim($data[0] ?? '');
            $email = trim($data[2] ?? '');
    
            if ($postcode) {
                if ($email && isset($location_ref[$email])) {
                    $term_id = $location_ref[$email]['term'];
                    $data_array[$term_id][] = ['code' => $postcode]; // 'code' is the ACF subfield
                } else {
                    $orphan_postcode[] = $postcode;
                }
            }
        }
        fclose($fh);
    }
    
    // Step 2: Update each term’s repeater field
    foreach ($data_array as $term_id => $postcodes) {
        update_field('postcode_area_coverage', $postcodes, $taxonomy . '_' . $term_id);
    }
    
    // Optional: Printing orphan postcodes for debugging
    if (!empty($orphan_postcode)) {
        echo "Postcodes with no matching email:<br>";
        echo implode(', ', $orphan_postcode);
    }
    
    

    What This Does

    • Reads the CSV line by line.
    • Trims and sanitises inputs to avoid matching issues.
    • Maps each row to the correct taxonomy term using the email ACF field.
    • Builds an array in ACF repeater format ([ ['code' => 'EH1'], ... ]).
    • Uses update_field() to push postcode data into each term.

    Why I Like This Approach

    • It’s fast and scalable.
    • It makes use of existing ACF structures — no custom database tables or plugins needed.
    • It’s repeatable: anytime the business updates postcode data, I just run the script again.

    This was a small but satisfying piece of dev work — a perfect example of automating the boring stuff. If you’re working with taxonomy terms, ACF, or bulk content in WordPress, scripting these kinds of updates can save hours of time and prevent manual errors.

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *