Completed
Push — milestone/2_0/react-ui ( 7118aa...a33d5e )
by
unknown
06:00
created

Field::set_classes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 4
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\App;
6
use Carbon_Fields\Datastore\Datastore_Interface;
7
use Carbon_Fields\Datastore\Datastore_Holder_Interface;
8
use Carbon_Fields\Value_Set\Value_Set;
9
use Carbon_Fields\Helper\Helper;
10
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
11
12
/**
13
 * Base field class.
14
 * Defines the key container methods and their default implementations.
15
 * Implements factory design pattern.
16
 **/
17
class Field implements Datastore_Holder_Interface {
18
	
19
	/**
20
	 * Globally unique field identificator. Generated randomly
21
	 *
22
	 * @var string
23
	 */
24
	protected $id;
25
26
	/**
27
	 * Stores the initial <kbd>$type</kbd> variable passed to the <code>factory()</code> method
28
	 *
29
	 * @see factory
30
	 * @var string
31
	 */
32
	public $type;
33
34
	/**
35
	 * Array of ancestor field names
36
	 *
37
	 * @var array
38
	 **/
39
	protected $hierarchy = array();
40
41
	/**
42
	 * Array of complex entry ids
43
	 *
44
	 * @var array
45
	 **/
46
	protected $hierarchy_index = array();
47
48
	/**
49
	 * Field value
50
	 *
51
	 * @var Value_Set
52
	 */
53
	protected $value_set;
54
55
	/**
56
	 * Default field value
57
	 *
58
	 * @var mixed
59
	 */
60
	protected $default_value = '';
61
62
	/**
63
	 * Sanitized field name used as input name attribute during field render
64
	 *
65
	 * @see factory()
66
	 * @see set_name()
67
	 * @var string
68
	 */
69
	protected $name;
70
71
	/**
72
	 * Field name prefix
73
	 *
74
	 * @see set_name()
75
	 * @var string
76
	 */
77
	protected $name_prefix = '_';
78
79
	/**
80
	 * The base field name which is used in the container.
81
	 *
82
	 * @see set_base_name()
83
	 * @var string
84
	 */
85
	protected $base_name;
86
87
	/**
88
	 * Field name used as label during field render
89
	 *
90
	 * @see factory()
91
	 * @see set_label()
92
	 * @var string
93
	 */
94
	protected $label;
95
96
	/**
97
	 * Additional text containing information and guidance for the user
98
	 *
99
	 * @see help_text()
100
	 * @var string
101
	 */
102
	protected $help_text;
103
104
	/**
105
	 * Field DataStore instance to which save, load and delete calls are delegated
106
	 *
107
	 * @see set_datastore()
108
	 * @see get_datastore()
109
	 * @var Datastore_Interface
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
	 * The type of the container this field is in
124
	 *
125
	 * @see get_context()
126
	 * @var string
127
	 */
128
	protected $context;
129
130
	/**
131
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
132
	 *
133
	 * @see set_autoload()
134
	 * @var bool
135
	 **/
136
	protected $autoload = false;
137
138
	/**
139
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
140
	 *
141
	 * @see set_lazyload()
142
	 * @var bool
143
	 **/
144
	protected $lazyload = false;
145
146
	/**
147
	 * The width of the field.
148
	 *
149
	 * @see set_width()
150
	 * @var int
151
	 **/
152
	protected $width = 0;
153
154
	/**
155
	 * Custom CSS classes.
156
	 *
157
	 * @see add_class()
158
	 * @var array
159
	 **/
160
	protected $classes = array();
161
162
	/**
163
	 * Whether or not this field is required.
164
	 *
165
	 * @see set_required()
166
	 * @var bool
167
	 **/
168
	protected $required = false;
169
170
	/**
171
	 * Stores the field conditional logic rules.
172
	 *
173
	 * @var array
174
	 **/
175
	protected $conditional_logic = array();
176
177
	/**
178
	 * Whether the field should be included in the response of the requests to the REST API
179
	 *
180
	 * @see  set_visible_in_rest_api
181
	 * @see  get_visible_in_rest_api
182
	 * @var boolean
183
	 */
184
	protected $visible_in_rest_api = false;
185
186
	/**
187
	 * Clone the Value_Set object as well
188
	 *
189
	 * @var array
190
	 **/
191
	public function __clone() {
192
		$this->set_value_set( clone $this->get_value_set() );
193
	}
194
195
	/**
196
	 * Create a new field of type $type and name $name and label $label.
197
	 *
198
	 * @param string $type
199
	 * @param string $name lower case and underscore-delimited
200
	 * @param string $label (optional) Automatically generated from $name if not present
201
	 * @return Field
202
	 **/
203 14
	public static function factory( $type, $name, $label = null ) {
204 14
		$type = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $type ) ) );
