Random Fields

Randomly display a specified number of fields on your form.

Read the Walkthrough

Code

Filename: gw-random-fields.php

<?php

/**
 * Gravity Wiz // Gravity Forms // Random Fields
 *
 * Randomly display a specified number of fields on your form.
 *
 * @version 1.3
 * @author  David Smith <david@gravitywiz.com>
 * @license GPL-2.0+
 * @link    https://gravitywiz.com/random-fields-with-gravity-forms/
 *
 * Plugin Name: Gravity Forms Random Fields
 * Plugin URI:  https://gravitywiz.com/random-fields-with-gravity-forms/
 * Description: Randomly display a specified number of fields on your form.
 * Author:      Gravity Wiz
 * Version:     1.3
 * Author URI:  https://gravitywiz.com
 */
class GFRandomFields {

	public $all_random_field_ids;
	public $display_count;
	public $selected_field_ids = array();
	public $preserve_order;

	public function __construct( $form_id, $display_count = 5, $random_field_ids = false, $preserve_order = false ) {

		$this->_form_id             = (int) $form_id;
		$this->all_random_field_ids = array_map( 'intval', array_filter( (array) $random_field_ids ) );
		$this->display_count        = (int) $display_count;
		$this->preserve_order       = (bool) $preserve_order;

		add_filter( "gform_pre_render_$form_id", array( $this, 'pre_render' ) );
		add_filter( "gform_form_tag_$form_id", array( $this, 'store_selected_field_ids' ), 10, 2 );
		add_filter( "gform_validation_$form_id", array( $this, 'validate' ) );
		add_filter( "gform_pre_submission_filter_$form_id", array( $this, 'pre_render' ) );
		add_filter( "gform_target_page_{$form_id}", array( $this, 'modify_target_page' ), 10, 3 );

		//add_filter( "gform_admin_pre_render_$form_id",      array( $this, 'admin_pre_render' ) );
		add_action( 'gform_entry_created', array( $this, 'save_selected_field_ids_meta' ), 10, 2 );

	}

	public function pre_render( $form ) {
		return $this->filter_form_fields( $form, $this->get_selected_field_ids( false, $form ) );
	}

	public function admin_pre_render( $form ) {

		if ( (int) $form['id'] !== $this->_form_id ) {
			return $form;
		}

		return $form;
	}

	public function store_selected_field_ids( $form_tag, $form ) {
		$hash  = $this->get_selected_field_ids_hash( $form );
		$value = implode( ',', $this->get_selected_field_ids( false, $form ) );
		$input = sprintf( '<input type="hidden" value="%s" name="gfrf_field_ids_%s">', $value, $hash );
		return $form_tag . $input;
	}

	public function validate( $validation_result ) {

		$validation_result['form']     = $this->filter_form_fields( $validation_result['form'], $this->get_selected_field_ids( false, $validation_result['form'] ) );
		$validation_result['is_valid'] = true;

		foreach ( $validation_result['form']['fields'] as $field ) {
			if ( $field['failed_validation'] ) {
				$validation_result['is_valid'] = false;
			}
		}

		return $validation_result;
	}

	public function filter_form_fields( $form, $selected_fields ) {

		$filtered_fields = array();
		$selected_fields = array_map( 'intval', $selected_fields );
		$random_indexes  = array();

		foreach ( $form['fields'] as $field ) {

			if ( in_array( (int) $field['id'], $this->get_random_field_ids( $form['fields'] ), true ) ) {
				if ( in_array( (int) $field['id'], $selected_fields, true ) ) {
					$filtered_fields[] = $field;
					$random_indexes[]  = count( $filtered_fields ) - 1;
				}
			} else {
				$filtered_fields[] = $field;
			}
		}

		if ( ! $this->preserve_order ) {

			$reordered_fields = array();
			$random_index_key = $random_indexes;

			shuffle( $random_indexes );

			foreach ( $filtered_fields as $index => $field ) {
				if ( in_array( $index, $random_index_key, true ) ) {
					$random_index       = array_pop( $random_indexes );
					$reordered_fields[] = $filtered_fields[ $random_index ];
				} else {
					$reordered_fields[] = $filtered_fields[ $index ];
				}
			}

			$filtered_fields = $reordered_fields;

		}

		$form['fields'] = $filtered_fields;

		return $form;
	}

