Completed
Pull Request — master (#50)
by
unknown
03:08
created

get_single_field_restructured_data()   D

Complexity

Conditions 26
Paths 19

Size

Total Lines 89
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 26
eloc 56
c 1
b 0
f 1
nc 19
nop 3
dl 0
loc 89
rs 4.5904

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\Datastore\Datastore_Interface;
6
use Carbon_Fields\Helper\Helper;
7
use Carbon_Fields\Field\Field;
8
use Carbon_Fields\Field\Group_Field;
9
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
10
11
/**
12
 * Complex field class.
13
 * Allows nested repeaters with multiple field groups to be created.
14
 */
15
class Complex_Field extends Field {
16
	const LAYOUT_TABLE = 'table';
17
	const LAYOUT_LIST = 'list';
18
19
	protected $fields = array();
20
	protected $values = array();
21
	protected $groups = array();
22
23
	protected $layout = self::LAYOUT_TABLE;
24
	protected $values_min = -1;
25
	protected $values_max = -1;
26
27
	/**
28
	 * Defines how complex field data is saved:
29
	 *  - multiple_fields - default. All sub-fields are stored as seperated postmeta fields.
30
	 *  - single_field - All sub-fields are stored serialized in a single postmeta field.
31
	 */
32
	protected $save_mode = 'multiple_fields';
33
34
	public $labels = array(
35
		'singular_name' => 'Entry',
36
		'plural_name' => 'Entries',
37
	);
38
39
	/**
40
	 * Initialization tasks
41
	 */
42
	public function init() {
43
		$this->labels = array(
44
			'singular_name' => __( 'Entry', 'carbon-fields' ),
45
			'plural_name' => __( 'Entries', 'carbon-fields' ),
46
		);
47
48
		// Include the complex group Underscore template
49
		$this->add_template( 'Complex-Group', array( $this, 'template_group' ) );
50
51
		parent::init();
52
	}
53
54
	/**
55
	 * Add a set/group of fields.
56
	 *
57
	 * @return $this
58
	 */
59
	public function add_fields() {
60
		$argv = func_get_args();
61
		$argc = count( $argv );
62
63
		if ( $argc == 1 ) {
64
			$fields = $argv[0];
65
			$name = '';
66
			$label = null;
67
		} else if ( $argc == 2 ) {
68 View Code Duplication
			if ( is_array( $argv[0] ) ) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69
				list( $fields, $name ) = $argv;
70
			} else {
71
				list( $name, $fields ) = $argv;
72
			}
73
			$label = null;
74
		} else if ( $argc == 3 ) {
75 View Code Duplication
			if ( is_array( $argv[0] ) ) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
76
				list( $fields, $name, $label ) = $argv;
77
			} else {
78
				list( $name, $label, $fields ) = $argv;
79
			}
80
		}
81
82
		if ( array_key_exists( '_' . $name, $this->groups ) ) {
83
			Incorrect_Syntax_Exception::raise( 'Group with name "' . $name . '" in Complex Field "' . $this->get_label() . '" already exists.' );
0 ignored issues
show
Bug introduced by
The variable $name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
84
		}
85
86
		$group = new Group_Field($name, $label, $fields);
0 ignored issues
show
Bug introduced by
The variable $label does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $fields does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
87
88
		$this->groups[ $group->get_name() ] = $group;
89
90
		return $this;
91
	}
92
93
	/**
94
	 * Set the group label Underscore template.
95
	 *
96
	 * @param  string|callable $template
97
	 * @return $this
98
	 */
99
	public function set_header_template( $template ) {
100
		if ( count($this->groups) === 0 ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
101
			Incorrect_Syntax_Exception::raise( "Can't set group label template. There are no present groups for Complex Field " . $this->get_label() . "." );
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal . does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
102
		}
103
104
		$template = is_callable( $template ) ? call_user_func( $template ) : $template;
105
106
		// Assign the template to the group that was added last
107
		$group = end( array_values( $this->groups ) );
108
		$group->set_label_template( $template );
109
110
		// Include the group label Underscore template
111
		$this->add_template( $group->get_group_id(), array( $group, 'template_label' ) );
112
113
		$this->groups[ $group->get_name() ] = $group;
114
115
		return $this;
116
	}