205
206 14
		$class = __NAMESPACE__ . '\\' . $type . '_Field';
207
208 14 View Code Duplication
		if ( ! class_exists( $class ) ) {
209 4
			Incorrect_Syntax_Exception::raise( 'Unknown field "' . $type . '".' );
210 1
			$class = __NAMESPACE__ . '\\Broken_Field';
211 1
		}
212
213 11
		$field = new $class( $type, $name, $label );
214
215 7
		return $field;
216
	}
217
218
	/**
219
	 * An alias of factory().
220
	 *
221
	 * @see Field::factory()
222
	 * @return Field
223
	 **/
224 14
	public static function make( $type, $name, $label = null ) {
225 14
		return static::factory( $type, $name, $label );
226
	}
227
228
	/**
229
	 * Create a field from a certain type with the specified label.
230
	 * 
231
	 * @param string $type  Field type
232
	 * @param string $name  Field name
233
	 * @param string $label Field label
234
	 */
235 11
	protected function __construct( $type, $name, $label ) {
236 11
		App::verify_boot();
237
		
238 11
		$this->type = $type;
239 11
		$this->set_base_name( $name );
240 11
		$this->set_name( $name );
241 7
		$this->set_label( $label );
242
243
		// Pick random ID
244 7
		$random_string = md5( mt_rand() . $this->get_name() . $this->get_label() );
245 7
		$random_string = substr( $random_string, 0, 5 ); // 5 chars should be enough
246 7
		$this->id = 'carbon-' . $random_string;
247
248 7
		$this->init();
249 7
	}
250
251
	/**
252
	 * Cleans up an object class for usage as HTML class
253
	 *
254
	 * @param string $type
255
	 * @return string
256
	 */
257
	protected function clean_type( $type ) {
258
		$remove = array(
259
			'_',
260
			'\\',
261
			'CarbonFields',
262
			'Field',
263
		);
264
		$clean_class = str_replace( $remove, '', $type );
265
266
		return $clean_class;
267
	}
268
269
	/**
270
	 * Returns the type of the field based on the class.
271
	 * The class is stripped by the "CarbonFields" prefix.
272
	 * Also the "Field" suffix is removed.
273
	 * Then underscores and backslashes are removed.
274
	 *
275
	 * @return string
276
	 */
277
	public function get_type() {
278
		$class = get_class( $this );
279
280
		return $this->clean_type( $class );
281
	}
282
283
	/**
284
	 * Activate the field once the container is attached.
285
	 */
286
	public function activate() {
287
		$this->admin_init();
288
289
		add_action( 'admin_footer', array( get_class(), 'admin_hook_scripts' ), 5 );
290
		add_action( 'admin_footer', array( get_class(), 'admin_hook_styles' ), 5 );
291
		add_action( 'admin_footer', array( get_class( $this ), 'admin_enqueue_scripts' ), 5 );
292
293
		do_action( 'crb_field_activated', $this );
294
	}
295
296
	/**
297
	 * Get array of hierarchy field names
298
	 *
299
	 * @return array
300
	 **/
301
	public function get_hierarchy() {
302
		return $this->hierarchy;
303
	}
