spivurno
12/18/2013 - 12:57 PM

Gravity Perks // GP Limit Choices // Share Limits Across Multiple Fields

Gravity Perks // GP Limit Choices // Share Limits Across Multiple Fields

<?php
/**
 * Gravity Perks // GP Limit Choices // Share Limits Across Multiple Fields (and Forms)
 *
 * NOTE: This snippet only works with Gravity Forms 2.3 or greater.
 *
 * Adds support for specifying groups of fields which share the same limit. For example, if you had two fields with a
 * limit of 10, selections from both fields would contribute to that limit.
 *
 * @version   2.1
 * @author    David Smith <david@gravitywiz.com>
 * @license   GPL-2.0+
 * @link      http://gravitywiz.com/
 */
class GWLimitChoicesSharedLimits {

	public $_args = array();

	public function __construct( $args = array() ) {

		$this->_args = wp_parse_args( $args, array(
			'form_id'      => false,
			'field_groups' => array(),
			'form_groups'  => array()
		) );

		// 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' ) );

	}

	public function init() {

		if( ! is_callable( array( 'GFFormsModel', 'get_database_version' ) ) ) {
			return;
		}

		add_filter( 'gwlc_choice_counts_query', array( $this, 'modify_choice_counts_query' ), 10, 2 );
		add_filter( 'gplc_requested_count', array( $this, 'modify_requested_count' ), 10, 2 );

	}
    
    public function modify_choice_counts_query( $query, $field ) {

	    if( ! $this->is_applicable_form( $field->formId ) ) {
		    return $query;
	    }

	    if( ! empty( $this->_args['form_groups'] ) ) {
		    $query = $this->modify_query_for_form_groups( $query, $field );
	    }

	    if( ! empty( $this->_args['field_groups'] ) ) {
		    $query = $this->modify_query_for_field_groups( $query, $field );
	    }

        return $query;
    }

	public function modify_query_for_field_groups( $query, $field ) {
		global $wpdb;

		$from_search = $wpdb->prepare( "(em.meta_key = %s OR em.meta_key LIKE %s)", $field['id'], $wpdb->esc_like( $field['id'] ) . '.%' );
		$from_replace = array();

		foreach( $this->_args['field_groups'] as $field_group ) {

			if( ! in_array( $field['id'], $field_group ) ) {
				continue;
			}

			foreach( $field_group as $field_id ) {
				$from_replace[] = $wpdb->prepare( "em.meta_key = %s OR em.meta_key LIKE %s", $field_id, $wpdb->esc_like( $field_id ) . '.%' );
			}

		}

		if( ! empty( $from_replace ) ) {
			$from_replace = sprintf( '( %s )', implode( ' OR ', $from_replace ) );
			$query['from'] = str_replace( $from_search, $from_replace, $query['from'] );
		}

		return $query;
	}

	public function modify_query_for_form_groups( $query, $field ) {
		global $wpdb;

		foreach( $this->_args['form_groups'] as $form_group ) {

			if( ! $this->is_form_group_field( $field, $form_group ) ) {
				continue;
			}

			$query['where'] = str_replace( sprintf( 'AND em.form_id = %d', $field->formId ), '', $query['where'] );

			$join_conditions = array();
			foreach( $form_group as $form_id => $field_id ) {
				$join_conditions[] = sprintf( '( em.form_id = %1$d AND ( em.meta_key = \'%2$d\' OR em.meta_key LIKE \'%2$d.%%\' ) )', $form_id, $field_id );
			}

			$query['from'] = sprintf( "FROM {$wpdb->prefix}gf_entry e INNER JOIN {$wpdb->prefix}gf_entry_meta em ON em.entry_id = e.id AND ( %s )", implode( "\nOR\n", $join_conditions ) );

		}

		return $query;
	}

	public function modify_requested_count( $requested_count, $field ) {

		if( ! $this->is_applicable_form( $field['formId'] ) ) {
			return $requested_count;
		}

		foreach( $this->_args['field_groups'] as $field_group ) {

			if( ! in_array( $field->id, $field_group ) ) {
				continue;
			}

			$selected_choices       = gp_limit_choices()->get_selected_choices( $field );
			$primary_choice         = reset( $selected_choices );
			$shared_requested_count = 0;

			remove_filter( 'gplc_requested_count', array( $this, 'modify_requested_count' ) );

			foreach( $field_group as $field_id ) {

				$form        = GFAPI::get_form( $field->formId );
				$group_field = GFFormsModel::get_field( $form, $field_id );

				$selected_choices = gp_limit_choices()->get_selected_choices( $group_field );
				$selected_choice  = reset( $selected_choices );

				if( $selected_choice['value'] == $primary_choice['value'] ) {
					$shared_requested_count += gp_limit_choices()->get_requested_count( $group_field );
				}

			}

			add_filter( 'gplc_requested_count', array( $this, 'modify_requested_count' ), 10, 2 );

			break;

		}

		return isset( $shared_requested_count ) ? intval( $shared_requested_count ) : $requested_count;
	}

	public function is_applicable_form( $form ) {

		$form_id            = is_array( $form ) ? $form['id'] : $form;
		$is_applicable_form = false;

		if ( ! empty( $this->_args['form_groups'] ) ) {
			foreach( $this->_args['form_groups'] as $form_group ) {
				$is_applicable_form = $this->is_form_group_form( $form, $form_group );
				if( $is_applicable_form ) {
					break;
				}
			}
		} else {
			$is_applicable_form = $form_id == $this->_args['form_id'];
		}

		return $is_applicable_form;
	}

	public function is_form_group_form( $form, $form_group ) {
		$form_id = is_array( $form ) ? $form['id'] : $form;
		return in_array( $form_id, array_keys( $form_group ) );
	}

	public function is_form_group_field( $field, $form_group ) {
		return $this->is_form_group_form( $field->formId, $form_group ) && in_array( $field->id, $form_group );
	}

}

# Configuration

// Share limits on between two fields on the same form
new GWLimitChoicesSharedLimits( array(
    'form_id' => 996,
    'field_groups' => array(
        array( 1, 2 )
    )
) );

// Share limits between fields across multiple forms
new GWLimitChoicesSharedLimits( array(
	'form_groups' => array(
		array(
			996 => 1,
			997 => 1
		),
		array(
			996 => 2,
			997 => 2
		)
	)
) );