MdMasud

WordPress, Laravel, Flutter

Category: PHP

  • How I Replaced a Static PHP Product Array with a WordPress Custom Post Type + ACF + Database Sync

    How I Replaced a Static PHP Product Array with a WordPress Custom Post Type + ACF + Database Sync

    If you’ve hardcoded product data into a PHP file and later needed it to be editable from the admin panel without breaking your existing database logic — here’s exactly how I handled that.

    The Problem

    We had a static array inside a PHP class:

    $merch = TCA_Pos::MERCH;

    Which populated a merch section like this:

    <div class="merch-product" data-item-id="<?= $item['id'] ?>">
    	<span class="name"><?= $item['name'] ?></span>
    	...
    </div>

    This array was also linked (by ID) to entries in a custom table: wp_pos_order_merch.

    Updating a product required editing PHP and coordinating updates in the database. That had to change.

    The Goal

    • Move product data into WordPress Custom Post Type (merch)
    • Use ACF for cost and options fields
    • Allow admin users to manage everything from the backend
    • Keep existing database entries consistent by syncing old item_id values

    Step-by-Step: What I Did

    1. Created the CPT

    register_post_type('merch', [
    	'label' => 'Merch',
    	'public' => false,
    	'show_ui' => true,
    	'show_in_menu' => true,
    	'supports' => ['title', 'editor', 'thumbnail'],
    	'capability_type' => 'post',
    	'map_meta_cap' => true,
    	'menu_icon' => 'dashicons-cart',
    ]);

    2. Created ACF Fields

    • cost (number)
    • options (repeater of text values)

    3. Wrote an Importer Script

    This CLI script:

    • Deletes all old merch posts
    • Re-creates them from the static array
    • Captures a mapping between the old static id and the new post_id
    • Updates wp_pos_order_merch accordingly
    $wp_ref_ID_array[] = ['db_id' => $item['id'], 'new_id' => $post_id];
    
    foreach ($wp_ref_ID_array as $ref) {
    	$wpdb->update('wp_pos_order_merch', ['item_id' => $ref['new_id']], ['item_id' => $ref['db_id']]);
    }

    4. Replaced Front-End Static Array

    Now, instead of loading TCA_Pos::MERCH, I fetch merch posts dynamically:

    $posts = get_posts(['post_type' => 'merch', 'numberposts' => -1]);
    
    foreach ($posts as $post) {
    	$merch[] = [
    		'id' => $post->ID,
    		'name' => $post->post_title,
    		'cost' => get_field('cost', $post->ID),
    		'options' => array_column(get_field('options', $post->ID) ?: [], 'option'),
    	];
    }

    Why This Works

    • Data is now fully managed via WordPress admin
    • Existing item_id relationships remain valid
    • It’s scalable: new merch, price changes, sizes — no more code edits
    • It’s safe: your legacy table remains usable

    What Brought You Here

    If you’re searching for:

    • “How to migrate static PHP array to WordPress CPT”
    • “Sync CPT with custom database table”
    • “Use ACF to replace static data”
    • “WordPress CLI content importer with ID remapping”

    …this solution gives you a clean, flexible, future-proof way to get it done.

    If you need to update your current site or modernise how your data is managed get in touch, I’d be happy to help.

  • How I Automatically Routed Service Requests to the Right Branch in WordPress Using Gravity Forms, ACF, and Postcode Matching

    How I Automatically Routed Service Requests to the Right Branch in WordPress Using Gravity Forms, ACF, and Postcode Matching

    One of the most rewarding things about working with WordPress is finding elegant ways to connect the front end with real-world business logic. Recently, I built a feature for a client that automatically routes customer service requests to the correct branch of the company — based entirely on the postcode entered in a Gravity Form.

    Here’s a breakdown of how I did it using Gravity Forms, Advanced Custom Fields (ACF), and some clean PHP logic.

    The Problem: Which Branch Covers This Area?

    The client has multiple branches across the UK, each covering a specific set of postcode areas — like EH12, AB10, G1, etc. When a customer submits a service request through the form on their website, the business needs that request to be automatically forwarded to the branch responsible for that postcode.

    Manual routing wasn’t an option. It had to be smart, scalable, and easy to manage.

    The Stack

    • WordPress (custom theme)
    • Gravity Forms for form handling
    • Advanced Custom Fields (ACF) to attach postcode coverage info to taxonomy terms
    • Custom PHP logic to handle the postcode matching and data routing

    The Data Structure

    Each branch is stored as a custom taxonomy term (location) in WordPress. Each term has an ACF repeater field called postcode_area_coverage, and inside that repeater, a subfield named code holds the outward postcode areas like EH12, G1, AB10.

    So the structure looks like this:

    location (taxonomy term)
    └── postcode_area_coverage (repeater)
    ├── code: EH1
    ├── code: EH2
    └── code: EH12

    The Gravity Form

    The form collects the usual user details, including a postcode field. Behind the scenes, I validate the format of the postcode, extract the outward part (everything before the space), and match it against the postcode coverage of each branch.

    Once matched, I automatically store the matched branch in a hidden Gravity Forms field. That field can then be used to:

    • Route notifications
    • Display confirmation messages
    • Track analytics

    Extracting the Outward Code from a Full Postcode

    if (preg_match('/^([A-Z]{1,2}[0-9][0-9A-Z]?)/', strtoupper(trim($postcode)), $matches)) {
        $outward = $matches[1]; // e.g. "EH12" from "EH12 5AB"
    }

    This snippet takes the full UK postcode submitted in the form and extracts the outward code (first part). That’s what I use to match against each branch’s coverage.

    Matching the Postcode to a Branch via ACF Repeater

    $terms = get_terms(['taxonomy' => 'location', 'hide_empty' => false]);
    foreach ($terms as $term) {
        $coverage = get_field('postcode_area_coverage', 'location_' . $term->term_id);
        foreach ($coverage as $row) {
            if (strtoupper($row['code']) === $outward) {
                return $term->term_id; // Match found
            }
        }
    }

    Here, I loop through each location taxonomy term, check their ACF postcode coverage, and find a match with the user’s outward postcode. Once matched, I return that branch’s ID.

    Updating a Gravity Form Field After Submission

    GFAPI::update_entry_field($entry['id'], self::LOCATION_SELECTED_FROM_POSTCODE, $branch_id);

    After finding the correct branch, I updated a hidden field in the submitted Gravity Form entry so it stores which branch should handle the request. This value is later used to route notifications.

    At the End

    • The business now handles customer requests faster and more efficiently.
    • Postcode coverage can be updated by non-technical users directly in the WordPress admin.
    • The system is scalable — just add a new location with postcode areas, and it’s instantly supported.

    It’s always fun to bridge the gap between user input and smart business logic. This was a great example of how custom taxonomy, ACF, and Gravity Forms can work together to create something genuinely useful — with no need for heavyweight plugins or complex integrations.

  • 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.