304
305
	/**
306
	 * Set array of hierarchy field names
307
	 **/
308
	public function set_hierarchy( $hierarchy ) {
309
		$this->hierarchy = $hierarchy;
310
	}
311
312
	/**
313
	 * Get array of hierarchy indexes
314
	 *
315
	 * @return array
316
	 **/
317
	public function get_hierarchy_index() {
318
		return $this->hierarchy_index;
319
	}
320
321
	/**
322
	 * Set array of hierarchy indexes
323
	 **/
324
	public function set_hierarchy_index( $hierarchy_index ) {
325
		$hierarchy_index = ( ! empty( $hierarchy_index ) ) ? $hierarchy_index : array( 0 );
326
		$this->hierarchy_index = $hierarchy_index;
327
	}
328
329
	/**
330
	 * Return whether the field is a root field and holds a single value
331
	 *
332
	 * @return bool
333
	 **/
334 3
	public function is_simple_root_field() {
335 3
		$hierarchy = $this->get_hierarchy();
336
		return (
337 3
			empty( $hierarchy )
338 3
			&&
339
			(
340 2
				$this->get_value_set()->get_type() === Value_Set::TYPE_SINGLE_VALUE
341 2
				||
342 2
				$this->get_value_set()->get_type() === Value_Set::TYPE_MULTIPLE_PROPERTIES
343 2
			)
344 3
		);
345
	}
346
347
	/**
348
	 * Perform instance initialization
349
	 **/
350
	public function init() {}
351
352
	/**
353
	 * Instance initialization when in the admin area.
354
	 * Called during field boot.
355
	 **/
356
	public function admin_init() {}
357
358
	/**
359
	 * Enqueue admin scripts.
360
	 * Called once per field type.
361
	 **/
362
	public static function admin_enqueue_scripts() {}
363
364
	/**
365
	 * Get value from datastore
366
	 *
367
	 * @param bool $fallback_to_default
368
	 * @return mixed
369
	 **/
370
	protected function get_value_from_datastore( $fallback_to_default = true ) {
371
		$value_tree = $this->get_datastore()->load( $this );
372
		
373
		$value = null;
374
		if ( isset( $value_tree['value_set'] ) ) {
375
			$value = $value_tree['value_set'];
376
		}
377
378
		if ( $value === null && $fallback_to_default ) {
379
			$value = $this->get_default_value();
380
		}
381
382
		return $value;
383
	}
384
385
	/**
386
	 * Load value from datastore
387
	 **/
388 2
	public function load() {
389 2
		$value = $this->get_value_from_datastore();
390 2
		$this->set_value( $value );
391 2
	}
392
393
	/**
394
	 * Save value to storage
395
	 **/
396 5
	public function save() {
397 5
		$delete_on_save = ! in_array( $this->get_value_set()->get_type(), array( Value_Set::TYPE_SINGLE_VALUE, Value_Set::TYPE_MULTIPLE_PROPERTIES ) );
398 5
		$delete_on_save = apply_filters( 'carbon_fields_should_delete_field_value_on_save', $delete_on_save, $this );
399 5
		if ( $delete_on_save ) {
400 2
			$this->delete();
401 2
		}
402
403 5
		$save = apply_filters( 'carbon_fields_should_save_field_value', true, $this->get_value(), $this );
404 5
		if ( $save ) {
405 5
			$this->get_datastore()->save( $this );
406 5
		}
407 5
	}
408
409
	/**
410
	 * Delete value from storage
411
	 */
412 1
	public function delete() {
413 1
		$this->get_datastore()->delete( $this );
414 1
	}
415
416
	/**
417
	 * Load the field value from an input array based on it's name
418
	 *
419
	 * @param array $input Array of field names and values.
420
	 **/
421 2
	public function set_value_from_input( $input ) {
422 2
		if ( isset( $input[ $this->get_name() ] ) ) {
423 1
			$this->set_value( $input[ $this->get_name() ] );
424 1
		} else {
425 1
			$this->set_value( array() );
426
		}
427 2
	}