	public function get_random_field_ids( $fields ) {

		if ( ! empty( $this->all_random_field_ids ) ) {
			return $this->all_random_field_ids;
		}

		$this->all_random_field_ids = array_map( 'intval', wp_list_pluck( $fields, 'id' ) );

		return $this->all_random_field_ids;
	}

	public function get_selected_field_ids( $entry_id = false, $form = array() ) {

		// check if class has already init fields
		if ( ! empty( $this->selected_field_ids ) ) {
			return $this->selected_field_ids;
		}

		$hash = $this->get_selected_field_ids_hash( $form );

		// If entry ID is passed, retrieve selected fields IDs from entry meta.
		if ( $entry_id ) {
			$field_ids = gform_get_meta( $entry_id, "_gfrf_field_ids_{$hash}" );

			return is_array( $field_ids ) ? $field_ids : array();
		}

		// Check if fields have been submitted.
		$field_ids = rgpost( "gfrf_field_ids_{$hash}" );
		if ( ! empty( $field_ids ) ) {
			return explode( ',', $field_ids );
		}

		$field_ids = array();
		$keys      = array_rand( $this->get_random_field_ids( $form['fields'] ), $this->display_count );

		if ( ! is_array( $keys ) ) {
			$keys = array( $keys );
		}

		foreach ( $keys as $key ) {
			$field_ids[] = $this->all_random_field_ids[ $key ];
		}

		$this->selected_field_ids = $field_ids;

		return $field_ids;
	}

	public function get_selected_field_ids_hash( $form ) {
		return wp_hash( implode( '_', $this->get_random_field_ids( $form['fields'] ) ) );
	}

	public function save_selected_field_ids_meta( $entry, $form ) {
		if ( (int) $form['id'] === (int) $this->_form_id ) {
			$hash = $this->get_selected_field_ids_hash( $form );
			gform_add_meta( $entry['id'], "_gfrf_field_ids_{$hash}", $this->get_selected_field_ids( false, $form ) );
		}
	}

	/**
	 * When the form is submitted modify the target page to avoid navigating to a page that has no visible fields.
	 *
	 * Known limitation: If the first page of a form has no visible fields, this will not avoid that as this only works
	 * on submission.
	 *
	 * @param $page_number
	 * @param $form
	 * @param $current_page
	 * @param $field_values
	 *
	 * @return int|mixed
	 */
	public function modify_target_page( $page_number, $form, $current_page ) {
		// For a single page form, no need to modify target page.
		if ( GFFormDisplay::get_max_page_number( $form ) === 0 ) {
			return $page_number;
		}

		$page_number = intval( $page_number );
		$form        = $this->pre_render( $form );

		$page_has_visible_fields = false;
		foreach ( $form['fields'] as $field ) {
			if ( (int) $field->pageNumber === (int) $page_number && GFFormDisplay::is_field_validation_supported( $field ) ) {
				$page_has_visible_fields = true;
			}
		}

		if ( ! $page_has_visible_fields ) {
			// Are we moving to the next or previous page?
			$is_next  = $page_number === 0 || $page_number > $current_page;
			$max_page = GFFormDisplay::get_max_page_number( $form );
			if ( $page_number !== 0 && $page_number < $max_page ) {
				$page_number += $is_next ? 1 : -1;
				// When moving to a previous page, always stop at the first page. Otherwise, we'll submit the form.
				if ( ! $is_next && $page_number === 0 ) {
					return 1;
				}
				$page_number = $this->modify_target_page( $page_number, $form, $current_page );
			} elseif ( $page_number >= $max_page ) {
				// Target page number 0 to submit the form.
				$page_number = 0;
			}
		}

		return $page_number;
	}

}

Leave a Reply

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

  • Trouble installing this snippet? See our troubleshooting tips.
  • Need to include code? Create a gist and link to it in your comment.
  • Reporting a bug? Provide a URL where this issue can be recreated.

By commenting, I understand that I may receive emails related to Gravity Wiz and can unsubscribe at any time.