117
118
	/**
119
	 * Retrieve all groups of fields.
120
	 *
121
	 * @return array $fields
122
	 */
123
	public function get_fields() {
124
		$fields = array();
125
126
		foreach ( $this->groups as $group ) {
127
			$group_fields = $group->get_fields();
128
129
			$fields = array_merge( $fields, $group_fields );
130
		}
131
132
		return $fields;
133
	}
134
135
	/**
136
	 * Set the field labels.
137
	 * Currently supported values:
138
	 *  - singular_name - the singular entry label
139
	 *  - plural_name - the plural entries label
140
	 *
141
	 * @param  array $labels Labels
142
	 */
143
	public function setup_labels( $labels ) {
144
		$this->labels = array_merge( $this->labels, $labels );
145
		return $this;
146
	}
147
148
	/**
149
	 * Set the datastore of this field.
150
	 *
151
	 * @param Datastore_Interface $store
152
	 */
153
	public function set_datastore( Datastore_Interface $store ) {
154
		$this->store = $store;
155
156
		foreach ( $this->groups as $group ) {
157
			$group->set_datastore( $this->store );
158
		}
159
	}
160
161
	/**
162
	 * Restructures data in single field mode so it emulates the data retrieved and saved via multiple fields.
163
	 *
164
	 * @param     array	$data	input data to be restructured
165
	 * @param     string	$mode	Defines type of transformation: "db_to_process" (transforms database data to a linear key-value-array to be used to display the fields in the backend), "db_save" (transforms input data to the database save-format)
166
	 * @param     array	$args	Arguments mainly used for recursion metadata. Initially only "complex_field_name" is required for mode "db_to_process"
167
	 * @return    array restructured data
168
	 */
169
	private function get_single_field_restructured_data( array $data = null, $mode, array $args = array() ) {
170
		if ( empty($data) ) return $data;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
171
172
		switch ( $mode ) {
173
			case 'db_to_process':
174
				$complex_field_name = isset( $args[ 'complex_field_name' ] ) ? $args[ 'complex_field_name' ] : null;
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
175
				if ( ! $complex_field_name ) Incorrect_Syntax_Exception::raise( "complex_field_name missing" );
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
Coding Style Comprehensibility introduced by
The string literal complex_field_name missing does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
176
				$level = isset( $args[ 'level' ] ) ? $args[ 'level' ] : 0;
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
177
				$prefix = isset( $args[ 'prefix' ] ) ? $args[ 'prefix' ] : $complex_field_name;
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
178
179
				$output = array();
180
				$i = -1;
181
				foreach ( $data as $index => $item ) {
182
					$i++;
183
184
					if ( ! isset( $item[ '_type' ] ) || ! is_array( $item ) || empty( $item ) ) {
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
185
						continue;
186
					}
187
188
					$type = $item[ '_type' ];
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
189
190
					foreach ( $item as $key => $val ) {
191
						if ( $key === '_type' ) continue;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
192
193
						$field_key = $prefix . $type . '-_' . $key . '_' . $i;
194
						$field_value = null;
195
196
						if ( is_array( $val ) ) {
197
							if ( isset( $val[ 0 ][ '_type' ] ) ) {
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
198
								$outputInner = $this->get_single_field_restructured_data( $val, $mode, array_merge( $args, array(
199
									'level' => $level + 1,
200
									'prefix' => $field_key,
201
								) ) );
202
								$output = array_merge( $output, $outputInner );
203
							}
204
							else {
205
								$field_value = serialize( $val );
206
							}
207
						}
208
						else if ( $val !== null ) {
209
							$field_value = (string) $val;
210
						}
211
212
						if ( $field_value !== null ) {
213
							$output[] = array(
214
								'field_key' => $field_key,
215
								'field_value' => $field_value,
216
							);
217
						}
218
					}
219
				}
220
				$data = $output;
221
222
				break;
223
			case 'db_save':
224
				$indices = array_keys( $data );
225
				foreach ( $indices as $index ) {
226
					if ( ! isset( $data[ $index ][ 'group' ] ) || ! is_array( $data[ $index ] ) || empty( $data[ $index ] ) ) {
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
227
						continue;
228
					}
229
230
					$keys = array_keys( $data[ $index ] );
231
					foreach ( $keys as $key ) {
232
						// rename key "group" to "_type"
233
						if ( $key === 'group' ) {
234
							$new_key = '_type';
235
							$data[ $index ][ $new_key ] = $data[ $index ][ $key ];
236
							unset( $data[ $index ][ $key ] );
237
							$key = $new_key;
238
						}
239
						// remove underline-prefix from keys
240
						else if ( preg_match( '/^_/', $key ) ) {
241
							$new_key = substr( $key, 1 );
242
							$data[ $index ][ $new_key ] = $data[ $index ][ $key ];
243
							unset( $data[ $index ][ $key ] );
244
							$key = $new_key;
245
						}
246
247
						if ( is_array( $data[ $index ][ $key ] ) ) {
248
							$data[ $index ][ $key ] = $this->get_single_field_restructured_data( $data[ $index ][ $key ], $mode );
249
						}
250
					}
251
				}
252
253
				break;
254
		}
255
256
		return $data;
257
	}
258
259
	/**
260
	 * Load the field value from an input array based on it's name
261
	 *
262
	 * @param array $input (optional) Array of field names and values. Defaults to $_POST
263
	 **/
264
	public function set_value_from_input( $input = null ) {
265
		$this->values = array();
266
267
		if ( is_null( $input ) ) {
268
			$input = $_POST;
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
269
		}
270
271
		if ( ! isset( $input[ $this->get_name() ] ) ) {
272
			return;
273
		}
274
275
		$input_groups = $input[ $this->get_name() ];
276
		$index = 0;
277
278
		// transform data
279
		if ( $this->save_mode === 'single_field' ) {
280
			$input_groups = stripslashes_deep( $input_groups );
281
			$input_groups = $this->get_single_field_restructured_data( $input_groups, 'db_save' );
282
			$this->set_value( $input_groups );
283
		}
284
285
		foreach ( $input_groups as $values ) {
286
			$value_group = array();
287
			if ( ! isset( $values['group'] ) || ! isset( $this->groups[ $values['group'] ] ) ) {
288
				continue;
289
			}
290
291
			$group = $this->groups[ $values['group'] ];
292
			unset( $values['group'] );
293
294
			$group_fields = $group->get_fields();
295
296
			// trim input values to those used by the field
297
			$group_field_names = array_flip( $group->get_field_names() );
298
			$values = array_intersect_key( $values, $group_field_names );
299
300
			foreach ( $group_fields as $field ) {
301
				// set value from the group
302
				$tmp_field = clone $field;
303
				if ( is_a( $tmp_field, __NAMESPACE__ . '\\Complex_Field' ) ) {
304
					if ( ! isset( $values[ $tmp_field->get_name() ] ) ) {
305
						continue; // bail if the complex field is empty
306
					}
307
308
					$new_name = $this->get_name() . $group->get_name() . '-' . $field->get_name() . '_' . $index;
309
					$new_values = array( $new_name => $values[ $tmp_field->get_name() ] );
310
311
					$tmp_field->set_name( $new_name );
312
					$tmp_field->set_value_from_input( $new_values );
313
				} else {
314
					$tmp_field->set_value_from_input( $values );
315
				}
316
317
				// update name to group name
318
				$tmp_field->set_name( $this->get_name() . $group->get_name() . '-' . $field->get_name() . '_' . $index );
319
				$value_group[] = $tmp_field;
320
			}
321
322
			$this->values[] = $value_group;
323
			$index++;
324
		}
325
	}
326
327
	/**
328
	 * Load all groups of fields and their data.
329
	 */
330
	public function load() {
331
		// load existing groups
332
		$this->load_values();
333
	}
334
335
	/**
336
	 * Save all contained groups of fields.
337
	 */
338
	public function save() {
339
		if ( $this->save_mode === 'single_field' ) {
340
			if ( $this->value !== null) {
0 ignored issues
show
introduced by
No space before closing parenthesis is prohibited
Loading history...
341
				return $this->store->save( $this );
342
			}
343
			else {
344
				return $this->delete();
345
			}
346
		}
347
348
		$this->delete();
349
350
		foreach ( $this->values as $value ) {
351
			foreach ( $value as $field ) {
352
				$field->save();
353
			}
354
		}
355
	}
356
357
	/**
358
	 * Delete the values of all contained fields.
359
	 */
360
	public function delete() {
361
		if ( $this->save_mode === 'single_field' ) {
362
			return $this->store->delete( $this );
363
		}
364
365
		return $this->store->delete_values( $this );
366
	}
367
368
	/**
369
	 * Load and parse the field data
370
	 */
371
	public function load_values() {
372
		if ( $this->save_mode === 'single_field' ) {
373
			$tmp_value = $this->value;
374
			$this->store->load( $this );
375
			$data = maybe_unserialize( $this->value );
376
			$this->value = $tmp_value;
377
378
			// transform data
379
			if ( is_array($data) ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
380
				$data = $this->get_single_field_restructured_data( $data, 'db_to_process', array(
381
					'complex_field_name' => $this->get_name(),
382
				) );
383
			}
384
385
			return $this->process_loaded_values($data);
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
386
		}
387
388
		return $this->load_values_from_db();
389
	}
390
391
	/**
392
	 * Load and parse the field data from the database.
393
	 */
394
	public function load_values_from_db() {
395
		$this->values = array();
396
397
		$group_rows = $this->store->load_values( $this );
398
399
		return $this->process_loaded_values( $group_rows );
400
	}
401
402
	/**
403
	 * Load and parse a raw set of field data.
404
	 *
405
	 * @param  array $values Raw data entries
406
	 * @return array 		 Processed data entries
407
	 */
408
	public function load_values_from_array( $values ) {
409
		$this->values = array();
410
411
		$group_rows = array();
412
413
		$meta_key = $this->get_name();
414
415
		foreach ( $values as $key => $value ) {
416
			if ( strpos( $key, $meta_key ) !== 0 ) {
417
				continue;
418
			}
419
420
			$group_rows[] = array(
421
				'field_key' => preg_replace( '~^(' . preg_quote( $this->name, '~' ) . ')_\d+_~', '$1_', $key ),
422
				'field_value' => $value,
423
			);
424
		}
425
426
		return $this->process_loaded_values( $group_rows );
427
	}
428
429
	/**
430
	 * Parse groups of raw field data into the actual field hierarchy.
431
	 *
432
	 * @param  array $group_rows Group rows
433
	 */