428
429
	/**
430
	 * Return whether the datastore instance is the default one or has been overriden
431
	 *
432
	 * @return boolean
433
	 **/
434
	public function has_default_datastore() {
435
		return $this->has_default_datastore;
436
	}
437
438
	/**
439
	 * Get the DataStore instance
440
	 *
441
	 * @return Datastore_Interface $datastore
442
	 **/
443 1
	public function get_datastore() {
444 1
		return $this->datastore;
445
	}
446
447
	/**
448
	 * Set datastore instance
449
	 *
450
	 * @param Datastore_Interface $datastore
451
	 * @return object $this
452
	 **/
453 1
	public function set_datastore( Datastore_Interface $datastore, $set_as_default = false ) {
454 1
		if ( $set_as_default && ! $this->has_default_datastore() ) {
455
			return $this; // datastore has been overriden with a custom one - abort changing to a default one
456
		}
457 1
		$this->datastore = $datastore;
458 1
		$this->has_default_datastore = $set_as_default;
459 1
		return $this;
460
	}
461
462
	/**
463
	 * Return the type of the container this field is in
464
	 *
465
	 * @return string
466
	 **/
467
	public function get_context() {
468
		return $this->context;
469
	}
470
471
	/**
472
	 * Assign the type of the container this field is in
473
	 *
474
	 * @param string
475
	 * @return object $this
476
	 **/
477
	public function set_context( $context ) {
478
		$this->context = $context;
479
		return $this;
480
	}
481
482
	/**
483
	 * Get the Value_Set object
484
	 *
485
	 * @return Value_Set
486
	 **/
487 2
	public function get_value_set() {
488 2
		if ( $this->value_set === null ) {
489 1
			$this->set_value_set( new Value_Set() );
490 1
		}
491 2
		return $this->value_set;
492
	}
493
494
	/**
495
	 * Set the Value_Set object
496
	 *
497
	 * @param Value_Set $value_set
498
	 **/
499 1
	public function set_value_set( $value_set ) {
500 1
		$this->value_set = $value_set;
501 1
	}
502
503
	/**
504
	 * Alias for $this->get_value_set()->get(); with fallback to default value
505
	 *
506
	 * @return mixed
507
	 **/
508 3
	public function get_value() {
509 3
		if ( $this->get_value_set()->get() === null ) {
510 1
			$this->set_value( $this->get_default_value() );
511 1
		}
512 3
		return $this->get_value_set()->get();
513
	}
514
515
	/**
516
	 * Alias for $this->get_value_set()->get_set(); with fallback to default value
517
	 *
518
	 * @return array<array>
519
	 **/
520
	public function get_full_value() {
521
		if ( $this->get_value_set()->get_set() === null ) {
522
			$this->set_value( $this->get_default_value() );
523
		}
524
		return $this->get_value_set()->get_set();
525
	}
526
527
	/**
528
	 * Return a differently formatted value for end-users
529
	 *
530
	 * @return mixed
531
	 **/
532 2
	public function get_formatted_value() {
533 2
		return $this->get_value();
534
	}
535
536
	/**
537
	 * Alias for $this->get_value_set()->set( $value );
538
	 **/
539 1
	public function set_value( $value ) {
540 1
		$this->get_value_set()->set( $value );
541 1
	}
542
543
	/**
544
	 * Get default field value
545
	 *
546
	 * @return mixed
547
	 **/
548 1
	public function get_default_value() {
549 1
		return $this->default_value;
550
	}
551
552
	/**
553
	 * Set default field value
554
	 *
555
	 * @param mixed $default_value
556
	 **/
557 1
	public function set_default_value( $default_value ) {
558 1
		$this->default_value = $default_value;
559 1
		return $this;
560
	}
561
562
	/**
563
	 * Return the field base name.
564
	 *
565
	 * @return string
566
	 **/
