Completed
Push — milestone/2_0/react-ui ( 0e2508...efd2b8 )
by
unknown
02:42
created

Field::set_value_set()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\Carbon_Fields;
6
use Carbon_Fields\Pimple\Container as PimpleContainer;
7
use Carbon_Fields\Datastore\Datastore_Interface;
8
use Carbon_Fields\Datastore\Datastore_Holder_Interface;
9
use Carbon_Fields\Value_Set\Value_Set;
10
use Carbon_Fields\Helper\Helper;
11
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
12
13
/**
14
 * Base field class.
15
 * Defines the key container methods and their default implementations.
16
 * Implements factory design pattern.
17
 */
18
class Field implements Datastore_Holder_Interface {
19
	
20
	/**
21
	 * Array of field class names that have had their activation method called
22
	 *
23
	 * @var array<string>
24
	 */
25
	protected static $activated_field_types = array();
26
27
	/**
28
	 * Globally unique field identificator. Generated randomly
29
	 *
30
	 * @var string
31
	 */
32
	protected $id;
33
34
	/**
35
	 * Stores the initial <kbd>$type</kbd> variable passed to the <code>factory()</code> method
36
	 *
37
	 * @see factory
38
	 * @var string
39
	 */
40
	public $type;
41
42
	/**
43
	 * Array of ancestor field names
44
	 *
45
	 * @var array
46
	 */
47
	protected $hierarchy = array();
48
49
	/**
50
	 * Array of complex entry ids
51
	 *
52
	 * @var array
53
	 */
54
	protected $hierarchy_index = array();
55
56
	/**
57
	 * Field value
58
	 *
59
	 * @var Value_Set
60
	 */
61
	protected $value_set;
62
63
	/**
64
	 * Default field value
65
	 *
66
	 * @var mixed
67
	 */
68
	protected $default_value = '';
69
70
	/**
71
	 * Sanitized field name used as input name attribute during field render
72
	 *
73
	 * @see factory()
74
	 * @see set_name()
75
	 * @var string
76
	 */
77
	protected $name;
78
79
	/**
80
	 * Field name prefix
81
	 *
82
	 * @see set_name()
83
	 * @var string
84
	 */
85
	protected $name_prefix = '_';
86
87
	/**
88
	 * The base field name which is used in the container.
89
	 *
90
	 * @see set_base_name()
91
	 * @var string
92
	 */
93
	protected $base_name;
94
95
	/**
96
	 * Field name used as label during field render
97
	 *
98
	 * @see factory()
99
	 * @see set_label()
100
	 * @var string
101
	 */
102
	protected $label;
103
104
	/**
105
	 * Additional text containing information and guidance for the user
106
	 *
107
	 * @see help_text()
108
	 * @var string
109
	 */
110
	protected $help_text;
111
112
	/**
113
	 * Field DataStore instance to which save, load and delete calls are delegated
114
	 *
115
	 * @see set_datastore()
116
	 * @see get_datastore()
117
	 * @var Datastore_Interface
118
	 */
119
	protected $datastore;
120
121
	/**
122
	 * Flag whether the datastore is the default one or replaced with a custom one
123
	 *
124
	 * @see set_datastore()
125
	 * @see get_datastore()
126
	 * @var boolean
127
	 */
128
	protected $has_default_datastore = true;
129
130
	/**
131
	 * The type of the container this field is in
132
	 *
133
	 * @see get_context()
134
	 * @var string
135
	 */
136
	protected $context;
137
138
	/**
139
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
140
	 *
141
	 * @see set_autoload()
142
	 * @var bool
143
	 */
144
	protected $autoload = false;
145
146
	/**
147
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
148
	 *
149
	 * @see set_lazyload()
150
	 * @var bool
151
	 */
152
	protected $lazyload = false;
153
154
	/**
155
	 * Key-value array of attribtues and their values
156
	 * 
157
	 * @var array
158
	 */
159
	protected $attributes = array();
160
161
	/**
162
	 * Array of attributes the user is allowed to change
163
	 * 
164
	 * @var array<string>
165
	 */
166
	protected $allowed_attributes = array( 'max', 'maxlength', 'min', 'pattern', 'placeholder', 'readonly', 'step', 'type' );
167
168
	/**
169
	 * The width of the field.
170
	 *
171
	 * @see set_width()
172
	 * @var int
173
	 */
174
	protected $width = 0;
175
176
	/**
177
	 * Custom CSS classes.
178
	 *
179
	 * @see set_classes()
180
	 * @var array
181
	 */
182
	protected $classes = array();
183
184
	/**
185
	 * Whether or not this field is required.
186
	 *
187
	 * @see set_required()
188
	 * @var bool
189
	 */
190
	protected $required = false;
191
192
	/**
193
	 * Stores the field conditional logic rules.
194
	 *
195
	 * @var array
196
	 */
197
	protected $conditional_logic = array();
198
199
	/**
200
	 * Whether the field should be included in the response of the requests to the REST API
201
	 *
202
	 * @see  set_visible_in_rest_api
203
	 * @see  get_visible_in_rest_api
204
	 * @var boolean
205
	 */
206
	protected $visible_in_rest_api = false;
207
208
	/**
209
	 * Clone the Value_Set object as well
210
	 *
211
	 * @var array
212
	 */
213
	public function __clone() {
214
		$this->set_value_set( clone $this->get_value_set() );
215
	}
216
217
	/**
218
	 * Create a new field of type $raw_type and name $name and label $label.
219
	 *
220
	 * @param string $raw_type
221
	 * @param string $name lower case and underscore-delimited
222
	 * @param string $label (optional) Automatically generated from $name if not present
223
	 * @return Field
224
	 */
225 16
	public static function factory( $raw_type, $name, $label = null ) {
226 16
		$type = Helper::normalize_type( $raw_type );
227
228
		// stop hidden symbol support when the end user is creating fields ]-[
229
		// @see Field::set_name()
1 ignored issue
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
230 16
		if ( ! empty( $name ) && ! preg_match( '/\A[a-z0-9_]+\z/', $name ) ) {
231 5
			Incorrect_Syntax_Exception::raise( 'Field name can only contain lowercase alphanumeric characters and underscores ("' . $name . '" passed).' );
232
			return;
233
		}
234
235 11
		if ( Carbon_Fields::has( $type, 'fields' ) ) {
236
			return Carbon_Fields::resolve_with_arguments( $type, array(
237
				'type' => $type,
238
				'name' => $name,
239
				'label' => $label,
240
			), 'fields' );
241
		}
242
243
		// Fallback to class name-based resolution
244 11
		$class = Helper::type_to_class( $type, __NAMESPACE__, '_Field' );
245 11 View Code Duplication
		if ( ! class_exists( $class ) ) {
246 4
			Incorrect_Syntax_Exception::raise( 'Unknown field "' . $raw_type . '".' );
247 1
			$class = __NAMESPACE__ . '\\Broken_Field';
248 1
		}
249
250 8
		$field = new $class( $type, $name, $label );
251 7
		return $field;
252
	}
253
254
	/**
255
	 * An alias of factory().
256
	 *
257
	 * @see Field::factory()
258
	 * @return Field
259
	 */
260 16
	public static function make( $type, $name, $label = null ) {
261 16
		return static::factory( $type, $name, $label );
262
	}
263
264
	/**
265
	 * Create a field from a certain type with the specified label.
266
	 * 
267
	 * @param string $type  Field type
268
	 * @param string $name  Field name
269
	 * @param string $label Field label
270
	 */
271 8
	public function __construct( $type, $name, $label ) {
272 8
		Carbon_Fields::verify_boot();
273
		
274 8
		$this->type = $type;
275 8
		$this->set_base_name( $name );
276 8
		$this->set_name( $name );
277 7
		$this->set_label( $label );
278
279
		// Pick random ID
280 7
		$random_string = md5( mt_rand() . $this->get_name() . $this->get_label() );
281 7
		$random_string = substr( $random_string, 0, 5 ); // 5 chars should be enough
282 7
		$this->id = 'carbon-' . $random_string;
283
284 7
		$this->init();
285 7
	}
286
287
	/**
288
	 * Returns the type of the field based on the class.
289
	 * The class is stripped by the "CarbonFields" prefix.
290
	 * Also the "Field" suffix is removed.
291
	 * Then underscores and backslashes are removed.
292
	 *
293
	 * @return string
294
	 */
295
	public function get_type() {
296
		return Helper::class_to_type( get_class( $this ), '_Field' );
297
	}
298
299
	/**
300
	 * Activate the field once the container is attached.
301
	 */
302
	public function activate() {
303
		$this->admin_init();
304
305
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_scripts' ), 5 );
306
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_styles' ), 5 );
307
		static::activate_field_type( get_class( $this ) );
308
309
		do_action( 'carbon_fields_field_activated', $this );
310
	}
311
312
	/**
313
	 * Activate a field type
314
	 * 
315
	 * @param string $class_name
316
	 */
317
	public static function activate_field_type( $class_name ) {
318
		if ( in_array( $class_name, static::$activated_field_types ) ) {
319
			return;
320
		}
321
322
		add_action( 'admin_print_footer_scripts', array( $class_name, 'admin_enqueue_scripts' ), 5 );
323
		call_user_func( array( $class_name, 'field_type_activated' ) );
324
325
		static::$activated_field_types[] = $class_name;
326
	}
327
328
	/**
329
	 * Prepare the field type for use
330
	 * Called once per field type when activated
331
	 */
332
	public static function field_type_activated() {}
333
334
	/**
335
	 * Get array of hierarchy field names
336
	 *
337
	 * @return array
338
	 */
339
	public function get_hierarchy() {
340
		return $this->hierarchy;
341
	}
342
343
	/**
344
	 * Set array of hierarchy field names
345
	 */
346
	public function set_hierarchy( $hierarchy ) {
347
		$this->hierarchy = $hierarchy;
348
	}
349
350
	/**
351
	 * Get array of hierarchy indexes
352
	 *
353
	 * @return array
354
	 */
355
	public function get_hierarchy_index() {
356
		return $this->hierarchy_index;
357
	}
358
359
	/**
360
	 * Set array of hierarchy indexes
361
	 */
362
	public function set_hierarchy_index( $hierarchy_index ) {
363
		$hierarchy_index = ( ! empty( $hierarchy_index ) ) ? $hierarchy_index : array();
364
		$this->hierarchy_index = $hierarchy_index;
365
	}
366
367
	/**
368
	 * Return whether the field is a root field and holds a single value
369
	 *
370
	 * @return bool
371
	 */
372 3
	public function is_simple_root_field() {
373 3
		$hierarchy = $this->get_hierarchy();
374
		return (
375 3
			empty( $hierarchy )
376 3
			&&
377 2
			in_array( $this->get_value_set()->get_type(), array( Value_Set::TYPE_SINGLE_VALUE, Value_Set::TYPE_MULTIPLE_PROPERTIES ) )
378 3
		);
379
	}
380
381
	/**
382
	 * Perform instance initialization
383
	 */
384
	public function init() {}
385
386
	/**
387
	 * Instance initialization when in the admin area
388
	 * Called during field boot
389
	 */
390
	public function admin_init() {}
391
392
	/**
393
	 * Enqueue scripts and styles in admin
394
	 * Called once per field type
395
	 */
396
	public static function admin_enqueue_scripts() {}
397
398
	/**
399
	 * Get value from datastore
400
	 *
401
	 * @param bool $fallback_to_default
402
	 * @return mixed
403
	 */
404
	protected function get_value_from_datastore( $fallback_to_default = true ) {
405
		$value = $this->get_datastore()->load( $this );
406
407
		if ( $value === null && $fallback_to_default ) {
408
			$value = $this->get_default_value();
409
		}
410
411
		return $value;
412
	}
413
414
	/**
415
	 * Load value from datastore
416
	 */
417 2
	public function load() {
418 2
		$this->set_value( $this->get_value_from_datastore() );
419 2
	}
420
421
	/**
422
	 * Save value to storage
423
	 */
424 5
	public function save() {
425 5
		$delete_on_save = ! in_array( $this->get_value_set()->get_type(), array( Value_Set::TYPE_SINGLE_VALUE, Value_Set::TYPE_MULTIPLE_PROPERTIES ) );
426 5
		$delete_on_save = apply_filters( 'carbon_fields_should_delete_field_value_on_save', $delete_on_save, $this );
427 5
		if ( $delete_on_save ) {
428 2
			$this->delete();
429 2
		}
430
431 5
		$save = apply_filters( 'carbon_fields_should_save_field_value', true, $this->get_value(), $this );
432 5
		if ( $save ) {
433 5
			$this->get_datastore()->save( $this );
434 5
		}
435 5
	}
436
437
	/**
438
	 * Delete value from storage
439
	 */
440 1
	public function delete() {
441 1
		$this->get_datastore()->delete( $this );
442 1
	}
443
444
	/**
445
	 * Load the field value from an input array based on it's name
446
	 *
447
	 * @param array $input Array of field names and values.
448
	 */
449 2
	public function set_value_from_input( $input ) {
450 2
		if ( isset( $input[ $this->get_name() ] ) ) {
451 1
			$this->set_value( $input[ $this->get_name() ] );
452 1
		} else {
453 1
			$this->clear_value();
454
		}
455 2
	}
456
457
	/**
458
	 * Return whether the datastore instance is the default one or has been overriden
459
	 *
460
	 * @return boolean
461
	 */
462
	public function has_default_datastore() {
463
		return $this->has_default_datastore;
464
	}
465
466
	/**
467
	 * Get the DataStore instance
468
	 *
469
	 * @return Datastore_Interface $datastore
470
	 */
471 1
	public function get_datastore() {
472 1
		return $this->datastore;
473
	}
474
475
	/**
476
	 * Set datastore instance
477
	 *
478
	 * @param  Datastore_Interface $datastore
479
	 * @param  boolean             $set_as_default
480
	 * @return object              $this
481
	 */
482 1
	public function set_datastore( Datastore_Interface $datastore, $set_as_default = false ) {
483 1
		if ( $set_as_default && ! $this->has_default_datastore() ) {
484
			return $this; // datastore has been overriden with a custom one - abort changing to a default one
485
		}
486 1
		$this->datastore = $datastore;
487 1
		$this->has_default_datastore = $set_as_default;
488 1
		return $this;
489
	}
490
491
	/**
492
	 * Return the type of the container this field is in
493
	 *
494
	 * @return string
495
	 */
496
	public function get_context() {
497
		return $this->context;
498
	}
499
500
	/**
501
	 * Assign the type of the container this field is in
502
	 *
503
	 * @param string
504
	 * @return object $this
505
	 */
506
	public function set_context( $context ) {
507
		$this->context = $context;
508
		return $this;
509
	}
510
511
	/**
512
	 * Get the Value_Set object
513
	 *
514
	 * @return Value_Set
515
	 */
516 2
	public function get_value_set() {
517 2
		if ( $this->value_set === null ) {
518 1
			$this->set_value_set( new Value_Set() );
519 1
		}
520 2
		return $this->value_set;
521
	}
522
523
	/**
524
	 * Set the Value_Set object
525
	 *
526
	 * @param Value_Set $value_set
527
	 */
528 1
	public function set_value_set( $value_set ) {
529 1
		$this->value_set = $value_set;
530 1
	}
531
532
	/**
533
	 * Alias for $this->get_value_set()->get(); with fallback to default value
534
	 *
535
	 * @return mixed
536
	 */
537 3
	public function get_value() {
538 3
		if ( $this->get_value_set()->get() === null ) {
539 1
			$this->set_value( $this->get_default_value() );
540 1
		}
541 3
		return $this->get_value_set()->get();
542
	}
543
544
	/**
545
	 * Alias for $this->get_value_set()->get_set(); with fallback to default value
546
	 *
547
	 * @return array<array>
548
	 */
549
	public function get_full_value() {
550
		if ( $this->get_value_set()->get_set() === null ) {
551
			$this->set_value( $this->get_default_value() );
552
		}
553
		return $this->get_value_set()->get_set();
554
	}
555
556
	/**
557
	 * Return a differently formatted value for end-users
558
	 *
559
	 * @return mixed
560
	 */
561 2
	public function get_formatted_value() {
562 2
		return $this->get_value();
563
	}
564
565
	/**
566
	 * Alias for $this->get_value_set()->set( $value );
567
	 */
568 1
	public function set_value( $value ) {
569 1
		$this->get_value_set()->set( $value );
570 1
	}
571
572
	/**
573
	 * Clear the field value to a blank one (but not the default one)
574
	 */
575
	public function clear_value() {
576
		$this->get_value_set()->set( array() );
577
	}
578
579
	/**
580
	 * Get default field value
581
	 *
582
	 * @return mixed
583
	 */
584 1
	public function get_default_value() {
585 1
		return $this->default_value;
586
	}
587
588
	/**
589
	 * Set default field value
590
	 *
591
	 * @param mixed $default_value
592
	 */
593 1
	public function set_default_value( $default_value ) {
594 1
		$this->default_value = $default_value;
595 1
		return $this;
596
	}
597
598
	/**
599
	 * Return the field base name.
600
	 *
601
	 * @return string
602
	 */
603
	public function get_base_name() {
604
		return $this->base_name;
605
	}
606
607
	/**
608
	 * Set field base name as defined in the container.
609
	 */
610
	public function set_base_name( $name ) {
611
		$this->base_name = $name;
612
	}
613
614
	/**
615
	 * Return the field name
616
	 *
617
	 * @return string
618
	 */
619 2
	public function get_name() {
620 2
		return $this->name;
621
	}
622
623
	/**
624
	 * Set field name.
625
	 * Use only if you are completely aware of what you are doing.
626
	 *
627
	 * @param string $name Field name, either sanitized or not
628
	 */
629 3
	public function set_name( $name ) {
630 3
		if ( empty( $name ) ) {
631 1
			Incorrect_Syntax_Exception::raise( 'Field name can\'t be empty' );
632
			return;
633
		}
634
635
		// symbols ]-[ are supported in a hidden way - required for widgets to work (WP imposes dashes and square brackets on field names)
636 2
		$regex = '/\A[a-z0-9_\-\[\]]+\z/';
637 2
		if ( ! preg_match( $regex, $name ) ) {
638
			Incorrect_Syntax_Exception::raise( 'Field name can only contain lowercase alphanumeric characters and underscores ("' . $name . '" passed).' );
639
			return;
640
		}
641
642 2
		$name_prefix = $this->get_name_prefix();
643 2
		$name = ( substr( $name, 0, strlen( $name_prefix ) ) !== $name_prefix ? $name_prefix . $name : $name );
644
645 2
		$this->name = $name;
646 2
	}
647
648
	/**
649
	 * Return the field name prefix
650
	 *
651
	 * @return string
652
	 */
653 3
	public function get_name_prefix() {
654 3
		return $this->name_prefix;
655
	}
656
657
	/**
658
	 * Set field name prefix
659
	 * Use only if you are completely aware of what you are doing.
660
	 *
661
	 * @param string $name_prefix
662
	 */
663 3
	public function set_name_prefix( $name_prefix ) {
664 3
		$name_prefix = strval( $name_prefix );
665 3
		$old_prefix_length = strlen( $this->name_prefix );
666 3
		$this->name_prefix = '';
667 3
		$this->set_name( substr( $this->get_name(), $old_prefix_length ) );
668
669 3
		$this->name_prefix = $name_prefix;
670 3
		$this->set_name( $this->name_prefix . $this->get_name() );
671 3
	}
672
673
	/**
674
	 * Return field label.
675
	 *
676
	 * @return string
677
	 */
678
	public function get_label() {
679
		return $this->label;
680
	}
681
682
	/**
683
	 * Set field label.
684
	 *
685
	 * @param string $label If null, the label will be generated from the field name
686
	 */
687
	public function set_label( $label ) {
688
		if ( is_null( $label ) ) {
689
			// Try to guess field label from it's name
690
			$label = Helper::normalize_label( $this->get_name() );
691
		}
692
693
		$this->label = $label;
694
	}
695
696
	/**
697
	 * Get a key-value array of attributes
698
	 * 
699
	 * @return array
700
	 */
701
	public function get_attributes() {
702
		return $this->attributes;
703
	}
704
705
	/**
706
	 * Get an attribute value
707
	 * 
708
	 * @param  string $name
709
	 * @return string
710
	 */
711
	public function get_attribute( $name ) {
712
		return isset( $this->attributes[ $name ] ) ? $this->attributes[ $name ] : '';
713
	}
714
715
	/**
716
	 * Set an attribute and it's value
717
	 * 
718
	 * @param  string $name
719
	 * @param  string $value
720
	 * @return Field  $this
721
	 */
722
	public function set_attribute( $name, $value = '' ) {
723
		if ( ! in_array( $name, $this->allowed_attributes ) ) {
724
			Incorrect_Syntax_Exception::raise( 'Only the following attributes are allowed: ' . implode( ', ', $this->allowed_attributes ) . '.' );
725
			return $this;
726
		}
727
		$this->attributes[ $name ] = $value;
728
		return $this;
729
	}
730
731
	/**
732
	 * Return the field help text
733
	 *
734
	 * @return object $this
735
	 */
736
	public function get_help_text() {
737
		return $this->help_text;
738
	}
739
740
	/**
741
	 * Set additional text to be displayed during field render,
742
	 * containing information and guidance for the user
743
	 *
744
	 * @return object $this
745
	 */
746
	public function set_help_text( $help_text ) {
747
		$this->help_text = $help_text;
748
		return $this;
749
	}
750
751
	/**
752
	 * Alias for set_help_text()
753
	 *
754
	 * @see set_help_text()
755
	 * @return object $this
756
	 */
757
	public function help_text( $help_text ) {
758
		return $this->set_help_text( $help_text );
759
	}
760
761
	/**
762
	 * Return whether or not this value should be auto loaded.
763
	 *
764
	 * @return bool
765
	 */
766
	public function get_autoload() {
767
		return $this->autoload;
768
	}
769
770
	/**
771
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
772
	 *
773
	 * @param bool $autoload
774
	 * @return object $this
775
	 */
776
	public function set_autoload( $autoload ) {
777
		$this->autoload = $autoload;
778
		return $this;
779
	}
780
781
	/**
782
	 * Return whether or not this field should be lazyloaded.
783
	 *
784
	 * @return bool
785
	 */
786
	public function get_lazyload() {
787
		return $this->lazyload;
788
	}
789
790
	/**
791
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
792
	 *
793
	 * @param bool $lazyload
794
	 * @return object $this
795
	 */
796
	public function set_lazyload( $lazyload ) {
797
		$this->lazyload = $lazyload;
798
		return $this;
799
	}
800
801
	/**
802
	 * Get the field width.
803
	 *
804
	 * @return int $width
805
	 */
806
	public function get_width() {
807
		return $this->width;
808
	}
809
810
	/**
811
	 * Set the field width.
812
	 *
813
	 * @param int $width
814
	 * @return object $this
815
	 */
816
	public function set_width( $width ) {
817
		$this->width = (int) $width;
818
		return $this;
819
	}
820
821
	/**
822
	 * Get custom CSS classes.
823
	 *
824
	 * @return array<string>
825
	 */
826
	public function get_classes() {
827
		return $this->classes;
828
	}
829
830
	/**
831
	 * Set CSS classes that the container should use.
832
	 *
833
	 * @param string|array<string> $classes
834
	 * @return object $this
835
	 */
836
	public function set_classes( $classes ) {
837
		$this->classes = Helper::sanitize_classes( $classes );
838
		return $this;
839
	}
840
841
	/**
842
	 * Whether this field is mandatory for the user
843
	 *
844
	 * @param bool $required
845
	 * @return object $this
846
	 */
847
	public function set_required( $required = true ) {
848
		$this->required = $required;
849
		return $this;
850
	}
851
852
	/**
853
	 * Return whether this field is mandatory for the user
854
	 *
855
	 * @return bool
856
	 */
857
	public function is_required() {
858
		return $this->required;
859
	}
860
861
	/**
862
	 * HTML id attribute getter.
863
	 * @return string
864
	 */
865 1
	public function get_id() {
866 1
		return $this->id;
867
	}
868
869
	/**
870
	 * HTML id attribute setter
871
	 * @param string $id
872
	 */
873 1
	public function set_id( $id ) {
874 1
		$this->id = $id;
875 1
	}
876
877
	/**
878
	 * Set the field visibility conditional logic.
879
	 *
880
	 * @param array
881
	 */
882 8
	public function set_conditional_logic( $rules ) {
883 8
		$this->conditional_logic = $this->parse_conditional_rules( $rules );
884 3
		return $this;
885
	}
886
887
	/**
888
	 * Get the conditional logic rules
889
	 *
890
	 * @return array
891
	 */
892 3
	public function get_conditional_logic() {
893 3
		return $this->conditional_logic;
894
	}
895
896
	/**
897
	 * Validate and parse the conditional logic rules.
898
	 *
899
	 * @param array $rules
900
	 * @return array
901
	 */
902
	protected function parse_conditional_rules( $rules ) {
903
		if ( ! is_array( $rules ) ) {
904
			Incorrect_Syntax_Exception::raise( 'Conditional logic rules argument should be an array.' );
905
			return array();
906
		}
907
908
		$allowed_operators = array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'INCLUDES', 'EXCLUDES' );
909
910
		$parsed_rules = array(
911
			'relation' => Helper::get_relation_type_from_array( $rules ),
912
			'rules' => array(),
913
		);
914
915
		foreach ( $rules as $key => $rule ) {
916
			if ( $key === 'relation' ) {
917
				continue; // Skip the relation key as it is already handled above
918
			}
919
920
			// Check if the rule is valid
921
			if ( ! is_array( $rule ) || empty( $rule['field'] ) ) {
922
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic rule format. The rule should be an array with the "field" key set.' );
923
				return array();
924
			}
925
926
			// Fill in optional keys with defaults
927
			$rule = array_merge( array(
928
				'compare' => '=',
929
				'value' => '',
930
			), $rule );
931
932 View Code Duplication
			if ( ! in_array( $rule['compare'], $allowed_operators ) ) {
933
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic compare operator: <code>' . $rule['compare'] . '</code><br>Allowed operators are: <code>' .
934
				implode( ', ', $allowed_operators ) . '</code>' );
935
				return array();
936
			}
937
938
			if ( in_array( $rule['compare'], array( 'IN', 'NOT IN' ) ) && ! is_array( $rule['value'] ) ) {
939
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic value format. An array is expected, when using the "' . $rule['compare'] . '" operator.' );
940
				return array();
941
			}
942
943
			$parsed_rules['rules'][] = $rule;
944
		}
945
946
		return $parsed_rules;
947
	}
948
949
	/**
950
	 * Set the REST visibility of the field
951
	 * 
952
	 * @param bool $visible
953
	 */
954
	public function set_visible_in_rest_api( $visible ) {
955
		$this->visible_in_rest_api = $visible;
956
	}
957
	
958
	/**
959
	 * Get the REST visibility of the field
960
	 * 
961
	 * @return bool
962
	 */
963
	public function get_visible_in_rest_api() {
964
		return $this->visible_in_rest_api;
965
	}
966
967
	/**
968
	 * Configuration function for setting the field visibility in the response of the requests to the REST API
969
	 * 
970
	 * @param bool $visible
971
	 * @return Field $this
972
	 */
973
	public function show_in_rest( $visible = true ) {
974
		$this->set_visible_in_rest_api( $visible );
975
		return $this;
976
	}
977
978
	/**
979
	 * Returns an array that holds the field data, suitable for JSON representation.
980
	 *
981
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
982
	 * @return array
983
	 */
984
	public function to_json( $load ) {
985
		if ( $load ) {
986
			$this->load();
987
		}
988
989
		$field_data = array(
990
			'id' => $this->get_id(),
991
			'type' => $this->get_type(),
992
			'label' => $this->get_label(),
993
			'name' => $this->get_name(),
994
			'base_name' => $this->get_base_name(),
995
			'value' => $this->get_formatted_value(),
996
			'default_value' => $this->get_default_value(),
997
			'attributes' => (object) $this->get_attributes(),
998
			'help_text' => $this->get_help_text(),
999
			'context' => $this->get_context(),
1000
			'required' => $this->is_required(),
1001
			'lazyload' => $this->get_lazyload(),
1002
			'width' => $this->get_width(),
1003
			'classes' => $this->get_classes(),
1004
			'conditional_logic' => $this->get_conditional_logic(),
1005
		);
1006
1007
		return $field_data;
1008
	}
1009
1010
	/**
1011
	 * Hook administration scripts.
1012
	 */
1013
	public static function admin_hook_scripts() {
1014
		wp_enqueue_media();
1015
	}
1016
1017
	/**
1018
	 * Hook administration styles.
1019
	 */
1020
	public static function admin_hook_styles() {
1021
		wp_enqueue_style( 'thickbox' );
1022
	}
1023
}
1024