434
	public function process_loaded_values( $group_rows ) {
435
		$input_groups = array();
436
437
		// Set default values
438
		$field_names = array();
439
		foreach ( $this->groups as $group ) {
440
			$group_fields = $group->get_fields();
441
			foreach ( $group_fields as $field ) {
442
				$field_names[] = $field->get_name();
443
				$field->set_value( $field->get_default_value() );
444
			}
445
		}
446
447
		if ( empty( $group_rows ) ) {
448
			return;
449
		}
450
451
		// load and parse values and group type
452
		foreach ( $group_rows as $row ) {
453
			if ( ! preg_match( Helper::get_complex_field_regex( $this->name, array_keys( $this->groups ), $field_names ), $row['field_key'], $field_name ) ) {
454
				continue;
455
			}
456
457
			$row['field_value'] = maybe_unserialize( $row['field_value'] );
458
			$input_groups[ $field_name['index'] ]['type'] = $field_name['group'];
459
460
			if ( ! empty( $field_name['trailing'] ) ) {
461
				$input_groups[ $field_name['index'] ][ $field_name['key'] . '_' . $field_name['sub'] . '-' . $field_name['trailing'] ] = $row['field_value'];
462 View Code Duplication
			} else if ( ! empty( $field_name['sub'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
463
				$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $field_name['sub'] ] = $row['field_value'];
464
			} else {
465
				$input_groups[ $field_name['index'] ][ $field_name['key'] ] = $row['field_value'];
466
			}
467
		}
468
469
		// create groups list with loaded fields
470
		ksort( $input_groups );
471
472
		foreach ( $input_groups as $index => $values ) {
473
			$value_group = array( 'type' => $values['type'] );
474
			$group_fields = $this->groups[ $values['type'] ]->get_fields();
475
			unset( $values['type'] );
476
477
			foreach ( $group_fields as $field ) {
478
				// set value from the group
479
				$tmp_field = clone $field;
480
481
				if ( is_a( $field, __NAMESPACE__ . '\\Complex_Field' ) ) {
482
					$tmp_field->load_values_from_array( $values );
483
				} else {
484
					$tmp_field->set_value_from_input( $values );
485
				}
486
487
				$value_group[] = $tmp_field;
488
			}
489
490
			$this->values[] = $value_group;
491
		}
492
	}
493
494
	/**
495
	 * Retrieve the field values
496
	 * @return array
497
	 */
498
	public function get_values() {
499
		return $this->values;
500
	}
501
502
	/**
503
	 * Generate and set the field prefix.
504
	 * @param string $prefix
505
	 */
506
	public function set_prefix( $prefix ) {
507
		parent::set_prefix( $prefix );
508
509
		foreach ( $this->groups as $group ) {
510
			$group->set_prefix( $prefix );
511
		}
512
	}
513
514
	/**
515
	 * Returns an array that holds the field data, suitable for JSON representation.
516
	 * This data will be available in the Underscore template and the Backbone Model.
517
	 *
518
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
519
	 * @return array
520
	 */
521
	public function to_json( $load ) {
522
		$complex_data = parent::to_json( $load );
523
524
		$groups_data = array();
525
		$values_data = array();
526
527
		foreach ( $this->groups as $group ) {
528
			$groups_data[] = $group->to_json( false );
529
		}
530
531
		foreach ( $this->values as $fields ) {
532
			$group = $this->get_group_by_name( $fields['type'] );
533
			unset( $fields['type'] );
534
535
			$data = array(
536
				'name' => $group->get_name(),
537
				'label' => $group->get_label(),
538
				'group_id' => $group->get_group_id(),
539
				'fields' => array(),
540
			);
541
542
			foreach ( $fields as $index => $field ) {
543
				$data['fields'][] = $field->to_json( false );
544
			}
545
546
			$values_data[] = $data;
547
		}
548
549
		$complex_data = array_merge( $complex_data, array(
550
			'layout' => $this->layout,
551
			'labels' => $this->labels,
552
			'min' => $this->get_min(),
553
			'max' => $this->get_max(),
554
			'multiple_groups' => count( $groups_data ) > 1,
555
			'groups' => $groups_data,
556
			'value' => $values_data,
557
		) );
558
559
		return $complex_data;
560
	}
561
562
	/**
563
	 * The main Underscore template
564
	 */
565
	public function template() {
566
		?>
567
		<div class="carbon-subcontainer carbon-grid {{ multiple_groups ? 'multiple-groups' : '' }}">
568
	
569
			<div class="carbon-empty-row">
570
				{{{ crbl10n.complex_no_rows.replace('%s', labels.plural_name) }}}
571
			</div>
572
573
			<div class="carbon-groups-holder layout-{{ layout }}"></div>
574
575
			<div class="carbon-actions">
576
				<div class="carbon-button">
577
					<a href="#" class="button" data-group="{{{ multiple_groups ? '' : groups[0].name }}}">
578
						{{{ crbl10n.complex_add_button.replace('%s', labels.singular_name) }}}
579
						{{{ multiple_groups ? '&#8681;' : '' }}}
580
					</a>
581
582
					<# if (multiple_groups) { #>
583
						<ul>
584
							<# _.each(groups, function(group) { #>
585
								<li><a href="#" data-group="{{{ group.name }}}">{{{ group.label }}}</a></li>
586
							<# }); #>
587
						</ul>
588
					<# } #>
589
				</div>
590
			</div>
591
		</div>
592
		<?php
593
	}
594
595
	/**
596
	 * The Underscore template for a complex field group
597
	 */
598
	public function template_group() {
599
		?>
600
		<div id="carbon-{{{ complex_name }}}-complex-container" class="carbon-row carbon-group-row" data-group-id="{{ id }}">
601
			<input type="hidden" name="{{{ complex_name + '[' + index + ']' }}}[group]" value="{{ name }}" />
602
603
			<div class="carbon-drag-handle">
604
				<span class="group-number">{{{ order + 1 }}}</span><span class="group-name">{{{ label_template || label }}}</span>
605
			</div>
606
			<div class="carbon-group-actions">
607
				<a class="carbon-btn-collapse" href="#" title="<?php esc_attr_e( 'Collapse/Expand', 'carbon-fields' ); ?>"><?php _e( 'Collapse/Expand', 'carbon-fields' ); ?></a>
608
				<a class="carbon-btn-duplicate" href="#" title="<?php esc_attr_e( 'Clone', 'carbon-fields' ); ?>"><?php _e( 'Clone', 'carbon-fields' ); ?></a>
609
				<a class="carbon-btn-remove" href="#" title="<?php esc_attr_e( 'Remove', 'carbon-fields' ); ?>"><?php _e( 'Remove', 'carbon-fields' ); ?></a>
610
			</div>
611
612
			<div class="fields-container">
613
				<# _.each(fields, function(field) { #>
614
					<div class="carbon-row carbon-subrow subrow-{{{ field.type }}} {{{ field.classes.join(' ') }}}">
615
						<label for="{{{ complex_id + '-' + field.id + '-' + index }}}">
616
							{{ field.label }}
617
618
							<# if (field.required) { #>
619
								 <span class="carbon-required">*</span>
620
							<# } #>
621
						</label>
622
623
						<div class="field-holder {{{ complex_id + '-' + field.id + '-' + index }}}"></div>
624
625
						<# if (field.help_text) { #>
626
							<em class="help-text">
627
								{{{ field.help_text }}}
628
							</em>
629
						<# } #>
630
631
						<em class="carbon-error"></em>
632
					</div>
633
				<# }) #>
634
			</div>
635
		</div>
636
		<?php
637
	}
638
639
	/**
640
	 * Modify the layout of this field.
641
	 * Deprecated in favor of set_width().
642
	 *
643
	 * @deprecated
644
	 *
645
	 * @param string $layout
646
	 */
647
	public function set_layout( $layout ) {
648
		_doing_it_wrong( __METHOD__, __( 'Complex field layouts are deprecated, please use <code>set_width()</code> instead.', 'carbon-fields' ), null );
649
650
		if ( ! in_array( $layout, array( self::LAYOUT_TABLE, self::LAYOUT_LIST ) ) ) {
651
			Incorrect_Syntax_Exception::raise( 'Incorrect layout specifier. Available values are "<code>' . self::LAYOUT_TABLE . '</code>" and "<code>' . self::LAYOUT_LIST . '</code>"' );
652
		}
653
654
		$this->layout = $layout;
655
656
		return $this;
657
	}
658
659
	/**
660
	 * Set the minimum number of entries.
661
	 *
662
	 * @param int $min
663
	 */
664
	public function set_min( $min ) {
665
		$this->values_min = intval( $min );
666
		return $this;
667
	}
668
669
	/**
670
	 * Get the minimum number of entries.
671
	 *
672
	 * @return int $min
673
	 */
674
	public function get_min() {
675
		return $this->values_min;
676
	}
677
678
	/**
679
	 * Set the maximum number of entries.
680
	 *
681
	 * @param int $max
682
	 */
683
	public function set_max( $max ) {
684
		$this->values_max = intval( $max );
685
		return $this;
686
	}
687
688
	/**
689
	 * Get the maximum number of entries.
690
	 *
691
	 * @return int $max
692
	 */
693
	public function get_max() {
694
		return $this->values_max;
695
	}
696
697
	/**
698
	 * Retrieve the groups of this field.
699
	 *
700
	 * @return array
701
	 */
702
	public function get_group_names() {
703
		return array_keys( $this->groups );
704
	}
705
706
	/**
707
	 * Retrieve a group by its name
708
	 * @param  string $group_name        Group name
709
	 * @return Group_Field $group_object Group object
710
	 */
711
	public function get_group_by_name( $group_name ) {
712
		$group_object = null;
713
714
		foreach ( $this->groups as $group ) {
715
			if ( $group->get_name() == $group_name ) {
716
				$group_object = $group;
717
			}
718
		}
719
720
		return $group_object;
721
	}
722
723
	public function set_save_mode( $save_mode ) {
724
		$this->save_mode = $save_mode;
725
726
		return $this;
727
	}
728
}
729