SmartIMPORT: Custom code features

SmartIMPORT: Custom code features

There are two custom code options within SmartIMPORT. First, the 'Start code' block runs at the start of the import process with the spreadsheet row data available. Second, the 'Search code' which runs just after we have searched for a matching record in the database. You are able to see data on that record and your incoming data and stop the process or output custom data fields.
Basics
Start code
Search code
Advanced
Basics
Common to all the custom code are the following.


1. PHP code

We use PHP code for all the custom code within SmartIMPORT

Need help with PHP?


2. Variables & Coding Restrictions

To ensure security and stability, the coding environment has specific constraints.

Variable Naming ($a - $z)

You may use internal variables for calculations, but you are strictly limited to single-letter variable names ranging from $a to $z.

  • ✅ Allowed: $x = 10;

  • ❌ Error: $total_price = 10;

Writing/Returning Data

You do not need to use a return statement. Modify the $output array directly. You can change existing values or add new keys.

Safe Expressions

The PHP environment is restricted to "safe expressions." Complex functions or external calls may result in errors.


3. The spreadsheet event data

The data made available to your code will include the spreadsheet information. The array keys of that data are automatically formatted:

  • Lowercase: All column headers are converted to lowercase.

  • Underscores: Spaces and special characters are replaced with _.

Tip: If you are unsure what your array keys look like, check your previous logs. They will show exactly how the system has renamed your headers.


4. Controlling Import Flow

You can use the global variable $output to skip rows or trigger warnings and errors based on your logic.

ActionCode ImplementationResult
Skip Row$output['success'] = 'Success message';The row is ignored. Processing continues to the next row without error.
Trigger Error$output['error'] = 'Invalid SKU';The import stops immediately with an error.
Trigger Warning$output['error'] = 'Warning: Stock low';The import logs a warning and stops processing (if prefix is 'Warning: ').


Start code
SmartIMPORT includes a powerful feature that lets you run custom logic on your spreadsheet data before it undergoes standard processing. This is ideal for cleaning data, combining columns, or validating rows on the fly. This guide explains how to write compliant code for the SmartIMPORT engine.


1. The Basics: The $output Variable

The core of this feature is the global $output variable. This variable acts as both your input (reading the spreadsheet) and your output (sending data to SmartIMPORT). When your code runs, $output is already populated with the data from the current spreadsheet row. 

You do not need to use a return statement. Modify the $output array directly. You can change existing values or add new keys.


2. Practical Examples

Example A: Combining Columns

Scenario: Your spreadsheet has "First Name" and "Last Name", but the system needs a "Full Name" field.

// Define a temporary variable for spacing
$s = ' '; 

// Combine the normalized keys into a new key
$output['full_name'] = $output['first_name'] . $s . $output['last_name'];

Example B: Conditional Logic & Math

Scenario: You want to add a 20% tax to the price, but only if the "Taxable" column is marked "Yes".

// Check if item is taxable
if ($output['taxable'] == 'Yes') {
    // Use $p as a temporary variable for the calculation
    $p = $output['price'];
    $output['price_inc_tax'] = $p * 1.20;
} else {
    $output['price_inc_tax'] = $output['price'];
}

Example C: Validation (Skipping Rows)

Scenario: Skip the row entirely if the "Status" column is set to "Inactive".

if ($output['status'] == 'Inactive') {
    // Set output to null to stop processing this specific row
    $output = null;
}

Search code
In the SmartIMPORT/SmartSYNC flow, you can run custom PHP code after the system has attempted to find a matching record but before the final import/update. This is the "Decision Step." It allows you to compare your incoming spreadsheet data against the existing database record to decide precisely how—or if—the import should proceed.

1. Available Data Objects

In this stage, you have access to the $input array, which contains two crucial sets of data:

A. The Incoming Data ($input['event'])

This contains the raw data from your spreadsheet row (normalised with lowercase keys), precisely as it appeared in the initial processing step.

B. The Search Result ($input['search'])

This object contains the results of the system's attempt to match the row to an existing constituent. It tells you whether a match was found and provides details about that record.


2. The Search Object Structure

To write effective logic, you need to understand the structure of $input['search']. Below is a reference schema showing the most common fields.

Note: The cons object contains the existing database record. If no match was found, some of these fields may be null or empty. The below is a JSON equivalent of the array based $input['search'] value. As an example $input['search']['cons'] will access the constituent data and $input['search']['cons']['gender'] will return 'Male'.
{
  "id": "185",                     // The System ID of the found record
  "lookup_id": "1001",             // The user-facing ID
  "record_already_exists": true,   // Boolean: true if a match was found
  "cons": {                        // The "Constituent" details object
      "id": "185",
      "first": "John",
      "last": "Doe",
      "title": "Mr.",
      "type": "Individual",
      "gender": "Male",
      "birthdate": {
          "d": 14,
          "m": 3,
          "y": 1980
      },
      "address": {
          "address_lines": "123 Main St",
          "city": "Denver",
          "state": "CO",
          "postal_code": "80202",
          "country": "United States",
          "type": "Home",
          "do_not_mail": false
      },
      "email": {
          "address": "john.doe@example.com",
          "type": "Email",
          "do_not_email": false
      },
      "phone": {
          "number": "(555) 123-4567",
          "type": "Home",
          "do_not_call": false
      },
      "spouse": {
          "first": "Jane",
          "last": "Doe"
      }
  }
}