567
	public function get_base_name() {
568
		return $this->base_name;
569
	}
570
571
	/**
572
	 * Set field base name as defined in the container.
573
	 **/
574
	public function set_base_name( $name ) {
575
		$this->base_name = $name;
576
	}
577
578
	/**
579
	 * Return the field name
580
	 *
581
	 * @return string
582
	 **/
583 2
	public function get_name() {
584 2
		return $this->name;
585
	}
586
587
	/**
588
	 * Set field name.
589
	 * Use only if you are completely aware of what you are doing.
590
	 *
591
	 * @param string $name Field name, either sanitized or not
592
	 **/
593 6
	public function set_name( $name ) {
594 6
		if ( empty( $name ) ) {
595 1
			Incorrect_Syntax_Exception::raise( 'Field name can\'t be empty' );
596
		}
597
598 5
		$regex = '/\A[a-z0-9_\-\[\]]+\z/'; // symbols ]-[ are supported in a hidden way - required for widgets to work (WP imposes dashes and square brackets on field names)
599 5
		if ( ! preg_match( $regex, $name ) ) {
600 3
			Incorrect_Syntax_Exception::raise( 'Field name can only contain lowercase alphanumeric characters and underscores.' );
601
		}
602
603 2
		$name_prefix = $this->get_name_prefix();
604 2
		$name = ( substr( $name, 0, strlen( $name_prefix ) ) !== $name_prefix ? $name_prefix . $name : $name );
605
606 2
		$this->name = $name;
607 2
	}
608
609
	/**
610
	 * Return the field name prefix
611
	 *
612
	 * @return string
613
	 **/
614 3
	public function get_name_prefix() {
615 3
		return $this->name_prefix;
616
	}
617
618
	/**
619
	 * Set field name prefix
620
	 * Use only if you are completely aware of what you are doing.
621
	 *
622
	 * @param string $name_prefix
623
	 **/
624 3
	public function set_name_prefix( $name_prefix ) {
625 3
		$name_prefix = strval( $name_prefix );
626 3
		$old_prefix_length = strlen( $this->name_prefix );
627 3
		$this->name_prefix = '';
628 3
		$this->set_name( substr( $this->get_name(), $old_prefix_length ) );
629
630 3
		$this->name_prefix = $name_prefix;
631 3
		$this->set_name( $this->name_prefix . $this->get_name() );
632 3
	}
633
634
	/**
635
	 * Return field label.
636
	 *
637
	 * @return string
638
	 **/
639
	public function get_label() {
640
		return $this->label;
641
	}
642
643
	/**
644
	 * Set field label.
645
	 *
646
	 * @param string $label If null, the label will be generated from the field name
647
	 **/
648 View Code Duplication
	public function set_label( $label ) {
1 ignored issue
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...
649
		// Try to guess field label from it's name
650
		if ( is_null( $label ) ) {
651
			// remove the leading underscore(if it's there)
652
			$label = preg_replace( '~^_~', '', $this->name );
653
654
			// remove the leading "crb_"(if it's there)
655
			$label = preg_replace( '~^crb_~', '', $label );
656
657
			// split the name into words and make them capitalized
658
			$label = mb_convert_case( str_replace( '_', ' ', $label ), MB_CASE_TITLE );
659
		}
660
661
		$this->label = $label;
662
	}
663
664
	/**
665
	 * Return the field help text
666
	 *
667
	 * @return object $this
668
	 **/
669
	public function get_help_text() {
670
		return $this->help_text;
671
	}
672
673
	/**
674
	 * Set additional text to be displayed during field render,
675
	 * containing information and guidance for the user
676
	 *
677
	 * @return object $this
678
	 **/
679
	public function set_help_text( $help_text ) {
680
		$this->help_text = $help_text;
681
		return $this;
682
	}
683
684
	/**
685
	 * Alias for set_help_text()
686
	 *
687
	 * @see set_help_text()
688
	 * @return object $this
689
	 **/
