Completed
Pull Request — master (#1446)
by Zack
22:43 queued 19:57
created

GravityView_Field_Address::field_options()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 6
dl 0
loc 23
ccs 0
cts 11
cp 0
crap 12
rs 9.552
c 0
b 0
f 0
1
<?php
2
/**
3
 * @file class-gravityview-field-address.php
4
 * @package GravityView
5
 * @subpackage includes\fields
6
 */
7
8
/**
9
 * Add custom options for address fields
10
 */
11
class GravityView_Field_Address extends GravityView_Field {
12
13
	var $name = 'address';
14
15
	var $group = 'advanced';
16
17
	var $is_numeric = false;
18
19
	var $is_searchable = true;
20
21
	var $search_operators = array( 'is', 'isnot', 'contains' );
22
23
	var $_gf_field_class_name = 'GF_Field_Address';
24
25
	public function __construct() {
26
		$this->label = esc_html__( 'Address', 'gravityview' );
27
28
		$this->add_hooks();
29
30
		parent::__construct();
31
	}
32
33
	/**
34
	 * Add filters for this field type
35
	 *
36
	 * @since 1.19.2
37
	 *
38
	 * @return void
39
	 */
40
	private function add_hooks() {
41
		add_filter( 'gravityview/extension/search/input_type', array( $this, 'search_bar_input_type' ), 10, 3 );
42
		add_filter( 'gravityview/search/input_types', array( $this, 'input_types' ) );
43
		add_filter( 'gravityview_widget_search_filters', array( $this, 'search_field_filter' ), 10, 3 );
44
	}
45
46
	/**
47
	 * Dynamically add choices to the address field dropdowns, if any
48
	 *
49
	 * @since 1.19.2
50
	 *
51
	 * @param array $search_fields Array of search filters with `key`, `label`, `value`, `type` keys
52
	 * @param GravityView_Widget_Search $widget Current widget object
53
	 * @param array $widget_args Args passed to this method. {@since 1.8}
54
	 *
55
	 * @return array If the search field GF Field type is `address`, and there are choices to add, adds them and changes the input type. Otherwise, sets the input to text.
56
	 */
57 4
	public function search_field_filter( $search_fields, $widget, $widget_args ) {
0 ignored issues
show
Unused Code introduced by
The parameter $widget is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $widget_args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
58
59 4
		foreach ( $search_fields as & $search_field ) {
60
61 4
			if ( 'address' !== \GV\Utils::get( $search_field, 'type' ) ) {
62 4
				continue;
63
			}
64
65
			$field_id = intval( floor( $search_field['key'] ) );
66
			$input_id = gravityview_get_input_id_from_id( $search_field['key'] );
67
			$form = GravityView_View::getInstance()->getForm();
68
69
			/** @var GF_Field_Address $address_field */
70
			$address_field = GFFormsModel::get_field( $form, $field_id );
71
72
			$choices = array();
73
74
			$method_name = 'get_choices_' . self::get_input_type_from_input_id( $input_id );
75
			if( method_exists( $this, $method_name ) ) {
76
				/**
77
				 * @uses GravityView_Field_Address::get_choices_country()
78
				 * @uses GravityView_Field_Address::get_choices_state()
79
				 */
80
				$choices = $this->{$method_name}( $address_field, $form );
81
			}
82
83
			if( ! empty( $choices ) ) {
84
				$search_field['choices'] = $choices;
85
				$search_field['type'] = \GV\Utils::get( $search_field, 'input');
86
			} else {
87
				$search_field['type'] = 'text';
88
				$search_field['input'] = 'input_text';
89
			}
90
		}
91
92 4
		return $search_fields;
93
	}
94
95
	/**
96
	 * Get array of countries to use for the search choices
97
	 *
98
	 * @since 1.19.2
99
	 *
100
	 * @see GF_Field_Address::get_countries()
101
	 *
102
	 * @param GF_Field_Address $address_field
103
	 *
104
	 * @return array Array of countries with `value` and `text` keys as the name of the country
105
	 */
106
	private function get_choices_country( $address_field ) {
107
108
		$countries = $address_field->get_countries();
109
110
		$country_choices = array();
111
112
		foreach ( $countries as $key => $country ) {
113
			$country_choices[] = array(
114
				'value' => $country,
115
				'text' => $country,
116
			);
117
		}
118
119
		return $country_choices;
120
	}
121
122
	/**
123
	 * Get array of states to use for the search choices
124
	 *
125
	 * @since 1.19.2
126
	 *
127
	 * @uses GF_Field_Address::get_us_states()
128
	 * @uses GF_Field_Address::get_us_state_code()
129
	 * @uses GF_Field_Address::get_canadian_provinces()
130
	 *
131
	 * @param GF_Field_Address $address_field
132
	 * @param array            $form
133
	 *
134
	 * @return array Array of countries with `value` and `text` keys as the name of the country
135
	 */
136
	private function get_choices_state( $address_field, $form ) {
137
138
		$address_type = empty( $address_field->addressType ) ? $address_field->get_default_address_type( $form['id'] ) : $address_field->addressType;
139
140
		$state_choices = array();
141
142
		switch ( $address_type ) {
143
			case 'us':
144
				$states = GFCommon::get_us_states();
145
				break;
146
			case 'canadian':
147
				$states = GFCommon::get_canadian_provinces();
148
				break;
149
			default:
150
				$address_types = $address_field->get_address_types( $form['id'] );
151
				$states = empty( $address_types[ $address_type ]['states'] ) ? array() : $address_types[ $address_type ]['states'];
152
				break;
153
		}
154
155
		foreach ( $states as $key => $state ) {
156
			if ( is_array( $state ) ) {
157
				$state_subchoices = array();
158
159
				foreach ( $state as $key => $substate ) {
160
					$state_subchoices[] = array(
161
						'value' => is_numeric( $key ) ? $substate : $key,
162
						'text' => $substate,
163
					);
164
				}
165
166
				$state_choices[] = array(
167
					'text' => $key,
168
					'value' => $state_subchoices,
169
				);
170
171
			} else {
172
				$state_choices[] = array(
173
					'value' => is_numeric( $key ) ? $state : $key,
174
					'text' => $state,
175
				);
176
			}
177
		}
178
179
		return $state_choices;
180
	}
181
182
	/**
183
	 * Add the input types available for each custom search field type
184
	 *
185
	 * @since 1.19.2
186
	 *
187
	 * @param array $input_types Array of input types as the keys (`select`, `radio`, `multiselect`, `input_text`) with a string or array of supported inputs as values
188
	 *
189
	 * @return array $input_types array, but
190
	 */
191
	public function input_types( $input_types ) {
192
193
		// Use the same inputs as the "text" input type allows
194
		$text_inputs = \GV\Utils::get( $input_types, 'text' );
195
196
		$input_types['street'] = $text_inputs;
197
		$input_types['street2'] = $text_inputs;
198
		$input_types['city'] = $text_inputs;
199
200
		$input_types['state'] = array( 'select', 'radio', 'link' ) + $text_inputs;
201
		$input_types['zip'] = array( 'input_text' );
202
		$input_types['country'] = array( 'select', 'radio', 'link' ) + $text_inputs;
203
204
		return $input_types;
205
	}
206
207
	/**
208
	 * Converts the custom input type (address) into the selected type
209
	 *
210
	 * @since 1.19.2
211
	 *
212
	 * @param string $input_type Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text`
213
	 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
214
	 * @param string|int|float $field_id ID of the field being processed
215
	 *
216
	 * @return string If the field ID matches an address field input, return those options {@see GravityView_Field_Address::input_types() }. Otherwise, original value is used.
217
	 */
218
	public function search_bar_input_type( $input_type, $field_type, $field_id ) {
219
220
		// Is this search field for an input (eg: 4.2) or the whole address field (eg: 4)?
221
		$input_id = gravityview_get_input_id_from_id( $field_id );
222
223
		if( 'address' !== $field_type && $input_id ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $input_id of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
224
			return $input_type;
225
		}
226
227
		// If the input ID matches an expected address input, set to that. Otherwise, keep existing input type.
228
		if( $address_field_name = self::get_input_type_from_input_id( $input_id ) ) {
229
			$input_type = $address_field_name;
230
		}
231
232
		return $input_type;
233
	}
234
235
	/**
236
	 * Get a name for the input based on the input ID
237
	 *
238
	 * @since 1.19.2
239
	 *
240
	 * @param int $input_id ID of the specific input for the address field
241
	 *
242
	 * @return false|string If the input ID matches a known address field input, returns a name for that input ("city", or "country"). Otherwise, returns false.
243
	 */
244
	private static function get_input_type_from_input_id( $input_id ) {
245
246
		$input_type = false;
247
248
		switch ( $input_id ) {
249
			case 1:
250
				$input_type = 'street';
251
				break;
252
			case 2:
253
				$input_type = 'street2';
254
				break;
255
			case 3:
256
				$input_type = 'city';
257
				break;
258
				break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
259
			case 4:
260
				$input_type = 'state';
261
				break;
262
			case 5:
263
				$input_type = 'zip';
264
				break;
265
			case 6:
266
				$input_type = 'country';
267
				break;
268
		}
269
270
		return $input_type;
271
	}
272
273
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id ) {
274
275
		// If this is NOT the full address field, return default options.
276
		if( floor( $field_id ) !== floatval( $field_id ) ) {
277
			return $field_options;
278
		}
279
280
		if( 'edit' === $context ) {
281
			return $field_options;
282
		}
283
284
		$add_options = array();
285
286
		$add_options['show_map_link'] = array(
287
			'type' => 'checkbox',
288
			'label' => __( 'Show Map Link:', 'gravityview' ),
289
			'desc' => __('Display a "Map It" link below the address', 'gravityview'),
290
			'value' => true,
291
			'merge_tags' => false,
292
		);
293
294
		return $add_options + $field_options;
295
	}
296
297
}
298
299
new GravityView_Field_Address;
300