Skip to content

Instantly share code, notes, and snippets.

@spivurno
Last active October 20, 2021 10:19
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save spivurno/c3526301a97a8ea26570 to your computer and use it in GitHub Desktop.
Save spivurno/c3526301a97a8ea26570 to your computer and use it in GitHub Desktop.
Gravity Wiz // Gravity Forms // AJAX Popluation
<?php
/**
* Gravity Wiz // Gravity Forms // AJAX Population
*
* Populate one or many fields from a remote data source (only .csv files are currently supported) based on the selected value of a field on the form.
*
* Example: You have multiple brands of lumber. The lumber comes in different sizes. You can store each lumber brand and its corresponding length, width, and height in a spreadsheet (csv format) and then populate this data into separate length, width and height fields on the form when a lumber brand is selectd from a drop down field on the form.
*
* @version 1.1
* @author David Smith <david@gravitywiz.com>
* @license GPL-2.0+
* @link http://gravitywiz.com/...
*/
class GW_AJAX_Population {
protected static $is_script_output = false;
public function __construct( $args = array() ) {
// set our default arguments, parse against the provided arguments, and store for use throughout the class
$this->_args = wp_parse_args( $args, array(
'form_id' => false,
'target_form_id' => false,
'trigger_field_id' => false,
'data_map' => array()
) );
// map synonyms
if( $this->_args['target_form_id'] ) {
$this->_args['form_id'] = $this->_args['target_form_id'];
}
// do version check in the init to make sure if GF is going to be loaded, it is already loaded
add_action( 'init', array( $this, 'init' ) );
}
function init() {
// make sure we're running the required minimum version of Gravity Forms
if( ! property_exists( 'GFCommon', 'version' ) || ! version_compare( GFCommon::$version, '1.8', '>=' ) ) {
return;
}
// time for hooks
add_filter( 'gform_pre_render', array( $this, 'load_form_script' ) );
add_filter( 'gform_register_init_scripts', array( $this, 'add_init_script' ) );
add_action( 'wp_ajax_gwap_request_data', array( $this, 'ajax_request_data' ) );
add_action( 'wp_ajax_nopriv_gwap_request_data', array( $this, 'ajax_request_data' ) );
}
function load_form_script( $form ) {
if( $this->is_applicable_form( $form ) && ! self::$is_script_output ) {
$this->output_script();
}
return $form;
}
function output_script() {
?>
<script type="text/javascript">
( function( $ ) {
window.GWAJAXPop = function( args ) {
var self = this;
// copy all args to current object: (list expected props)
for( prop in args ) {
if( args.hasOwnProperty( prop ) )
self[prop] = args[prop];
}
self.init = function() {
$triggerField = $( '#input_' + self.formId + '_' + self.triggerFieldId );
$triggerField.change( function() {
var value = $( this ).val();
if( ! value ) {
self.populateValues( self.getEmptyMapping() );
return;
}
$.post( self.ajaxUrl, {
action: 'gwap_request_data',
form_id: self.formId,
trigger_field_id: self.triggerFieldId,
trigger_value: $( this ).val()
}, function( response ) {
response = $.parseJSON( response );
if( response.is_error ) {
self.populateValues( self.getEmptyMapping() );
} else {
var mapping = response.data;
self.populateValues( mapping );
}
} );
} );
};
self.populateValues = function( values ) {
$.each( values, function( fieldId, value ) {
$( '#input_' + self.formId + '_' + fieldId ).val( value );
} );
};
self.getEmptyMapping = function() {
var mapping = {};
$.each( self.dataMap, function( column, fieldId ) {
mapping[ fieldId ] = '';
} );
return mapping;
};
self.init();
}
} )( jQuery );
</script>
<?php
self::$is_script_output = true;
}
function add_init_script( $form ) {
if( ! $this->is_applicable_form( $form ) ) {
return;
}
$args = array(
'formId' => $this->_args['form_id'],
'triggerFieldId' => $this->_args['trigger_field_id'],
'dataMap' => $this->_args['data_map'],
'ajaxUrl' => admin_url( 'admin-ajax.php' )
);
$script = 'new GWAJAXPop( ' . json_encode( $args ) . ' );';
$slug = implode( '_', array( 'gw_ajax_pop', $this->_args['form_id'], $this->_args['trigger_field_id'] ) );
GFFormDisplay::add_init_script( $this->_args['form_id'], $slug, GFFormDisplay::ON_PAGE_RENDER, $script );
}
function ajax_request_data() {
$form_id = rgpost( 'form_id' );
$trigger_field_id = rgpost( 'trigger_field_id' );
if( $form_id != $this->_args['form_id'] || $trigger_field_id != $this->_args['trigger_field_id'] ) {
return;
}
$trigger_value = rgpost( 'trigger_value' );
$data = $this->get_data( $trigger_value );
$response = array();
if( empty( $data ) ) {
$response['is_error'] = true;
$response['message'] = __( 'No data found.' );
} else {
$response['is_error'] = false;
$response['data'] = $data;
}
die( json_encode( $response ) );
}
function get_data( $source_value ) {
if( is_callable( $this->_args['ajax_callback'] ) ) {
$data = call_user_func( $this->_args['ajax_callback'], $source_value, $this );
} else if( $this->_args['file'] ) {
$file_data = $this->get_data_from_file( $source_value );
$data = $this->sub_columns_for_field_ids( $this->get_row_by_value( $source_value, array_search( $trigger_field_id, $this->_args['data_map'] ), $file_data ) );
}
return $data;
}
function get_data_from_file( $file ) {
$data = array();
$contents = file_get_contents( $file );
$lines = strpos( $contents, "\n" ) ? explode( "\n", $contents ) : explode( "\r", $contents );
foreach( $lines as $line ) {
$bits = explode( ',', $line );
$row = array();
foreach( $bits as $index => $bit ) {
$row[ $this->digits_to_letters( $index ) ] = $bit;
}
$data[] = $row;
}
return $data;
}
function get_row_by_value( $value, $column, $data ) {
foreach( $data as $row ) {
if( $row[ $column ] == $value ) {
return $row;
}
}
return false;
}
function sub_columns_for_field_ids( $row ) {
$_row = array();
foreach( $row as $column => $value ) {
$_row[ $this->sub_column_for_field_id( $column ) ] = $value;
}
return $_row;
}
function sub_column_for_field_id( $column ) {
$field_id = rgar( $this->_args['data_map'], $column );
return $field_id ? $field_id : $column;
}
function digits_to_letters( $input ) {
return strtr( $input, '0123456789', 'ABCDEFGHIJ' );
}
function is_applicable_form( $form ) {
$form_id = isset( $form['id'] ) ? $form['id'] : $form;
return $form_id == $this->_args['form_id'];
}
}
# Configuration
$tt_ajax_pop = new GW_AJAX_Population( array(
'source_form_id' => 1108, // form on which the unique ID was generated
'source_field_id' => 15, // the field ID of the unique ID
'target_form_id' => 1109, // the form that data will be populated into
'trigger_field_id' => 9, // the field that the unique ID will be entered into and trigger the population
'data_map' => array( // a map of field IDs from the source form that should be mapped into field IDs on the target form
11 => 10 // 11 = source form field ID, 10 = target form field ID
),
'ajax_callback' => function( $source_value, $ajax_pop_obj ) {
$entries = GFAPI::get_entries( $ajax_pop_obj->_args['source_form_id'], array(
'field_filters' => array(
array(
'key' => $ajax_pop_obj->_args['source_field_id'],
'value' => $source_value
)
)
) );
if( empty( $entries ) ) {
$entry = array();
} else {
$entry = array_shift( $entries );
}
$data = array();
foreach( $ajax_pop_obj->_args['data_map'] as $source_field_id => $target_field_id ) {
$data[ $target_field_id ] = rgar( $entry, $source_field_id );
}
return $data;
}
) );
new GW_AJAX_Population( array(
'form_id' => 636,
'trigger_field_id' => 99,
// an array of CSV column names and their corresponding field IDs (from the form)
'data_map' => array(
'A' => 99,
'B' => 147,
'C' => 148
),
'file' => ABSPATH . '/wp-content/uploads/Accent Band Sheet.csv'
) );
new GW_AJAX_Population( array(
'form_id' => 636,
'trigger_field_id' => 100,
// an array of CSV column names and their corresponding field IDs (from the form)
'data_map' => array(
'A' => 100,
'B' => 149,
'C' => 150
),
'file' => ABSPATH . '/wp-content/uploads/Accent Band Sheet.csv'
) );
new GW_AJAX_Population( array(
'form_id' => 636,
'trigger_field_id' => 101,
'data_map' => array(
'A' => 101,
'B' => 152,
'C' => 151
),
'file' => ABSPATH . '/wp-content/uploads/Accent Band Sheet.csv'
) );
@GitHubGreg
Copy link

Thanks for all these great tools you share with the community. Very generous and appreciated.

I have tried implementing this one without success. My path to CSV is correct, and I believe I have configured the array and csv correctly. Here is my approach, using your lumber example:

Form ID is 20 (and the form has Ajax enabled and no re-captcha)

Field ID 1 has label "Lumber type" with two dropdown options: "Wide lumber" and "Square lumber"
Field ID 2 has label "Width"
Field ID 3 has label "Length"

When someone chooses a lumber type, it should populate the other two fields based on a CSV file which looks like this (I tried with and without the first line):

A,B,C
Wide lumber,10 inches,5 inches
Square lumber,10 inches,10 inches

I mapped this data as follows when instantiating your class:

new GW_AJAX_Population( array(
    'form_id' => 20,
    'trigger_field_id' => 1,
    'data_map' => array(
        'A' => 1,
        'B' => 2,
        'C' => 3
    ),
    'file' => ABSPATH . '/wp-content/uploads/Accent Band Sheet.csv'
) );

When I load this form and make a selection from my dropdown menu, the other two fields don't change accordingly.

Any help you might provide would be greatly appreciated. Perhaps I am simply misunderstanding how it needs to be implemented.

Thanks very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment