Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Gravity Forms Field with Multiple Inputs

I have been working on a Gravity Forms extension for a client. The concept is to add a new field type with 4 inputs. I have tried about 10 different variations on how people build custom gravity form fields, but I keep running into the same issue.

When creating a custom field, if I use only 1 input under the naming convention of input_{field_id} the form will save and validate properly. But the moment I try to add more than one field using the names input_{field_id}.{i} just like the built in fields the form will no longer save my data.

<?php if ( ! class_exists( 'GFForms' ) ) { die(); }

class GF_Field_Attendees extends GF_Field {

    public $type = 'attendees';

    public function get_form_editor_field_title() { return esc_attr__( 'Attendees', 'gravityforms' ); }

    public function get_form_editor_button() {
        return array(
            'group' => 'advanced_fields',
            'text'  => $this->get_form_editor_field_title(),
            'onclick'   => "StartAddField('".$this->type."');",
        );
    }

    public function get_form_editor_field_settings() {
        return array(
            'conditional_logic_field_setting',
            'prepopulate_field_setting',
            'error_message_setting',
            'label_setting',
            'admin_label_setting',
            'rules_setting',
            'duplicate_setting',
            'description_setting',
            'css_class_setting',
        );
    }

    public function is_conditional_logic_supported() { return true; }

    public function get_field_input( $form, $value = '', $entry = null ) {
        $form_id    = $form['id'];
        $field_id   = intval( $this->id );

        $first  = esc_attr( GFForms::get( 'input_' . $this->id . '_1', $value ) );
        $last   = esc_attr( GFForms::get( 'input_' . $this->id . '_2', $value ) );
        $email  = esc_attr( GFForms::get( 'input_' . $this->id . '_3', $value ) );
        $phone  = esc_attr( GFForms::get( 'input_' . $this->id . '_4', $value ) );

        $disabled_text = $is_form_editor ? "disabled='disabled'" : '';
        $class_suffix  = $is_entry_detail ? '_admin' : '';

        $first_tabindex = GFCommon::get_tabindex();
        $last_tabindex  = GFCommon::get_tabindex();
        $email_tabindex = GFCommon::get_tabindex();
        $phone_tabindex = GFCommon::get_tabindex();

        $required_attribute     = $this->isRequired ? 'aria-required="true"' : '';
        $invalid_attribute      = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

        $first_markup = '<span id="input_'.$field_id.'_'.$form_id.'.1_container" class="attendees_first">';
            $first_markup .= '<input type="text" name="input_'.$field_id.'.1" id="input_'.$field_id.'_'.$form_id.'_1" value="'.$first.'" aria-label="First Name" '.$first_tabindex.' '.$disabled_text.' '.$required_attribute.' '.$invalid_attribute.'>';
            $first_markup .= '<label for="input_'.$field_id.'_'.$form_id.'_1">First Name</label>';
        $first_markup .= '</span>';

        $last_markup = '<span id="input_'.$field_id.'_'.$form_id.'.2_container" class="attendees_last">';
            $last_markup .= '<input type="text" name="input_'.$field_id.'.2" id="input_'.$field_id.'_'.$form_id.'_2" value="'.$last.'" aria-label="Last Name" '.$last_tabindex.' '.$disabled_text.' '.$required_attribute.' '.$invalid_attribute.'>';
            $last_markup .= '<label for="input_'.$field_id.'_'.$form_id.'_2">Last Name</label>';
        $last_markup .= '</span>';

        $email_markup = '<span id="input_'.$field_id.'_'.$form_id.'.3_container" class="attendees_email">';
            $email_markup .= '<input type="text" name="input_'.$field_id.'.3" id="input_'.$field_id.'_'.$form_id.'_3" value="'.$email.'" aria-label="Email" '.$email_tabindex.' '.$disabled_text.' '.$required_attribute.' '.$invalid_attribute.'>';
            $email_markup .= '<label for="input_'.$field_id.'_'.$form_id.'_3">Email</label>';
        $email_markup .= '</span>';

        $phone_markup = '<span id="input_'.$field_id.'_'.$form_id.'.4_container" class="attendees_phone">';
            $phone_markup .= '<input type="text" name="input_'.$field_id.'.4" id="input_'.$field_id.'_'.$form_id.'_4" value="'.$phone.'" aria-label="Phone #" '.$phone_tabindex.' '.$disabled_text.' '.$required_attribute.' '.$invalid_attribute.'>';
            $phone_markup .= '<label for="input_'.$field_id.'_'.$form_id.'_4">Phone #</label>';
        $phone_markup .= '</span>';

        $css_class = $this->get_css_class();

        return "<div class='ginput_complex{$class_suffix} ginput_container {$css_class} gfield_trigger_change' id='{$field_id}'>
                    {$first_markup}
                    {$last_markup}
                    {$email_markup}
                    {$phone_markup}
                    <div class='gf_clear gf_clear_complex'></div>
                </div>";
    }