690
	public function help_text( $help_text ) {
691
		return $this->set_help_text( $help_text );
692
	}
693
694
	/**
695
	 * Return whether or not this value should be auto loaded.
696
	 *
697
	 * @return bool
698
	 **/
699
	public function get_autoload() {
700
		return $this->autoload;
701
	}
702
703
	/**
704
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
705
	 *
706
	 * @param bool $autoload
707
	 * @return object $this
708
	 **/
709
	public function set_autoload( $autoload ) {
710
		$this->autoload = $autoload;
711
		return $this;
712
	}
713
714
	/**
715
	 * Return whether or not this field should be lazyloaded.
716
	 *
717
	 * @return bool
718
	 **/
719
	public function get_lazyload() {
720
		return $this->lazyload;
721
	}
722
723
	/**
724
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
725
	 *
726
	 * @param bool $lazyload
727
	 * @return object $this
728
	 **/
729
	public function set_lazyload( $lazyload ) {
730
		$this->lazyload = $lazyload;
731
		return $this;
732
	}
733
734
	/**
735
	 * Get the field width.
736
	 *
737
	 * @return int $width
738
	 **/
739
	public function get_width() {
740
		return $this->width;
741
	}
742
743
	/**
744
	 * Set the field width.
745
	 *
746
	 * @param int $width
747
	 * @return object $this
748
	 **/
749
	public function set_width( $width ) {
750
		$this->width = (int) $width;
751
		return $this;
752
	}
753
754
	/**
755
	 * Get custom CSS classes.
756
	 *
757
	 * @return array<string>
758
	 */
759
	public function get_classes() {
760
		return $this->classes;
761
	}
762
763
	/**
764
	 * Set CSS classes that the container should use.
765
	 *
766
	 * @param string|array $classes
767
	 * @return object $this
768
	 */
769
	public function set_classes( $classes ) {
770
		$this->classes = Helper::sanitize_classes( $classes );
0 ignored issues
show
Bug introduced by
It seems like $classes defined by parameter $classes on line 769 can also be of type array; however, Carbon_Fields\Helper\Helper::sanitize_classes() does only seem to accept string|array<integer,obj..._Fields\Helper\strnig>>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
771
		return $this;
772
	}
773
774
	/**
775
	 * Whether this field is mandatory for the user
776
	 *
777
	 * @param bool $required
778
	 * @return object $this
779
	 **/
780
	public function set_required( $required = true ) {
781
		$this->required = $required;
782
		return $this;
783
	}
784
785
	/**
786
	 * Return whether this field is mandatory for the user
787
	 *
788
	 * @return bool
789
	 **/
790
	public function is_required() {
791
		return $this->required;
792
	}
793
794
	/**
795
	 * HTML id attribute getter.
796
	 * @return string
797
	 */
798 1
	public function get_id() {
799 1
		return $this->id;
800
	}
801
802
	/**
803
	 * HTML id attribute setter
804
	 * @param string $id
805
	 */
806 1
	public function set_id( $id ) {
807 1
		$this->id = $id;
808 1
	}
809
810
	/**
811
	 * Set the field visibility conditional logic.
812
	 *
813
	 * @param array
814
	 */
815 8
	public function set_conditional_logic( $rules ) {
816 8
		$this->conditional_logic = $this->parse_conditional_rules( $rules );
817
818 3
		return $this;
819
	}
820
821
	/**
822
	 * Get the conditional logic rules
823
	 *
824
	 * @return array
825
	 */
826 3
	public function get_conditional_logic() {
827 3
		return $this->conditional_logic;
828
	}
829
830
	/**
831
	 * Validate and parse the conditional logic rules.
832
	 *
833
	 * @param array $rules
834
	 * @return array
835
	 */