3. Practical Examples

You can use this data to perform complex logic. As with the pre-processing step, you modify the global $output variable to control the flow.

Example A: Skip Import Based on Criteria

Scenario: If the record already exists and is marked as "Deceased" in the database, do not process this row.

// Check if match exists
if (array_key_exists('record_already_exists', $input['search']) && 
      $input['search']['record_already_exists']) {
    // Check the 'deceased' flag in the 'cons' object
    if ($input['search']['cons']['deceased'] == true) {
        // Set output to null to stop processing this row entirely
        $output = null; 
    }
}

Example B: Comparing Incoming vs. Existing Data

Scenario: You want to log a warning if the spreadsheet address is different from the database address.

if (array_key_exists('record_already_exists', $input['search']) && 
      $input['search']['record_already_exists']) {
      // Get the database zip code (from search/cons) $z = $input['search']['cons']['address']['postal_code']; // Get the spreadsheet zip code (from event) $y = $input['event']['zip_code']; // Assuming column header is 'Zip Code' // Compare first 5 digits if (substr($z, 0, 5) != substr($y, 0, 5)) { // Stop the flow with a warning
            $output['error'] = 'Warning: Address mismatch detected.'; } }
Advanced
SmartIMPORT allows you to perform advanced operations, such as looking up data from other spreadsheets, by passing a specific command structure back to the system.

This process works in a two-step cycle:

  1. Request Phase: Your code checks if data is missing. If so, it assigns a command array to $output. The system detects this, pauses your code, and fetches the data.

  2. Processing Phase: The system re-runs your code, this time populating $output with the requested data and a completion flag.

The Command Structure

To trigger a function, you must structure the $output['command'] array as follows:

$output['command'] = [
    'function' => 'lookup',
    'arguments' => [
        'use_default' => 'true',
        'return_rows' => false,   // Set to true if you need the full row data
        'mode' => 'normal',
        'searches' => [
            [
                'spreadsheet' => '123456789', // The ID of the external sheet
                'search' => 'value_to_find',  // The value to search for
                'matching' => 'exact'         // 'exact' or 'contains'
            ]
        ]
    ]
];

Handling the Results

When the system returns the data, two new keys are available in the global variable:

  • $output['lookups_done']: (Boolean) Returns true indicates the lookup has finished.

  • $output['lookups_data']: (Array) Contains the results of the search.

Example: Cross-Referencing a Spreadsheet

Scenario: You are importing orders. You have a product_id but need the product_name one that lives in a different spreadsheet (ID 987654321).

// Step 1: Check if the lookup has been performed yet
if (empty($output['lookups_done'])) {
    // PREPARE THE LOOKUP
    // We want to search the external sheet for the product_id from the current row
    $output['command'] = [
        'function' => 'lookup',
        'arguments' => [
            'use_default' => 'true',
            'return_rows' => false,
            'mode' => 'normal',
            'searches' => [
                [
                    'spreadsheet' => '987654321', // The external Product Database ID
                    'search' => $output['product_id'],
                    'matching' => 'exact'
                ]
            ]
        ]
    ];
} else {
    // Step 2: Process the results
    // The system has returned with the data. 
    // Remember to use single-letter variables ($a-$z).
    
    $d = $output['lookups_data'];
    
    // Assuming the lookup returns an array where the first item is the result
    if (!empty($d)) {
        $output['product_name'] = $d[0];
    } else {
        $output['product_name'] = 'Unknown Product';
    }
}
Note: When using library functions, ensure your logic is wrapped in an if/else block checking for lookups_done. Without this check, the code may enter an infinite loop of requesting data.
 

    • Related Articles

    • How SmartIMPORT Manages Duplicates and Matching for Continuous Data Flow

      Duplicates and Matching for Continuous Data Flow SmartIMPORT uses a flexible, rule-based matching engine designed to keep your data flowing into Raiser’s Edge NXT. Instead of stopping an entire import when a potential duplicate is found, SmartIMPORT ...
    • SmartIMPORT: Data 'Mappings' Guide

      Understanding Mappings (Dictionaries) Note for Omatic Users: In SmartIMPORT, we use the term Mappings. These are the functional equivalent of the Dictionaries you may be used to in other import tools. Why Map Data? External data is rarely perfect or ...
    • SmartPORTAL: User login API features

      SmartPORTAL allows users to log in to your website using their Raiser's Edge NXT record. This means they can use the various SmartPORTAL forms such as login, profile updates, add action and the callhandler function. On top of this it also provides a ...
    • SmartCHECKOUT: GiveWP Export donations with custom fields i.e. BBMS Transaction ID

      You may wish to do financial reconciliation between your GiveWP donations and Raiser's Edge NXT's gifts. BBMS Is Blackbaud Merchant Services and is the payment provider of choice for Blackbaud clients. Unfortunately GiveWP do not by default export ...
    • SmartIMPORT: Transforms Guide

      Basic Transforms No Spaces Removes every space in the value. Example: "AC 23 7 B" → "AC237B" Not Blank Checks whether the field contains anything. Returns true if filled, false if empty. Example: "Jones" → true, "" → false Phone Removes everything ...