    public function get_css_class() {
        $first_input = GFFormsModel::get_input( $this, $this->id . '_2' );
        $last_input  = GFFormsModel::get_input( $this, $this->id . '_3' );
        $email_input = GFFormsModel::get_input( $this, $this->id . '_4' );
        $phone_input   = GFFormsModel::get_input( $this, $this->id . '_5' );

        $css_class = '';
        $visible_input_count = 0;

        if ( $first_input && ! rgar( $first_input, 'isHidden' ) ) {
            $visible_input_count++;
            $css_class .= 'has_first_name ';
        } else {
            $css_class .= 'no_first_name ';
        }

        if ( $last_input && ! rgar( $last_input, 'isHidden' ) ) {
            $visible_input_count++;
            $css_class .= 'has_last_name ';
        } else {
            $css_class .= 'no_last_name ';
        }

        if ( $email_input && ! rgar( $email_input, 'isHidden' ) ) {
            $visible_input_count++;
            $css_class .= 'has_email ';
        } else {
            $css_class .= 'no_email ';
        }

        if ( $phone_input && ! rgar( $phone_input, 'isHidden' ) ) {
            $visible_input_count++;
            $css_class .= 'has_phone ';
        } else {
            $css_class .= 'no_phone ';
        }

        $css_class .= "gf_attendees_has_{$visible_input_count} ginput_container_attendees ";

        return trim( $css_class );
    }

    public function get_value_submission( $field_values, $get_from_post ) {
        if(!$get_from_post) {
            return $field_values;
        }

        return $_POST;
    }
}

GF_Fields::register( new GF_Field_Attendees() );

I have spend about 20 hours trying different fixes and searching the internet to get this working, with no luck to show for it. At one point I was able to get the form fields to save using a different method (see below), but I could not make the field required or use conditional login on it, which is a must.

$group_title = "Attendees";
$group_name = "attendees";
$group_fields = array(
    'attendee_first' => 'First Name',
    'attendee_last' => 'Last Name',
    'attendee_email' => 'Email',
    'attendee_phone' => 'Phone'
);
$group_values = array();

add_filter('gform_add_field_buttons', add_field);
function add_field($field_group)
{
  global $group_title, $group_name;

  foreach ($field_group as &$group) {

    if ($group['name'] == 'advanced_fields') {
      $group['fields'][] = array (
        'class'     => 'button',
        'value'     => __($group_title, 'gravityforms'),
        'onclick'   => "StartAddField('".$group_name."');",
        'data-type' => $group_name
      );
      break;
    }
  }

  return $field_group;
}

add_filter('gform_field_type_title', add_field_title, 10, 2);
function add_field_title($title, $field_type)
{
  global $group_title, $group_name;

  if ($field_type == $group_name) {
    $title = __($group_title, 'gravityforms');
  }

  return $title;
}

add_filter('gform_field_input', 'render_fields', 10, 5);
function render_fields($input, $field, $value, $entry_id, $form_id)
{
  global $group_name, $group_fields;

  if ($field->type == $group_name)
  {
    $i = 1;
    $input = '<div class="ginput_complex ginput_container">';
    foreach ($group_fields as $key => $val) {
        $input .= '<span id="input_'.$field['id'].'_'.$form_id.'_'.$i.'_container" class="name_suffix ">';
            $input .= '<input type="text" name="input_'.$field['id'].'_'.$i.'" id="input_'.$field['id'].'_'.$form_id.'_'.$i.'" value="'.$value[$field['id'].'.'.$i].'" class="'.esc_attr($key).'" aria-label="'.$val.'">';
            $input .= '<label for="input_'.$field['id'].'_'.$form_id.'_'.$i.'">'.$val.'</label>';
        $input .= '</span>';
        $i ++;
        if ($i % 10 == 0) { $i++; }
    }
    $input .= '</div>';
  }

  return $input;
}