836
	protected function parse_conditional_rules( $rules ) {
837
		if ( ! is_array( $rules ) ) {
838
			Incorrect_Syntax_Exception::raise( 'Conditional logic rules argument should be an array.' );
839
		}
840
841
		$allowed_operators = array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN' );
842
843
		$parsed_rules = array(
844
			'relation' => Helper::get_relation_type_from_array( $rules ),
845
			'rules' => array(),
846
		);
847
848
		foreach ( $rules as $key => $rule ) {
849
			if ( $key === 'relation' ) {
850
				continue; // Skip the relation key as it is already handled above
851
			}
852
853
			// Check if the rule is valid
854
			if ( ! is_array( $rule ) || empty( $rule['field'] ) ) {
855
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic rule format. ' .
856
				'The rule should be an array with the "field" key set.' );
857
			}
858
859
			// Check the compare operator
860
			if ( empty( $rule['compare'] ) ) {
861
				$rule['compare'] = '=';
862
			}
863 View Code Duplication
			if ( ! in_array( $rule['compare'], $allowed_operators ) ) {
864
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic compare operator: <code>' .
865
					$rule['compare'] . '</code><br>Allowed operators are: <code>' .
866
				implode( ', ', $allowed_operators ) . '</code>' );
867
			}
868
			if ( $rule['compare'] === 'IN' || $rule['compare'] === 'NOT IN' ) {
869
				if ( ! is_array( $rule['value'] ) ) {
870
					Incorrect_Syntax_Exception::raise( 'Invalid conditional logic value format. ' .
871
					'An array is expected, when using the "' . $rule['compare'] . '" operator.' );
872
				}
873
			}
874
875
			// Check the value
876
			if ( ! isset( $rule['value'] ) ) {
877
				$rule['value'] = '';
878
			}
879
880
			$parsed_rules['rules'][] = $rule;
881
		}
882
883
		return $parsed_rules;
884
	}
885
886
	/**
887
	 * Set the REST visibility of the field
888
	 * 
889
	 * @param bool $visible
890
	 */
891
	public function set_visible_in_rest_api( $visible ) {
892
		$this->visible_in_rest_api = $visible;
893
	}
894
	
895
	/**
896
	 * Get the REST visibility of the field
897
	 * 
898
	 * @return bool
899
	 */
900
	public function get_visible_in_rest_api() {
901
		return $this->visible_in_rest_api;
902
	}
903
904
	/**
905
	 * Configuration function for setting the field visibility in the response of the requests to the REST API
906
	 * 
907
	 * @param bool $visible
908
	 * @return Field $this
909
	 */
910
	public function show_in_rest( $visible = true ) {
911
		$this->set_visible_in_rest_api( $visible );
912
		return $this;
913
	}
914
915
	/**
916
	 * Returns an array that holds the field data, suitable for JSON representation.
917
	 * This data will be available in the Underscore template and the Backbone Model.
918
	 *
919
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
920
	 * @return array
921
	 */
922
	public function to_json( $load ) {
923
		if ( $load ) {
924
			$this->load();
925
		}
926
927
		$field_data = array(
928
			'id' => $this->get_id(),
929
			'type' => $this->get_type(),
930
			'label' => $this->get_label(),
931
			'name' => $this->get_name(),
932
			'base_name' => $this->get_base_name(),
933
			'value' => $this->get_value(),
934
			'default_value' => $this->get_default_value(),
935
			'help_text' => $this->get_help_text(),
936
			'context' => $this->get_context(),
937
			'required' => $this->is_required(),
938
			'lazyload' => $this->get_lazyload(),
939
			'width' => $this->get_width(),
940
			'classes' => $this->get_classes(),
941
			'conditional_logic' => $this->get_conditional_logic(),
942
		);
943
944
		return $field_data;
945
	}
946
947
	/**
948
	 * Hook administration scripts.
949
	 */
950
	public static function admin_hook_scripts() {
951
		wp_enqueue_media();
952
	}
953
954
	/**
955
	 * Hook administration styles.
956
	 */
957
	public static function admin_hook_styles() {
958
		wp_enqueue_style( 'thickbox' );
959
	}
960
}
961