Completed
Push — development ( 778d40...fbb6b9 )
by
unknown
02:13
created

Container::factory()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 46
Code Lines 30

Duplication

Lines 4
Ratio 8.7 %

Code Coverage

Tests 22
CRAP Score 8.4953

Importance

Changes 0
Metric Value
cc 7
eloc 30
nc 10
nop 3
dl 4
loc 46
ccs 22
cts 32
cp 0.6875
crap 8.4953
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
namespace Carbon_Fields\Container;
4
5
use Carbon_Fields\Carbon_Fields;
6
use Carbon_Fields\Helper\Helper;
7
use Carbon_Fields\Field\Field;
8
use Carbon_Fields\Field\Group_Field;
9
use Carbon_Fields\Container\Fulfillable\Fulfillable_Collection;
10
use Carbon_Fields\Datastore\Datastore_Interface;
11
use Carbon_Fields\Datastore\Datastore_Holder_Interface;
12
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
13
14
/**
15
 * Base container class.
16
 * Defines the key container methods and their default implementations.
17
 */
18
abstract class Container implements Datastore_Holder_Interface {
19
	/**
20
	 * Where to put a particular tab -- at the head or the tail. Tail by default
21
	 */
22
	const TABS_TAIL = 1;
23
	const TABS_HEAD = 2;
24
25
	/**
26
	 * Separator signifying field hierarchy relation
27
	 * Used when searching for fields in a specific complex field
28
	 */
29
	const HIERARCHY_FIELD_SEPARATOR = '/';
30
31
	/**
32
	 * Separator signifying complex_field->group relation
33
	 * Used when searching for fields in a specific complex field group
34
	 */
35
	const HIERARCHY_GROUP_SEPARATOR = ':';
36
37
	/**
38
	 * Stores if the container is active on the current page
39
	 *
40
	 * @see activate()
41
	 * @var bool
42
	 */
43
	protected $active = false;
44
45
	/**
46
	 * List of registered unique field names for this container instance
47
	 *
48
	 * @see register_field_name()
49
	 * @var array
50
	 */
51
	protected $registered_field_names = array();
52
53
	/**
54
	 * Tabs available
55
	 */
56
	protected $tabs = array();
57
58
	/**
59
	 * List of default container settings
60
	 *
61
	 * @see init()
62
	 * @var array
63
	 */
64
	public $settings = array();
65
66
	/**
67
	 * Title of the container
68
	 *
69
	 * @var string
70
	 */
71
	public $title = '';
72
73
	/**
74
	 * List of notification messages to be displayed on the front-end
75
	 *
76
	 * @var array
77
	 */
78
	protected $notifications = array();
79
80
	/**
81
	 * List of error messages to be displayed on the front-end
82
	 *
83
	 * @var array
84
	 */
85
	protected $errors = array();
86
87
	/**
88
	 * List of container fields
89
	 *
90
	 * @see add_fields()
91
	 * @var array
92
	 */
93
	protected $fields = array();
94
95
	/**
96
	 * Array of custom CSS classes.
97
	 *
98
	 * @see set_classes()
99
	 * @see get_classes()
100
	 * @var array<string>
101
	 */
102
	protected $classes = array();
103
104
	/**
105
	 * Container datastores. Propagated to all container fields
106
	 *
107
	 * @see set_datastore()
108
	 * @see get_datastore()
109
	 * @var object
110
	 */
111
	protected $datastore;
112
113
	/**
114
	 * Flag whether the datastore is the default one or replaced with a custom one
115
	 *
116
	 * @see set_datastore()
117
	 * @see get_datastore()
118
	 * @var boolean
119
	 */
120
	protected $has_default_datastore = true;
121
122
	/**
123
	 * Fulfillable_Collection to use when checking attachment/saving conditions
124
	 *
125
	 * @var Fulfillable_Collection
126
	 */
127
	protected $condition_collection;
128
129
	/**
130
	 * Translator to use when translating conditions to json
131
	 *
132
	 * @var Carbon_Fields\Container\Fulfillable\Translator\Translator
133
	 */
134
	protected $condition_translator;
135
136
	/**
137
	 * Create a new container of type $type and name $name.
138
	 *
139
	 * @param  string    $raw_type
140
	 * @param  string    $id        Unique id for the container. Optional
141
	 * @param  string    $name      Human-readable name of the container
142
	 * @return Container $container
143
	 */
144 8
	public static function factory( $raw_type, $id, $name = '' ) {
145
		// no name provided - switch input around as the id is optionally generated based on the name
146 8
		if ( $name === '' ) {
147 8
			$name = $id;
148 8
			$id = '';
149 8
		}
150
151 8
		$type = Helper::normalize_type( $raw_type );
152 8
		$repository = Carbon_Fields::resolve( 'container_repository' );
153 8
		$container = null;
0 ignored issues
show
Unused Code introduced by
$container is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
154 8
		$id = $repository->get_unique_container_id( ( $id !== '' ) ? $id : $name );
155
156 8
		if ( ! Helper::is_valid_entity_id( $id ) ) {
157
			Incorrect_Syntax_Exception::raise( 'Container IDs can only contain lowercase alphanumeric characters, dashes and underscores ("' . $id . '" passed).' );
158
			return null;
159
		}
160
161 8
		if ( ! $repository->is_unique_container_id( $id ) ) {
162
			Incorrect_Syntax_Exception::raise( 'The passed container id is already taken ("' . $id . '" passed).' );
163
			return null;
164
		}
165
166 8
		if ( Carbon_Fields::has( $type, 'containers' ) ) {
167
			$container = Carbon_Fields::resolve_with_arguments( $type, array(
168
				'id' => $id,
169
				'name' => $name,
170
				'type' => $type,
171
			), 'containers' );
172
		} else {
173
			// Fallback to class name-based resolution
174 8
			$class = Helper::type_to_class( $type, __NAMESPACE__, '_Container' );
175
176 8 View Code Duplication
			if ( ! class_exists( $class ) ) {
177 3
				Incorrect_Syntax_Exception::raise( 'Unknown container "' . $raw_type . '".' );
178 1
				$class = __NAMESPACE__ . '\\Broken_Container';
179 1
			}
180
181 6
			$fulfillable_collection = Carbon_Fields::resolve( 'container_condition_fulfillable_collection' );
182 6
			$condition_translator = Carbon_Fields::resolve( 'container_condition_translator_json' );
183 6
			$container = new $class( $id, $name, $type, $fulfillable_collection, $condition_translator );
184
		}
185
186 6
		$repository->register_container( $container );
187
188 6
		return $container;
189
	}
190
191
	/**
192
	 * An alias of factory().
193
	 *
194
	 * @see    Container::factory()
195
	 * @return Container
196
	 */
197
	public static function make() {
198
		return call_user_func_array( array( get_class(), 'factory' ), func_get_args() );
199
	}
200
201
	/**
202
	 * Create a new container
203
	 *
204
	 * @param string                 $id                   Unique id of the container
205
	 * @param string                 $title                Title of the container
206
	 * @param string                 $type                 Type of the container
207
	 * @param Fulfillable_Collection $condition_collection
208
	 * @param Carbon_Fields\Container\Fulfillable\Translator\Translator $condition_translator
209
	 */
210 2
	public function __construct( $id, $title, $type, $condition_collection, $condition_translator ) {
211 2
		Carbon_Fields::verify_boot();
212
213 2
		if ( empty( $title ) ) {
214 1
			Incorrect_Syntax_Exception::raise( 'Empty container title is not supported' );
215
		}
216
217 1
		$this->id = $id;
218 1
		$this->title = $title;
219 1
		$this->type = $type;
220 1
		$this->condition_collection = $condition_collection;
221 1
		$this->condition_collection->set_condition_type_list(
222 1
			array_merge( $this->get_condition_types( true ), $this->get_condition_types( false ) ),
223
			true
224 1
		);
225 1
		$this->condition_translator = $condition_translator;
226 1
	}
227
228
	/**
229
	 * Return the container id
230
	 *
231
	 * @return string
232
	 */
233
	public function get_id() {
234
		return $this->id;
235
	}
236
237
	/**
238
	 * Get array of all static condition types
239
	 *
240
	 * @param  boolean       $static
241
	 * @return array<string>
242
	 */
243
	protected function get_condition_types( $static ) {
244
		$group = $static ? 'static' : 'dynamic';
245
		$container_type = Helper::class_to_type( get_class( $this ), '_Container' );
246
247
		$condition_types = array();
248
		$condition_types = apply_filters( 'carbon_fields_' . $container_type . '_container_' . $group . '_condition_types', $condition_types, $container_type, $this );
249
		$condition_types = apply_filters( 'carbon_fields_container_' . $group . '_condition_types', $condition_types, $container_type, $this );
250
251
		return $condition_types;
252
	}
253
254
	/**
255
	 * Return whether the container is active
256
	 */
257
	public function is_active() {
258
		return $this->active;
259
	}
260
261
	/**
262
	 * Activate the container and trigger an action
263
	 */
264
	protected function activate() {
265
		$this->active = true;
266
		$this->boot();
267
		do_action( 'carbon_fields_container_activated', $this );
268
269
		$fields = $this->get_fields();
270
		foreach ( $fields as $field ) {
271
			$field->activate();
272
		}
273
	}
274
275
	/**
276
	 * Perform instance initialization
277
	 */
278
	abstract public function init();
279
280
	/**
281
	 * Boot the container once it's attached.
282
	 */
283
	protected function boot() {
284
285
	}
286
287
	/**
288
	 * Load the value for each field in the container.
289
	 * Could be used internally during container rendering
290
	 */
291
	public function load() {
292
		foreach ( $this->fields as $field ) {
293
			$field->load();
294
		}
295
	}
296
297
	/**
298
	 * Called first as part of the container save procedure.
299
	 * Responsible for checking the request validity and
300
	 * calling the container-specific save() method
301
	 *
302
	 * @see save()
303
	 * @see is_valid_save()
304
	 */
305
	public function _save() {
306
		$param = func_get_args();
307
		if ( call_user_func_array( array( $this, '_is_valid_save' ), $param ) ) {
308
			call_user_func_array( array( $this, 'save' ), $param );
309
		}
310
	}
311
312
	/**
313
	 * Load submitted data and save each field in the container
314
	 *
315
	 * @see is_valid_save()
316
	 */
317
	public function save( $data = null ) {
318
		foreach ( $this->fields as $field ) {
319
			$field->set_value_from_input( stripslashes_deep( $_POST ) );
320
			$field->save();
321
		}
322
	}
323
324
	/**
325
	 * Checks whether the current save request is valid
326
	 *
327
	 * @return bool
328
	 */
329
	final protected function _is_valid_save() {
330
		$params = func_get_args();
331
		$is_valid_save = call_user_func_array( array( $this, 'is_valid_save' ), $params );
332
		return apply_filters( 'carbon_fields_container_is_valid_save', $is_valid_save, $this );
333
	}
334
335
	/**
336
	 * Checks whether the current save request is valid
337
	 *
338
	 * @return bool
339
	 */
340
	abstract protected function is_valid_save();
341
342
	/**
343
	 * Called first as part of the container attachment procedure.
344
	 * Responsible for checking it's OK to attach the container
345
	 * and if it is, calling the container-specific attach() method
346
	 *
347
	 * @see attach()
348
	 * @see is_valid_attach()
349
	 */
350
	public function _attach() {
351
		if ( ! $this->is_valid_attach() ) {
352
			return;
353
		}
354
355
		$param = func_get_args();
356
		call_user_func_array( array( $this, 'attach' ), $param );
357
358
		// Allow containers to activate but not load (useful in cases such as theme options)
359
		if ( $this->should_activate() ) {
360
			$this->activate();
361
		}
362
	}
363
364
	/**
365
	 * Attach the container rendering and helping methods
366
	 * to concrete WordPress Action hooks
367
	 */
368
	public function attach() {}
369
370
	/**
371
	 * Perform checks whether the container should be attached during the current request
372
	 *
373
	 * @return bool True if the container is allowed to be attached
374
	 */
375
	final public function is_valid_attach() {
376
		$is_valid_attach = $this->is_valid_attach_for_request();
377
		return apply_filters( 'carbon_fields_container_is_valid_attach', $is_valid_attach, $this );
378
	}
379
380
	/**
381
	 * Get environment array for page request (in admin)
382
	 *
383
	 * @return array
384
	 */
385
	abstract protected function get_environment_for_request();
386
387
	/**
388
	 * Check container attachment rules against current page request (in admin)
389
	 *
390
	 * @return bool
391
	 */
392
	abstract protected function is_valid_attach_for_request();
393
394
	/**
395
	 * Check if conditions pass for request
396
	 *
397
	 * @return bool
398
	 */
399
	protected function static_conditions_pass() {
400
		$environment = $this->get_environment_for_request();
401
		$static_condition_collection = $this->condition_collection->evaluate(
402
			$this->get_condition_types( false ),
403
			true
404
		);
405
		return $static_condition_collection->is_fulfilled( $environment );
406
	}
407
408
	/**
409
	 * Get environment array for object id
410
	 *
411
	 * @param integer $object_id
412
	 * @return array
413
	 */
414
	abstract protected function get_environment_for_object( $object_id );
415
416
	/**
417
	 * Check container attachment rules against object id
418
	 *
419
	 * @param int $object_id
420
	 * @return bool
421
	 */
422
	abstract public function is_valid_attach_for_object( $object_id );
423
424
	/**
425
	 * Check if all conditions pass for object
426
	 *
427
	 * @param  integer $object_id
428
	 * @return bool
429
	 */
430
	protected function all_conditions_pass( $object_id ) {
431
		$environment = $this->get_environment_for_object( $object_id );
432
		return $this->condition_collection->is_fulfilled( $environment );
433
	}
434
435
	/**
436
	 * Whether this container is currently viewed.
437
	 */
438
	public function should_activate() {
439
		return $this->is_valid_attach();
440
	}
441
442
	/**
443
	 * Perform a check whether the current container has fields
444
	 *
445
	 * @return bool
446
	 */
447
	public function has_fields() {
448
		return (bool) $this->fields;
449
	}
450
451
	/**
452
	 * Returns the private container array of fields.
453
	 * Use only if you are completely aware of what you are doing.
454
	 *
455
	 * @return array
456
	 */
457
	public function get_fields() {
458
		return $this->fields;
459
	}
460
461
	/**
462
	 * Return root field from container with specified name
463
	 *
464
	 * @example crb_complex
465
	 *
466
	 * @param string $field_name
467
	 * @return Field
468
	 */
469
	public function get_root_field_by_name( $field_name ) {
470
		$fields = $this->get_fields();
471
		foreach ( $fields as $field ) {
472
			if ( $field->get_base_name() === $field_name ) {
473
				return $field;
474
			}
475
		}
476
		return null;
477
	}
478
479
	/**
480
	 * Get a regex to match field name patterns used to fetch specific fields
481
	 *
482
	 * @return string
483
	 */
484
	protected function get_field_pattern_regex() {
485
		// matches:
486
		// field_name
487
		// field_name[0]
488
		// field_name[0]:group_name
489
		// field_name:group_name
490
		$regex = '/
491
			\A
492
			(?P<field_name>[a-z0-9_]+)
493
			(?:\[(?P<group_index>\d+)\])?
494
			(?:' .  preg_quote( static::HIERARCHY_GROUP_SEPARATOR, '/' ). '(?P<group_name>[a-z0-9_]+))?
495
			\z
496
		/x';
497
		return $regex;
498
	}
499
500
	/**
501
	 * Return field from container with specified name
502
	 *
503
	 * @example $field_name = 'crb_complex/text_field'
504
	 * @example $field_name = 'crb_complex/complex_2'
505
	 * @example $field_name = 'crb_complex/complex_2:text_group/text_field'
506
	 * @example $field_name = 'crb_complex[3]/complex_2[1]:text_group/text_field'
507
	 *
508
	 * @param string $field_name
509
	 * @return Field
510
	 */
511
	public function get_field_by_name( $field_name ) {
512
		$hierarchy = array_filter( explode( static::HIERARCHY_FIELD_SEPARATOR, $field_name ) );
513
		$field = null;
514
515
		$field_group = $this->get_fields();
516
		$hierarchy_left = $hierarchy;
517
		$field_pattern_regex = $this->get_field_pattern_regex();
518
		$hierarchy_index = array();
519
520
		while ( ! empty( $hierarchy_left ) ) {
521
			$segment = array_shift( $hierarchy_left );
522
			$segment_pieces = array();
523
			if ( ! preg_match( $field_pattern_regex, $segment, $segment_pieces ) ) {
524
				return null;
525
			}
526
527
			$segment_field_name = $segment_pieces['field_name'];
528
			$segment_group_index = isset( $segment_pieces['group_index'] ) ? $segment_pieces['group_index'] : 0;
529
			$segment_group_name = isset( $segment_pieces['group_name'] ) ? $segment_pieces['group_name'] : Group_Field::DEFAULT_GROUP_NAME;
530
531
			foreach ( $field_group as $f ) {
532
				if ( $f->get_base_name() !== $segment_field_name ) {
533
					continue;
534
				}
535
536
				if ( empty( $hierarchy_left ) ) {
537
					$field = clone $f;
538
					$field->set_hierarchy_index( $hierarchy_index );
539
				} else {
540
					if ( ! is_a( $f, 'Carbon_Fields\\Field\\Complex_Field' ) ) {
541
						return null;
542
					}
543
544
					$group = $f->get_group_by_name( $segment_group_name );
545
					if ( ! $group ) {
546
						return null;
547
					}
548
					$field_group = $group->get_fields();
549
					$hierarchy_index[] = $segment_group_index;
550
				}
551
				break;
552
			}
553
		}
554
555
		return $field;
556
	}
557
558
	/**
559
	 * Perform checks whether there is a field registered with the name $name.
560
	 * If not, the field name is recorded.
561
	 *
562
	 * @param string $name
563
	 * @return boolean
564
	 */
565 View Code Duplication
	protected function register_field_name( $name ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
566
		if ( in_array( $name, $this->registered_field_names ) ) {
567
			Incorrect_Syntax_Exception::raise( 'Field name "' . $name . '" already registered' );
568
			return false;
569
		}
570
571
		$this->registered_field_names[] = $name;
572
		return true;
573
	}
574
575
	/**
576
	 * Return whether the datastore instance is the default one or has been overriden
577
	 *
578
	 * @return boolean
579
	 */
580 6
	public function has_default_datastore() {
581 6
		return $this->has_default_datastore;
582
	}
583
584
	/**
585
	 * Set datastore instance
586
	 *
587
	 * @param Datastore_Interface $datastore
588
	 * @return Container $this
589
	 */
590 6 View Code Duplication
	public function set_datastore( Datastore_Interface $datastore, $set_as_default = false ) {
591 6
		if ( $set_as_default && ! $this->has_default_datastore() ) {
592 1
			return $this; // datastore has been overriden with a custom one - abort changing to a default one
593
		}
594 6
		$this->datastore = $datastore;
595 6
		$this->has_default_datastore = $set_as_default;
596
597 6
		foreach ( $this->fields as $field ) {
598
			$field->set_datastore( $this->get_datastore(), true );
599 6
		}
600 6
		return $this;
601
	}
602
603
	/**
604
	 * Get the DataStore instance
605
	 *
606
	 * @return Datastore_Interface $datastore
607
	 */
608 6
	public function get_datastore() {
609 6
		return $this->datastore;
610
	}
611
612
	/**
613
	 * Return WordPress nonce name used to identify the current container instance
614
	 *
615
	 * @return string
616
	 */
617
	protected function get_nonce_name() {
618
		return $this->get_id() . '_nonce';
619
	}
620
621
	/**
622
	 * Return WordPress nonce name used to identify the current container instance
623
	 *
624
	 * @return string
625
	 */
626
	protected function get_nonce_value() {
627
		return wp_create_nonce( $this->get_nonce_name() );
628
	}
629
630
	/**
631
	 * Check if the nonce is present in the request and that it is verified
632
	 *
633
	 * @return bool
634
	 */
635
	protected function verified_nonce_in_request() {
636
		$input = stripslashes_deep( $_REQUEST );
637
		$nonce_name = $this->get_nonce_name();
638
		$nonce_value = isset( $input[ $nonce_name ] ) ? $input[ $nonce_name ] : '';
639
		return wp_verify_nonce( $nonce_value, $nonce_name );
640
	}
641
642
	/**
643
	 * Internal function that creates the tab and associates it with particular field set
644
	 *
645
	 * @param string $tab_name
646
	 * @param array $fields
647
	 * @param int $queue_end
648
	 * @return object $this
649
	 */
650
	private function create_tab( $tab_name, $fields, $queue_end = self::TABS_TAIL ) {
651
		if ( isset( $this->tabs[ $tab_name ] ) ) {
652
			Incorrect_Syntax_Exception::raise( "Tab name duplication for $tab_name" );
653
		}
654
655
		if ( $queue_end === static::TABS_TAIL ) {
656
			$this->tabs[ $tab_name ] = array();
657
		} else if ( $queue_end === static::TABS_HEAD ) {
658
			$this->tabs = array_merge(
659
				array( $tab_name => array() ),
660
				$this->tabs
661
			);
662
		}
663
664
		foreach ( $fields as $field ) {
665
			$field_name = $field->get_name();
666
			$this->tabs[ $tab_name ][ $field_name ] = $field;
667
		}
668
669
		$this->settings['tabs'] = $this->get_tabs_json();
670
	}
671
672
	/**
673
	 * Whether the container is tabbed or not
674
	 *
675
	 * @return bool
676
	 */
677
	public function is_tabbed() {
678
		return (bool) $this->tabs;
679
	}
680
681
	/**
682
	 * Retrieve all fields that are not defined under a specific tab
683
	 *
684
	 * @return array
685
	 */
686
	protected function get_untabbed_fields() {
687
		$tabbed_fields_names = array();
688
		foreach ( $this->tabs as $tab_fields ) {
689
			$tabbed_fields_names = array_merge( $tabbed_fields_names, array_keys( $tab_fields ) );
690
		}
691
692
		$untabbed_fields = array_filter( $this->fields, function( $field ) use ( $tabbed_fields_names ) {
693
			return ! in_array( $field->get_name(), $tabbed_fields_names );
694
		} );
695
696
		return $untabbed_fields;
697
	}
698
699
	/**
700
	 * Retrieve all tabs.
701
	 * Create a default tab if there are any untabbed fields.
702
	 *
703
	 * @return array
704
	 */
705
	protected function get_tabs() {
706
		$untabbed_fields = $this->get_untabbed_fields();
707
708
		if ( ! empty( $untabbed_fields ) ) {
709
			$this->create_tab( __( 'General', 'carbon-fields' ), $untabbed_fields, static::TABS_HEAD );
710
		}
711
712
		return $this->tabs;
713
	}
714
715
	/**
716
	 * Build the tabs JSON
717
	 *
718
	 * @return array
719
	 */
720
	protected function get_tabs_json() {
721
		$tabs_json = array();
722
		$tabs = $this->get_tabs();
723
724
		foreach ( $tabs as $tab_name => $fields ) {
725
			foreach ( $fields as $field_name => $field ) {
726
				$tabs_json[ $tab_name ][] = $field_name;
727
			}
728
		}
729
730
		return $tabs_json;
731
	}
732
733
	/**
734
	 * Get custom CSS classes.
735
	 *
736
	 * @return array<string>
737
	 */
738
	public function get_classes() {
739
		return $this->classes;
740
	}
741
742
	/**
743
	 * Set CSS classes that the container should use.
744
	 *
745
	 * @param string|array<string> $classes
746
	 * @return Container $this
747
	 */
748
	public function set_classes( $classes ) {
749
		$this->classes = Helper::sanitize_classes( $classes );
750
		return $this;
751
	}
752
753
	/**
754
	 * Returns an array that holds the container data, suitable for JSON representation.
755
	 *
756
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
757
	 * @return array
758
	 */
759
	public function to_json( $load ) {
760
		$conditions = $this->condition_collection->evaluate( $this->get_condition_types( true ), $this->get_environment_for_request(), array( 'CUSTOM' ) );
761
		$conditions = $this->condition_translator->fulfillable_to_foreign( $conditions );
762
763
		$container_data = array(
764
			'id' => $this->get_id(),
765
			'type' => $this->type,
766
			'title' => $this->title,
767
			'classes' => $this->get_classes(),
768
			'settings' => $this->settings,
769
			'conditions' => $conditions,
770
			'fields' => array(),
771
			'nonce' => array(
772
				'name' => $this->get_nonce_name(),
773
				'value' => $this->get_nonce_value(),
774
			),
775
		);
776
777
		$fields = $this->get_fields();
778
		foreach ( $fields as $field ) {
779
			$field_data = $field->to_json( $load );
780
			$container_data['fields'][] = $field_data;
781
		}
782
783
		return $container_data;
784
	}
785
786
	/**
787
	 * COMMON USAGE METHODS
788
	 */
789
790
	/**
791
	 * Append array of fields to the current fields set. All items of the array
792
	 * must be instances of Field and their names should be unique for all
793
	 * Carbon containers.
794
	 * If a field does not have DataStore already, the container datastore is
795
	 * assigned to them instead.
796
	 *
797
	 * @param array $fields
798
	 * @return Container $this
799
	 */
800
	public function add_fields( $fields ) {
801
		foreach ( $fields as $field ) {
802
			if ( ! is_a( $field, 'Carbon_Fields\\Field\\Field' ) ) {
803
				Incorrect_Syntax_Exception::raise( 'Object must be of type Carbon_Fields\\Field\\Field' );
804
				return $this;
805
			}
806
807
			$unique = $this->register_field_name( $field->get_name() );
808
			if ( ! $unique ) {
809
				return $this;
810
			}
811
812
			$field->set_context( $this->type );
813
			if ( ! $field->get_datastore() ) {
814
				$field->set_datastore( $this->get_datastore(), $this->has_default_datastore() );
815
			}
816
		}
817
818
		$this->fields = array_merge( $this->fields, $fields );
819
820
		return $this;
821
	}
822
823
	/**
824
	 * Configuration function for adding tab with fields
825
	 *
826
	 * @param string $tab_name
827
	 * @param array $fields
828
	 * @return Container $this
829
	 */
830
	public function add_tab( $tab_name, $fields ) {
831
		$this->add_fields( $fields );
832
		$this->create_tab( $tab_name, $fields );
833
		return $this;
834
	}
835
836
	/**
837
	 * Proxy function to set attachment conditions
838
	 *
839
	 * @see    Fulfillable_Collection::where()
840
	 * @return Container $this
841
	 */
842
	public function where() {
843
		call_user_func_array( array( $this->condition_collection, 'where' ), func_get_args() );
844
		return $this;
845
	}
846
847
	/**
848
	 * Proxy function to set attachment conditions
849
	 *
850
	 * @see    Fulfillable_Collection::or_where()
851
	 * @return Container $this
852
	 */
853
	public function or_where() {
854
		call_user_func_array( array( $this->condition_collection, 'or_where' ), func_get_args() );
855
		return $this;
856
	}
857
}
858