add_action('gform_editor_js_set_default_values', set_default_values);
function set_default_values()
{
  global $group_title, $group_name, $group_fields;
  ?>
  case '<?php echo $group_name; ?>' :
    field.label = '<?php _e($group_title, 'gravityforms'); ?>';
    field.inputs = [
        <?php
        $i = 1;
        foreach ($group_fields as $key => $val) { ?>
            new Input(field.id + 0.<?php echo $i; ?>, '<?php echo esc_js(__($val, 'gravityforms')); ?>'),
        <?php
            $i++;
            if ($i % 10 == 0) { $i++; }
        } ?>
    ];
    break;
  <?php
}

add_filter( 'gform_entry_field_value', 'category_names', 10, 4 );
function category_names( $value, $field, $lead, $form )
{
  global $group_name, $group_values;

  if($field->type == $group_name)
  {
    $array = array();
    $output = "";
    foreach($field->inputs as $input)
    {
      $array[$input['label']] = $value[$input['id']];

      $output .= "<strong>".$input['label'].":</strong> ";
      $output .= $value[$input['id']]."<br>";
    }
    $group_values[] = $array;

    return $output;
  }

  return $value;
}

If anyone can help me with either issue, it would be greatly appreciated.

Class update:

  • Cleaned Up get_field_input

  • Added get_value_submission

like image 427
Trevor Geene Avatar asked Mar 24 '17 21:03

Trevor Geene


People also ask

How do you create a custom field in gravity forms?

When using the Settings API you can define a custom field type by creating a function called settings_{your_custom_field_type}. The text after the first underscore will be what you decided to name your type. The function will run for every field of that type.

Is gravity forms no longer free?

Gravity Forms PricingBasic License: $59 per year * Elite License: $259 per year * Pro License: $159 per year * * Gravity Forms updates and support are provided only for the duration of an active Gravity Forms subscription.


1 Answers

After working with the Gravity Forms support team for a few days, we were able to come up with this solution. Everything seems to be working now. Hope this helps someone in the future.

class GF_Field_Attendees extends GF_Field {

    public $type = 'attendees';

    public function get_form_editor_field_title() {
        return esc_attr__( 'Attendees', 'gravityforms' );
    }

    public function get_form_editor_button() {
        return array(
            'group' => 'advanced_fields',
            'text'  => $this->get_form_editor_field_title(),
        );
    }

    public function get_form_editor_field_settings() {
        return array(
            'conditional_logic_field_setting',
            'prepopulate_field_setting',
            'error_message_setting',
            'label_setting',
            'admin_label_setting',
            'rules_setting',
            'duplicate_setting',
            'description_setting',
            'css_class_setting',
        );
    }

    public function is_conditional_logic_supported() {
        return true;
    }

    public function get_field_input( $form, $value = '', $entry = null ) {
        $is_entry_detail = $this->is_entry_detail();
        $is_form_editor  = $this->is_form_editor();

        $form_id  = $form['id'];
        $field_id = intval( $this->id );

        $first = $last = $email = $phone = '';

        if ( is_array( $value ) ) {
            $first = esc_attr( rgget( $this->id . '.1', $value ) );
            $last  = esc_attr( rgget( $this->id . '.2', $value ) );
            $email = esc_attr( rgget( $this->id . '.3', $value ) );
            $phone = esc_attr( rgget( $this->id . '.4', $value ) );
        }

        $disabled_text = $is_form_editor ? "disabled='disabled'" : '';
        $class_suffix  = $is_entry_detail ? '_admin' : '';

        $first_tabindex = GFCommon::get_tabindex();
        $last_tabindex  = GFCommon::get_tabindex();
        $email_tabindex = GFCommon::get_tabindex();
        $phone_tabindex = GFCommon::get_tabindex();

        $required_attribute = $this->isRequired ? 'aria-required="true"' : '';
        $invalid_attribute  = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

        $first_markup = '<span id="input_' . $field_id . '_' . $form_id . '.1_container" class="attendees_first">';
        $first_markup .= '<input type="text" name="input_' . $field_id . '.1" id="input_' . $field_id . '_' . $form_id . '_1" value="' . $first . '" aria-label="First Name" ' . $first_tabindex . ' ' . $disabled_text . ' ' . $required_attribute . ' ' . $invalid_attribute . '>';
        $first_markup .= '<label for="input_' . $field_id . '_' . $form_id . '_1">First Name</label>';
        $first_markup .= '</span>';

        $last_markup = '<span id="input_' . $field_id . '_' . $form_id . '.2_container" class="attendees_last">';
        $last_markup .= '<input type="text" name="input_' . $field_id . '.2" id="input_' . $field_id . '_' . $form_id . '_2" value="' . $last . '" aria-label="Last Name" ' . $last_tabindex . ' ' . $disabled_text . ' ' . $required_attribute . ' ' . $invalid_attribute . '>';
        $last_markup .= '<label for="input_' . $field_id . '_' . $form_id . '_2">Last Name</label>';
        $last_markup .= '</span>';

        $email_markup = '<span id="input_' . $field_id . '_' . $form_id . '.3_container" class="attendees_email">';
        $email_markup .= '<input type="text" name="input_' . $field_id . '.3" id="input_' . $field_id . '_' . $form_id . '_3" value="' . $email . '" aria-label="Email" ' . $email_tabindex . ' ' . $disabled_text . ' ' . $required_attribute . ' ' . $invalid_attribute . '>';
        $email_markup .= '<label for="input_' . $field_id . '_' . $form_id . '_3">Email</label>';
        $email_markup .= '</span>';

        $phone_markup = '<span id="input_' . $field_id . '_' . $form_id . '.4_container" class="attendees_phone">';
        $phone_markup .= '<input type="text" name="input_' . $field_id . '.4" id="input_' . $field_id . '_' . $form_id . '_4" value="' . $phone . '" aria-label="Phone #" ' . $phone_tabindex . ' ' . $disabled_text . ' ' . $required_attribute . ' ' . $invalid_attribute . '>';
        $phone_markup .= '<label for="input_' . $field_id . '_' . $form_id . '_4">Phone #</label>';
        $phone_markup .= '</span>';

        $css_class = $this->get_css_class();

        return "<div class='ginput_complex{$class_suffix} ginput_container {$css_class} gfield_trigger_change' id='{$field_id}'>
                    {$first_markup}
                    {$last_markup}
                    {$email_markup}
                    {$phone_markup}
                    <div class='gf_clear gf_clear_complex'></div>
                </div>";
    }

    public function get_css_class() {
        $first_input = GFFormsModel::get_input( $this, $this->id . '.1' );
        $last_input  = GFFormsModel::get_input( $this, $this->id . '.2' );
        $email_input = GFFormsModel::get_input( $this, $this->id . '.3' );
        $phone_input = GFFormsModel::get_input( $this, $this->id . '.4' );

        $css_class           = '';
        $visible_input_count = 0;

        if ( $first_input && ! rgar( $first_input, 'isHidden' ) ) {
            $visible_input_count ++;
            $css_class .= 'has_first_name ';
        } else {
            $css_class .= 'no_first_name ';
        }

        if ( $last_input && ! rgar( $last_input, 'isHidden' ) ) {
            $visible_input_count ++;
            $css_class .= 'has_last_name ';
        } else {
            $css_class .= 'no_last_name ';
        }

        if ( $email_input && ! rgar( $email_input, 'isHidden' ) ) {
            $visible_input_count ++;
            $css_class .= 'has_email ';
        } else {
            $css_class .= 'no_email ';
        }

        if ( $phone_input && ! rgar( $phone_input, 'isHidden' ) ) {
            $visible_input_count ++;
            $css_class .= 'has_phone ';
        } else {
            $css_class .= 'no_phone ';
        }

        $css_class .= "gf_attendees_has_{$visible_input_count} ginput_container_attendees ";

        return trim( $css_class );
    }

    public function get_form_editor_inline_script_on_page_render() {

        // set the default field label for the field
        $script = sprintf( "function SetDefaultValues_%s(field) {
        field.label = '%s';
        field.inputs = [new Input(field.id + '.1', '%s'), new Input(field.id + '.2', '%s'), new Input(field.id + '.3', '%s'), new Input(field.id + '.4', '%s')];
        }", $this->type, $this->get_form_editor_field_title(), 'First Name', 'Last Name', 'Email', 'Phone' ) . PHP_EOL;

        return $script;
    }

    public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
        if ( is_array( $value ) ) {
            $first = trim( rgget( $this->id . '.1', $value ) );
            $last  = trim( rgget( $this->id . '.2', $value ) );
            $email = trim( rgget( $this->id . '.3', $value ) );
            $phone = trim( rgget( $this->id . '.4', $value ) );

            $return = $first;
            $return .= ! empty( $return ) && ! empty( $last ) ? " $last" : $last;
            $return .= ! empty( $return ) && ! empty( $email ) ? " $email" : $email;
            $return .= ! empty( $return ) && ! empty( $phone ) ? " $phone" : $phone;

        } else {
            $return = '';
        }

        if ( $format === 'html' ) {
            $return = esc_html( $return );
        }

        return $return;
    }

}

GF_Fields::register( new GF_Field_Attendees() );
like image 94
Trevor Geene Avatar answered Sep 21 '22 00:09

Trevor Geene