Completed
Pull Request — 2.x (#4569)
by Scott Kingsley
07:09
created

Pods::input()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 10
nop 3
dl 0
loc 32
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Pods class.
5
 *
6
 * @package Pods
7
 */
8
class Pods implements Iterator {
9
10
	/**
11
	 * Whether the Pods object is in a PHP Iterator call.
12
	 *
13
	 * @var bool
14
	 */
15
	private $iterator = false;
16
17
	/**
18
	 * PodsAPI object.
19
	 *
20
	 * @var PodsAPI
21
	 */
22
	public $api;
23
24
	/**
25
	 * PodsData object.
26
	 *
27
	 * @var PodsData
28
	 */
29
	public $data;
30
31
	/**
32
	 * PodsData object for additional calls.
33
	 *
34
	 * @var PodsData
35
	 */
36
	public $alt_data;
37
38
	/**
39
	 * Array of pod item arrays.
40
	 *
41
	 * @var array
42
	 */
43
	public $rows;
44
45
	/**
46
	 * Current pod item array.
47
	 *
48
	 * @var array
49
	 */
50
	public $row;
51
52
	/**
53
	 * Row number.
54
	 *
55
	 * @var int
56
	 */
57
	private $row_number;
58
59
	/**
60
	 * Override pod item array.
61
	 *
62
	 * @var array
63
	 */
64
	public $row_override = array();
65
66
	/**
67
	 * Whether to display errors on the screen.
68
	 *
69
	 * @var bool
70
	 */
71
	public $display_errors = true;
72
73
	/**
74
	 * Current pod information.
75
	 *
76
	 * @var array|bool|mixed|null|void
77
	 */
78
	public $pod_data;
79
80
	/**
81
	 * Last used Pods::find() parameters.
82
	 *
83
	 * @var array
84
	 */
85
	public $params = array();
86
87
	/**
88
	 * Current Pod name.
89
	 *
90
	 * @var string
91
	 */
92
	public $pod;
93
94
	/**
95
	 * Current Pod ID.
96
	 *
97
	 * @var int
98
	 */
99
	public $pod_id;
100
101
	/**
102
	 * Pod fields information.
103
	 *
104
	 * @var array
105
	 */
106
	public $fields;
107
108
	/**
109
	 * Last used filters() parameters.
110
	 *
111
	 * @var array
112
	 */
113
	public $filters = array();
114
115
	/**
116
	 * Detail page URL used for Advanced Content Types.
117
	 *
118
	 * @var string
119
	 */
120
	public $detail_page;
121
122
	/**
123
	 * Current Item ID.
124
	 *
125
	 * @var int
126
	 */
127
	public $id;
128
129
	/**
130
	 * Last used limit from find() lookup.
131
	 *
132
	 * @var int
133
	 */
134
	public $limit = 15;
135
136
	/**
137
	 * Last used offset from find() lookup.
138
	 *
139
	 * @var int
140
	 */
141
	public $offset = 0;
142
143
	/**
144
	 * Query variable used for pagination number.
145
	 *
146
	 * @var string
147
	 */
148
	public $page_var = 'pg';
149
150
	/**
151
	 * Last used page from find() lookup.
152
	 *
153
	 * @var int|mixed
154
	 */
155
	public $page = 1;
156
157
	/**
158
	 * Last used state of whether pagination was enabled from find() lookup.
159
	 *
160
	 * @var bool
161
	 */
162
	public $pagination = true;
163
164
	/**
165
	 * Last used state of whether search was enabled from find() lookup.
166
	 *
167
	 * @var bool
168
	 */
169
	public $search = true;
170
171
	/**
172
	 * Query variable used for search string.
173
	 *
174
	 * @var string
175
	 */
176
	public $search_var = 'search';
177
178
	/**
179
	 * Search mode (int | text | text_like).
180
	 *
181
	 * @var string
182
	 */
183
	public $search_mode = 'int';
184
185
	/**
186
	 * Total number of records returned from find() lookup.
187
	 *
188
	 * @var int
189
	 */
190
	public $total = 0;
191
192
	/**
193
	 * Total number of records found from find() lookup.
194
	 *
195
	 * @var int
196
	 */
197
	public $total_found = 0;
198
199
	/**
200
	 * Last used ui options for ui() call.
201
	 *
202
	 * @var array
203
	 */
204
	public $ui = array();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ui. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
205
206
	/**
207
	 * Page template to use for SEO feature in Pods Pages.
208
	 *
209
	 * @var string
210
	 */
211
	public $page_template;
212
213
	/**
214
	 * Body classes to use for SEO feature in Pods Pages.
215
	 *
216
	 * @var array
217
	 */
218
	public $body_classes;
219
220
	/**
221
	 * Meta tags to use for SEO feature in Pods Pages.
222
	 *
223
	 * @var array
224
	 */
225
	public $meta = array();
226
227
	/**
228
	 * Meta properties to use for SEO feature in Pods Pages.
229
	 *
230
	 * @var array
231
	 */
232
	public $meta_properties = array();
233
234
	/**
235
	 * Meta custom HTML to use for SEO feature in Pods Pages.
236
	 *
237
	 * @var string
238
	 */
239
	public $meta_extra = '';
240
241
	/**
242
	 * Last SQL query used by a find() lookup.
243
	 *
244
	 * @var string
245
	 */
246
	public $sql;
247
248
	/**
249
	 * Pods_Deprecated object.
250
	 *
251
	 * @var Pods_Deprecated
252
	 */
253
	public $deprecated;
254
255
	/**
256
	 * Old Pod name variable.
257
	 *
258
	 * @var string
259
	 *
260
	 * @deprecated 2.0
261
	 */
262
	public $datatype;
263
264
	/**
265
	 * Old Pod ID variable.
266
	 *
267
	 * @var int
268
	 *
269
	 * @deprecated 2.0
270
	 */
271
	public $datatype_id;
272
273
	/**
274
	 * Constructor - Pods Framework core.
275
	 *
276
	 * @param string $pod The pod name.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
277
	 * @param mixed  $id  (optional) The ID or slug, to load a single record; Provide array of $params to run 'find'.
278
	 *
279
	 * @license http://www.gnu.org/licenses/gpl-2.0.html
280
	 * @since   1.0.0
281
	 * @link    https://pods.io/docs/pods/
282
	 */
283
	public function __construct( $pod = null, $id = null ) {
284
285
		if ( null === $pod ) {
286
			$queried_object = get_queried_object();
287
288
			if ( $queried_object ) {
289
				$id_lookup = true;
290
291
				if ( isset( $queried_object->post_type ) ) {
292
					// Post Type Singular.
293
					$pod = $queried_object->post_type;
294
				} elseif ( isset( $queried_object->taxonomy ) ) {
295
					// Term Archive.
296
					$pod = $queried_object->taxonomy;
297
				} elseif ( isset( $queried_object->user_login ) ) {
298
					// Author Archive.
299
					$pod = 'user';
300
				} elseif ( isset( $queried_object->public, $queried_object->name ) ) {
301
					// Post Type Archive.
302
					$pod = $queried_object->name;
303
304
					$id_lookup = false;
305
				}
306
307
				if ( null === $id && $id_lookup ) {
308
					$id = get_queried_object_id();
309
				}
310
			}//end if
311
		}//end if
312
313
		$this->api                 = pods_api( $pod );
314
		$this->api->display_errors =& $this->display_errors;
315
316
		$this->data               = pods_data( $this->api, $id, false );
317
		PodsData::$display_errors =& $this->display_errors;
318
319
		// Set up page variable.
320
		if ( pods_strict( false ) ) {
321
			$this->page       = 1;
322
			$this->pagination = false;
323
			$this->search     = false;
324
		} else {
325
			// Get the page variable.
326
			$this->page = pods_v( $this->page_var, 'get', 1, true );
327
328
			if ( ! empty( $this->page ) ) {
329
				$this->page = max( 1, pods_absint( $this->page ) );
330
			}
331
		}
332
333
		// Set default pagination handling to on/off.
334
		if ( defined( 'PODS_GLOBAL_POD_PAGINATION' ) ) {
335
			if ( ! PODS_GLOBAL_POD_PAGINATION ) {
336
				$this->page       = 1;
337
				$this->pagination = false;
338
			} else {
339
				$this->pagination = true;
340
			}
341
		}
342
343
		// Set default search to on/off.
344
		if ( defined( 'PODS_GLOBAL_POD_SEARCH' ) ) {
345
			if ( PODS_GLOBAL_POD_SEARCH ) {
346
				$this->search = true;
347
			} else {
348
				$this->search = false;
349
			}
350
		}
351
352
		// Set default search mode.
353
		$allowed_search_modes = array( 'int', 'text', 'text_like' );
354
355
		if ( defined( 'PODS_GLOBAL_POD_SEARCH_MODE' ) && in_array( PODS_GLOBAL_POD_SEARCH_MODE, $allowed_search_modes, true ) ) {
356
			$this->search_mode = PODS_GLOBAL_POD_SEARCH_MODE;
357
		}
358
359
		// Sync Settings.
360
		$this->data->page        =& $this->page;
361
		$this->data->limit       =& $this->limit;
362
		$this->data->pagination  =& $this->pagination;
363
		$this->data->search      =& $this->search;
364
		$this->data->search_mode =& $this->search_mode;
365
366
		// Sync Pod Data.
367
		$this->api->pod_data =& $this->data->pod_data;
368
		$this->pod_data      =& $this->api->pod_data;
369
		$this->api->pod_id   =& $this->data->pod_id;
0 ignored issues
show
Bug introduced by
The property pod_id does not seem to exist in PodsData.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
370
		$this->pod_id        =& $this->api->pod_id;
371
		$this->datatype_id   =& $this->pod_id;
0 ignored issues
show
Deprecated Code introduced by
The property Pods::$datatype_id has been deprecated with message: 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
372
		$this->api->pod      =& $this->data->pod;
373
		$this->pod           =& $this->api->pod;
374
		$this->datatype      =& $this->pod;
0 ignored issues
show
Deprecated Code introduced by
The property Pods::$datatype has been deprecated with message: 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
375
		$this->api->fields   =& $this->data->fields;
376
		$this->fields        =& $this->api->fields;
377
		$this->detail_page   =& $this->data->detail_page;
378
		$this->id            =& $this->data->id;
379
		$this->row           =& $this->data->row;
380
		$this->rows          =& $this->data->data;
381
		$this->row_number    =& $this->data->row_number;
382
		$this->sql           =& $this->data->sql;
383
384
		if ( is_array( $id ) || is_object( $id ) ) {
385
			$this->find( $id );
386
		}
387
	}
388
389
	/**
390
	 * Whether this Pod object is valid or not
391
	 *
392
	 * @return bool
393
	 *
394
	 * @since 2.0
395
	 */
396
	public function valid() {
397
398
		if ( empty( $this->pod_id ) ) {
399
			return false;
400
		}
401
402
		if ( $this->iterator ) {
403
			return isset( $this->rows[ $this->row_number ] );
404
		}
405
406
		return true;
407
	}
408
409
	/**
410
	 * Check if in Iterator mode
411
	 *
412
	 * @return bool
413
	 *
414
	 * @since 2.3.4
415
	 *
416
	 * @link  http://www.php.net/manual/en/class.iterator.php
417
	 */
418
	public function is_iterator() {
419
420
		return $this->iterator;
421
	}
422
423
	/**
424
	 * Turn off Iterator mode to off
425
	 *
426
	 * @return void
427
	 *
428
	 * @since 2.3.4
429
	 *
430
	 * @link  http://www.php.net/manual/en/class.iterator.php
431
	 */
432
	public function stop_iterator() {
433
434
		$this->iterator = false;
435
436
	}
437
438
	/**
439
	 * Rewind Iterator
440
	 *
441
	 * @since 2.3.4
442
	 *
443
	 * @link  http://www.php.net/manual/en/class.iterator.php
444
	 */
445
	public function rewind() {
446
447
		if ( ! $this->iterator ) {
448
			$this->iterator = true;
449
450
			$this->row_number = 0;
451
		}
452
	}
453
454
	/**
455
	 * Get current Iterator row
456
	 *
457
	 * @return mixed|boolean
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Pods|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
458
	 *
459
	 * @since 2.3.4
460
	 *
461
	 * @link  http://www.php.net/manual/en/class.iterator.php
462
	 */
463
	public function current() {
464
465
		if ( $this->iterator && $this->fetch() ) {
466
			return $this;
467
		}
468
469
		return false;
470
	}
471
472
	/**
473
	 * Get current Iterator key
474
	 *
475
	 * @return int
476
	 *
477
	 * @since 2.3.4
478
	 *
479
	 * @link  http://www.php.net/manual/en/class.iterator.php
480
	 */
481
	public function key() {
482
483
		return $this->row_number;
484
	}
485
486
	/**
487
	 * Move onto the next Iterator row
488
	 *
489
	 * @return void
490
	 *
491
	 * @since 2.3.4
492
	 *
493
	 * @link  http://www.php.net/manual/en/class.iterator.php
494
	 */
495
	public function next() {
496
497
		$this->row_number ++;
498
	}
499
500
	/**
501
	 * Whether a Pod item exists or not when using fetch() or construct with an ID or slug
502
	 *
503
	 * @return bool
504
	 *
505
	 * @since 2.0
506
	 */
507
	public function exists() {
508
509
		if ( empty( $this->row ) ) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !empty($this->row);.
Loading history...
510
			return false;
511
		}
512
513
		return true;
514
	}
515
516
	/**
517
	 * Return an array of all rows returned from a find() call.
518
	 *
519
	 * Most of the time, you will want to loop through data using fetch()
520
	 * instead of using this function.
521
	 *
522
	 * @return array|bool An array of all rows returned from a find() call, or false if no items returned
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
523
	 *
524
	 * @since 2.0
525
	 * @link  https://pods.io/docs/data/
526
	 */
527
	public function data() {
528
529
		do_action( 'pods_pods_data', $this );
530
531
		if ( empty( $this->rows ) ) {
532
			return false;
533
		}
534
535
		return (array) $this->rows;
536
	}
537
538
	/**
539
	 * Return a field input for a specific field
540
	 *
541
	 * @param string|array      $field      Field name or Field data array.
542
	 * @param string|array|null $input_name Input field name to use (overrides default name).
543
	 * @param mixed             $value      Current value to use.
544
	 *
545
	 * @return string Field Input HTML
546
	 *
547
	 * @since 2.3.10
548
	 */
549
	public function input( $field, $input_name = null, $value = '__null' ) {
550
551
		if ( is_array( $field ) ) {
552
			// Field data override.
553
			$field_data = $field;
554
555
			$field = pods_v( 'name', $field );
556
		} else {
557
			// Get field data from field name.
558
			$field_data = $this->fields( $field );
559
		}
560
561
		if ( ! empty( $field_data ) ) {
562
			$field_type = pods_v( 'type', $field_data );
563
564
			if ( empty( $input_name ) ) {
565
				$input_name = $field;
566
			}
567
568
			if ( '__null' === $value ) {
569
				$value = $this->field( array(
570
					'name'    => $field,
571
					'in_form' => true,
572
				) );
573
			}
574
575
			return PodsForm::field( $input_name, $value, $field_type, $field_data, $this, $this->id() );
576
		}
577
578
		return '';
579
580
	}
581
582
	/**
583
	 * Return field array from a Pod, a field's data, or a field option
584
	 *
585
	 * @param null $field  Field name.
586
	 * @param null $option Option name.
587
	 *
588
	 * @return bool|mixed
589
	 *
590
	 * @since 2.0
591
	 */
592
	public function fields( $field = null, $option = null ) {
593
594
		$field_data = null;
595
596
		if ( empty( $this->fields ) ) {
597
			// No fields found.
598
			$field_data = array();
599
		} elseif ( empty( $field ) ) {
600
			// Return all fields.
601
			$field_data = (array) $this->fields;
602
		} elseif ( ! isset( $this->fields[ $field ] ) && ! isset( $this->pod_data['object_fields'][ $field ] ) ) {
603
			// Field not found.
604
			$field_data = array();
605
		} elseif ( empty( $option ) ) {
606
			// Return all field data.
607
			if ( isset( $this->fields[ $field ] ) ) {
608
				$field_data = $this->fields[ $field ];
609
			} elseif ( isset( $this->pod_data['object_fields'] ) ) {
610
				$field_data = $this->pod_data['object_fields'][ $field ];
611
			}
612
		} else {
613
			$options = array();
614
615
			// Merge options.
616
			if ( isset( $this->fields[ $field ] ) ) {
617
				$options = array_merge( $this->fields[ $field ], $this->fields[ $field ]['options'] );
618
			} elseif ( isset( $this->pod_data['object_fields'] ) ) {
619
				$options = array_merge( $this->pod_data['object_fields'][ $field ], $this->pod_data['object_fields'][ $field ]['options'] );
620
			}
621
622
			if ( 'data' === $option && in_array( pods_v( 'type', $options ), PodsForm::tableless_field_types(), true ) ) {
623
				// Get a list of available items from a relationship field.
624
				$field_data = PodsForm::field_method( 'pick', 'get_field_data', $options );
625
			} elseif ( isset( $options[ $option ] ) ) {
626
				// Return option.
627
				$field_data = $options[ $option ];
628
			}
629
		}//end if
630
631
		/**
632
		 * Modify the field data before returning
633
		 *
634
		 * @since unknown
635
		 *
636
		 * @param array       $field_data The data for the field.
637
		 * @param string|null $field      The specific field that data is being return for, if set when method is called or null.
638
		 * @param string|null $option     Value of option param when method was called. Can be used to get a list of available items from a relationship field.
639
		 * @param Pods|object $this       The current Pods class instance.
640
		 */
641
		return apply_filters( 'pods_pods_fields', $field_data, $field, $option, $this );
642
643
	}
644
645
	/**
646
	 * Return row array for an item
647
	 *
648
	 * @return array|false
649
	 *
650
	 * @since 2.0
651
	 */
652
	public function row() {
653
654
		do_action( 'pods_pods_row', $this );
655
656
		if ( ! is_array( $this->row ) ) {
657
			return false;
658
		}
659
660
		return (array) $this->row;
661
	}
662
663
	/**
664
	 * Return the output for a field. If you want the raw value for use in PHP for custom manipulation,
665
	 * you will want to use field() instead. This function will automatically convert arrays into a
666
	 * list of text such as "Rick, John, and Gary"
667
	 *
668
	 * @param string|array|object  $name   The field name, or an associative array of parameters.
669
	 * @param boolean|array|object $single (optional) For tableless fields, to return an array or the first.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $single not be boolean|array|object|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
670
	 *
671
	 * @return string|null|false The output from the field, null if the field doesn't exist, false if no value returned
0 ignored issues
show
Documentation introduced by
Should the return type not be object|integer|double|string|null|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
672
	 *                           for tableless fields
673
	 * @since 2.0
674
	 * @link  https://pods.io/docs/display/
675
	 */
676
	public function display( $name, $single = null ) {
677
678
		$defaults = array(
679
			'name'          => $name,
680
			'single'        => $single,
681
			'display'       => true,
682
			'serial_params' => null,
683
		);
684
685
		if ( is_array( $name ) || is_object( $name ) ) {
686
			$defaults['name'] = null;
687
688
			$params = (object) array_merge( $defaults, (array) $name );
689
		} elseif ( is_array( $single ) || is_object( $single ) ) {
690
			$defaults['single'] = null;
691
692
			$params = (object) array_merge( $defaults, (array) $single );
693
		} else {
694
			$params = $defaults;
695
		}
696
697
		$params = (object) $params;
698
699
		$value = $this->field( $params );
700
701
		if ( is_array( $value ) ) {
702
			$fields = $this->fields;
703
704
			if ( isset( $this->pod_data['object_fields'] ) ) {
705
				$fields = array_merge( $fields, $this->pod_data['object_fields'] );
706
			}
707
708
			$serial_params = array(
709
				'field'  => $params->name,
710
				'fields' => $fields,
711
			);
712
713
			if ( ! empty( $params->serial_params ) && is_array( $params->serial_params ) ) {
714
				$serial_params = array_merge( $serial_params, $params->serial_params );
715
			}
716
717
			$value = pods_serial_comma( $value, $serial_params );
718
		}
719
720
		return $value;
721
	}
722
723
	/**
724
	 * Return the raw output for a field If you want the raw value for use in PHP for custom manipulation,
725
	 * you will want to use field() instead. This function will automatically convert arrays into a
726
	 * list of text such as "Rick, John, and Gary"
727
	 *
728
	 * @param string|array|object  $name   The field name, or an associative array of parameters.
729
	 * @param boolean|array|object $single (optional) For tableless fields, to return an array or the first.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $single not be boolean|array|object|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
730
	 *
731
	 * @return string|null|false The output from the field, null if the field doesn't exist, false if no value returned
732
	 *                           for tableless fields
733
	 * @since 2.0
734
	 * @link  https://pods.io/docs/display/
735
	 */
736
	public function raw( $name, $single = null ) {
737
738
		$defaults = array(
739
			'name'   => $name,
740
			'single' => $single,
741
			'raw'    => true,
742
		);
743
744
		if ( is_array( $name ) || is_object( $name ) ) {
745
			$defaults['name'] = null;
746
747
			$params = (object) array_merge( $defaults, (array) $name );
748
		} elseif ( is_array( $single ) || is_object( $single ) ) {
749
			$defaults['single'] = null;
750
751
			$params = (object) array_merge( $defaults, (array) $single );
752
		} else {
753
			$params = (object) $defaults;
754
		}
755
756
		return $this->field( $params );
757
	}
758
759
	/**
760
	 * Return the value for a field.
761
	 *
762
	 * If you are getting a field for output in a theme, most of the time you will want to use display() instead.
763
	 *
764
	 * This function will return arrays for relationship and file fields.
765
	 *
766
	 * @param string|array|object  $name   The field name, or an associative array of parameters.
767
	 * @param boolean|array|object $single For tableless fields, to return the whole array or the just the first item,
0 ignored issues
show
Documentation introduced by
Should the type for parameter $single not be boolean|array|object|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
768
	 *                                     or an associative array of parameters.
769
	 * @param boolean|array|object $raw    Whether to return the raw value, or to run through the field type's display
770
	 *                                     method, or an associative array of parameters.
771
	 *
772
	 * @return mixed|null Value returned depends on the field type, null if the field doesn't exist, false if no value
773
	 *                    returned for tableless fields.
774
	 * @since 2.0
775
	 * @link  https://pods.io/docs/field/
776
	 */
777
	public function field( $name, $single = null, $raw = false ) {
778
779
		$defaults = array(
780
			'name'        => $name,
781
			'orderby'     => null,
782
			'single'      => $single,
783
			'params'      => null,
784
			'in_form'     => false,
785
			'raw'         => $raw,
786
			'raw_display' => false,
787
			'display'     => false,
788
			'get_meta'    => false,
789
			'output'      => null,
790
			'deprecated'  => false,
791
			// extra data to send to field handlers.
792
			'args'        => array(),
793
		);
794
795
		$is_name_object   = is_object( $name );
796
		$is_single_object = is_object( $single );
797
		$is_raw_object    = is_object( $raw );
798
799
		if ( is_array( $name ) || $is_name_object ) {
800
			if ( $is_name_object ) {
801
				$name = get_object_vars( $name );
802
			}
803
804
			$defaults['name'] = null;
805
806
			$params = (object) array_merge( $defaults, (array) $name );
807
		} elseif ( is_array( $single ) || $is_single_object ) {
808
			if ( $is_single_object ) {
809
				$single = get_object_vars( $single );
810
			}
811
812
			$defaults['single'] = null;
813
814
			$params = (object) array_merge( $defaults, (array) $single );
815
		} elseif ( is_array( $raw ) || $is_raw_object ) {
816
			if ( $is_raw_object ) {
817
				$raw = get_object_vars( $raw );
818
			}
819
820
			$defaults['raw'] = false;
821
822
			$params = (object) array_merge( $defaults, (array) $raw );
823
		} else {
824
			$params = (object) $defaults;
825
		}//end if
826
827
		if ( $params->in_form ) {
828
			$params->output = 'ids';
829
		} elseif ( null === $params->output ) {
830
			/**
831
			 * Override the way related fields are output
832
			 *
833
			 * @param string       $output How to output related fields. Default is 'arrays'. Options: ids|names|objects|arrays|pods|find
834
			 * @param array|object $row    Current row being outputted.
835
			 * @param array        $params Params array passed to field().
836
			 * @param Pods         $this   Current Pods object.
837
			 */
838
			$params->output = apply_filters( 'pods_pods_field_related_output_type', 'arrays', $this->row, $params, $this );
839
		}
840
841
		if ( in_array( $params->output, array( 'id', 'name', 'object', 'array', 'pod' ), true ) ) {
842
			$params->output .= 's';
843
		}
844
845
		// Support old $orderby variable.
846
		if ( null !== $params->single && is_string( $params->single ) && empty( $params->orderby ) ) {
847
			if ( ! class_exists( 'Pod' ) || Pod::$deprecated_notice ) {
848
				pods_deprecated( 'Pods::field', '2.0', 'Use $params[ \'orderby\' ] instead' );
849
			}
850
851
			$params->orderby = $params->single;
852
			$params->single  = false;
853
		}
854
855
		if ( null !== $params->single ) {
856
			$params->single = (boolean) $params->single;
857
		}
858
859
		$params->name = trim( $params->name );
860
		if ( is_array( $params->name ) || '' === $params->name ) {
861
			return null;
862
		}
863
864
		$params->full_name = $params->name;
865
866
		$value = null;
867
868
		if ( isset( $this->row_override[ $params->name ] ) ) {
869
			$value = $this->row_override[ $params->name ];
870
		}
871
872
		if ( false === $this->row() ) {
873
			if ( false !== $this->data() ) {
874
				$this->fetch();
875
			} else {
876
				return $value;
877
			}
878
		}
879
880
		if ( $this->data->field_id === $params->name ) {
881
			if ( isset( $this->row[ $params->name ] ) ) {
882
				return $this->row[ $params->name ];
883
				// @codingStandardsIgnoreLine.
884
			} elseif ( null !== $value ) {
885
				return $value;
886
			}
887
888
			return 0;
889
		}
890
891
		$tableless_field_types    = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
892
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
893
894
		$params->traverse = array();
895
896
		if ( in_array( $params->name, array(
897
			'_link',
898
			'detail_url',
899
		), true ) || ( in_array( $params->name, array(
900
			'permalink',
901
			'the_permalink',
902
		), true ) && in_array( $this->pod_data['type'], array(
903
			'post_type',
904
			'taxonomy',
905
			'media',
906
			'user',
907
			'comment',
908
		), true ) ) ) {
909
			if ( 0 < strlen( $this->detail_page ) ) {
910
				$value = get_home_url() . '/' . $this->do_magic_tags( $this->detail_page );
911
			} elseif ( in_array( $this->pod_data['type'], array( 'post_type', 'media' ), true ) ) {
912
				$value = get_permalink( $this->id() );
913
			} elseif ( 'taxonomy' === $this->pod_data['type'] ) {
914
				$value = get_term_link( $this->id(), $this->pod_data['name'] );
915
			} elseif ( 'user' === $this->pod_data['type'] ) {
916
				$value = get_author_posts_url( $this->id() );
917
			} elseif ( 'comment' === $this->pod_data['type'] ) {
918
				$value = get_comment_link( $this->id() );
919
			}
920
		}
921
922
		$field_data      = false;
923
		$last_field_data = false;
924
		$field_type      = false;
925
926
		$first_field = explode( '.', $params->name );
927
		$first_field = $first_field[0];
928
929
		if ( isset( $this->fields[ $first_field ] ) ) {
930
			$field_data = $this->fields[ $first_field ];
931
			$field_type = 'field';
932
		} elseif ( ! empty( $this->pod_data['object_fields'] ) ) {
933
			if ( isset( $this->pod_data['object_fields'][ $first_field ] ) ) {
934
				$field_data = $this->pod_data['object_fields'][ $first_field ];
935
				$field_type = 'object_field';
936
			} else {
937
				$object_fields = (array) $this->pod_data['object_fields'];
938
939
				foreach ( $object_fields as $object_field => $object_field_opt ) {
940
					if ( in_array( $first_field, $object_field_opt['alias'], true ) ) {
941
						if ( $first_field === $params->name ) {
942
							$params->name = $object_field;
943
						}
944
945
						$first_field = $object_field;
946
						$field_data  = $object_field_opt;
947
						$field_type  = 'object_field';
948
949
						break;
950
					}
951
				}
952
			}
953
		}//end if
954
955
		// Simple fields have no other output options.
956
		if ( 'pick' === $field_data['type'] && in_array( $field_data['pick_object'], $simple_tableless_objects, true ) ) {
957
			$params->output = 'arrays';
958
		}
959
960
		if ( empty( $value ) && in_array( $field_data['type'], $tableless_field_types, true ) ) {
961
			$params->raw = true;
962
963
			$value = false;
964
965
			$row_key = sprintf( '_%s_%s', $params->output, $params->name );
966
967
			if ( 'arrays' !== $params->output && isset( $this->row[ $row_key ] ) ) {
968
				$value = $this->row[ '_' . $params->output . '_' . $params->name ];
969
			} elseif ( 'arrays' === $params->output && isset( $this->row[ $params->name ] ) ) {
970
				$value = $this->row[ $params->name ];
971
			}
972
973
			if ( false !== $value && ! is_array( $value ) && 'pick' === $field_data['type'] && in_array( $field_data['pick_object'], $simple_tableless_objects, true ) ) {
974
				$value = PodsForm::field_method( 'pick', 'simple_value', $params->name, $value, $field_data, $this->pod_data, $this->id(), true );
975
			}
976
		}
977
978
		if ( empty( $value ) && isset( $this->row[ $params->name ] ) && ( ! in_array( $field_data['type'], $tableless_field_types, true ) || 'arrays' === $params->output ) ) {
979
			if ( empty( $field_data ) || in_array( $field_data['type'], array(
980
				'boolean',
981
				'number',
982
				'currency',
983
			), true ) ) {
984
				$params->raw = true;
985
			}
986
987
			if ( null === $params->single ) {
988
				if ( isset( $this->fields[ $params->name ] ) && ! in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true ) ) {
989
					$params->single = true;
990
				} else {
991
					$params->single = false;
992
				}
993
			}
994
995
			$value = $this->row[ $params->name ];
996
		} elseif ( empty( $value ) ) {
997
			$object_field_found = false;
998
999
			if ( 'object_field' === $field_type ) {
1000
				$object_field_found = true;
1001
1002
				if ( isset( $this->row[ $first_field ] ) ) {
1003
					$value = $this->row[ $first_field ];
1004
				} elseif ( in_array( $field_data['type'], $tableless_field_types, true ) ) {
1005
					$this->fields[ $first_field ] = $field_data;
1006
1007
					$object_field_found = false;
1008
				} else {
1009
					return null;
1010
				}
1011
			}
1012
1013
			if ( 'post_type' === $this->pod_data['type'] && ! isset( $this->fields[ $params->name ] ) ) {
1014
				if ( ! isset( $this->fields['post_thumbnail'] ) && ( 'post_thumbnail' === $params->name || 0 === strpos( $params->name, 'post_thumbnail.' ) ) ) {
1015
					$size = 'thumbnail';
1016
1017
					if ( 0 === strpos( $params->name, 'post_thumbnail.' ) ) {
1018
						$field_names = explode( '.', $params->name );
1019
1020
						if ( isset( $field_names[1] ) ) {
1021
							$size = $field_names[1];
1022
						}
1023
					}
1024
1025
					// Pods will auto-get the thumbnail ID if this isn't an attachment.
1026
					$value = pods_image( $this->id(), $size, 0, null, true );
1027
1028
					$object_field_found = true;
1029
				} elseif ( ! isset( $this->fields['post_thumbnail_url'] ) && ( 'post_thumbnail_url' === $params->name || 0 === strpos( $params->name, 'post_thumbnail_url.' ) ) ) {
1030
					$size = 'thumbnail';
1031
1032
					if ( 0 === strpos( $params->name, 'post_thumbnail_url.' ) ) {
1033
						$field_names = explode( '.', $params->name );
1034
1035
						if ( isset( $field_names[1] ) ) {
1036
							$size = $field_names[1];
1037
						}
1038
					}
1039
1040
					// Pods will auto-get the thumbnail ID if this isn't an attachment.
1041
					$value = pods_image_url( $this->id(), $size, 0, true );
1042
1043
					$object_field_found = true;
1044
				} elseif ( 0 === strpos( $params->name, 'image_attachment.' ) ) {
1045
					$size = 'thumbnail';
1046
1047
					$image_id = 0;
1048
1049
					$field_names = explode( '.', $params->name );
1050
1051
					if ( isset( $field_names[1] ) ) {
1052
						$image_id = $field_names[1];
1053
					}
1054
1055
					if ( isset( $field_names[2] ) ) {
1056
						$size = $field_names[2];
1057
					}
1058
1059
					if ( ! empty( $image_id ) ) {
1060
						$value = pods_image( $image_id, $size, 0, null, true );
1061
1062
						if ( ! empty( $value ) ) {
1063
							$object_field_found = true;
1064
						}
1065
					}
1066
				} elseif ( 0 === strpos( $params->name, 'image_attachment_url.' ) ) {
1067
					$size = 'thumbnail';
1068
1069
					$image_id = 0;
1070
1071
					$field_names = explode( '.', $params->name );
1072
1073
					if ( isset( $field_names[1] ) ) {
1074
						$image_id = $field_names[1];
1075
					}
1076
1077
					if ( isset( $field_names[2] ) ) {
1078
						$size = $field_names[2];
1079
					}
1080
1081
					if ( ! empty( $image_id ) ) {
1082
						$value = pods_image_url( $image_id, $size, 0, true );
1083
1084
						if ( ! empty( $value ) ) {
1085
							$object_field_found = true;
1086
						}
1087
					}
1088
				}//end if
1089
			} elseif ( 'user' === $this->pod_data['type'] && ! isset( $this->fields[ $params->name ] ) ) {
1090
				if ( ! isset( $this->fields['avatar'] ) && ( 'avatar' === $params->name || 0 === strpos( $params->name, 'avatar.' ) ) ) {
1091
					$size = null;
1092
1093
					if ( 0 === strpos( $params->name, 'avatar.' ) ) {
1094
						$field_names = explode( '.', $params->name );
1095
1096
						if ( isset( $field_names[1] ) ) {
1097
							$size = (int) $field_names[1];
1098
						}
1099
					}
1100
1101
					if ( 0 < $size ) {
1102
						$value = get_avatar( $this->id(), $size );
1103
					} else {
1104
						$value = get_avatar( $this->id() );
1105
					}
1106
1107
					$object_field_found = true;
1108
				}
1109
			} elseif ( 0 === strpos( $params->name, 'image_attachment.' ) ) {
1110
				$size = 'thumbnail';
1111
1112
				$image_id = 0;
1113
1114
				$field_names = explode( '.', $params->name );
1115
1116
				if ( isset( $field_names[1] ) ) {
1117
					$image_id = $field_names[1];
1118
				}
1119
1120
				if ( isset( $field_names[2] ) ) {
1121
					$size = $field_names[2];
1122
				}
1123
1124
				if ( ! empty( $image_id ) ) {
1125
					$value = pods_image( $image_id, $size, 0, null, true );
1126
1127
					if ( ! empty( $value ) ) {
1128
						$object_field_found = true;
1129
					}
1130
				}
1131
			} elseif ( 0 === strpos( $params->name, 'image_attachment_url.' ) ) {
1132
				$size = 'thumbnail';
1133
1134
				$image_id = 0;
1135
1136
				$field_names = explode( '.', $params->name );
1137
1138
				if ( isset( $field_names[1] ) ) {
1139
					$image_id = $field_names[1];
1140
				}
1141
1142
				if ( isset( $field_names[2] ) ) {
1143
					$size = $field_names[2];
1144
				}
1145
1146
				if ( ! empty( $image_id ) ) {
1147
					$value = pods_image_url( $image_id, $size, 0, true );
1148
1149
					if ( ! empty( $value ) ) {
1150
						$object_field_found = true;
1151
					}
1152
				}
1153
			}//end if
1154
1155
			if ( false === $object_field_found ) {
1156
				$params->traverse = array( $params->name );
1157
1158
				if ( false !== strpos( $params->name, '.' ) ) {
1159
					$params->traverse = explode( '.', $params->name );
1160
1161
					$params->name = $params->traverse[0];
1162
				}
1163
1164
				if ( isset( $this->fields[ $params->name ], $this->fields[ $params->name ]['type'] ) ) {
1165
					/**
1166
					 * Modify value returned by field() after its retrieved, but before its validated or formatted
1167
					 *
1168
					 * Filter name is set dynamically with name of field: "pods_pods_field_{field_name}"
1169
					 *
1170
					 * @since unknown
1171
					 *
1172
					 * @param array|string|null $value  Value retrieved.
1173
					 * @param array|object      $row    Current row being outputted.
1174
					 * @param array             $params Params array passed to field().
1175
					 * @param object|Pods       $this   Current Pods object.
1176
					 */
1177
					$v = apply_filters( 'pods_pods_field_' . $this->fields[ $params->name ]['type'], null, $this->fields[ $params->name ], $this->row, $params, $this );
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $v. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1178
1179
					if ( null !== $v ) {
1180
						return $v;
1181
					}
1182
				}
1183
1184
				$simple = false;
1185
1186
				if ( isset( $this->fields[ $params->name ] ) ) {
1187
					if ( 'meta' === $this->pod_data['storage'] && ! in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true ) ) {
1188
						$simple = true;
1189
					}
1190
1191
					if ( in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true ) ) {
1192
						$params->raw = true;
1193
1194
						if ( 'pick' === $this->fields[ $params->name ]['type'] && in_array( $this->fields[ $params->name ]['pick_object'], $simple_tableless_objects, true ) ) {
1195
							$simple         = true;
1196
							$params->single = true;
1197
						}
1198
					} elseif ( in_array( $this->fields[ $params->name ]['type'], array(
1199
						'boolean',
1200
						'number',
1201
						'currency',
1202
					), true ) ) {
1203
						$params->raw = true;
1204
					}
1205
				}
1206
1207
				$is_field_set       = isset( $this->fields[ $params->name ] );
1208
				$is_tableless_field = false;
1209
1210
				if ( $is_field_set ) {
1211
					$is_tableless_field = in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true );
1212
				}
1213
1214
				if ( $simple || ! $is_field_set || ! $is_tableless_field ) {
1215
					if ( null === $params->single ) {
1216
						if ( $is_field_set && $is_tableless_field ) {
1217
							$params->single = true;
1218
						} else {
1219
							$params->single = false;
1220
						}
1221
					}
1222
1223
					$no_conflict = pods_no_conflict_check( $this->pod_data['type'] );
1224
1225
					if ( ! $no_conflict ) {
1226
						pods_no_conflict_on( $this->pod_data['type'] );
1227
					}
1228
1229
					if ( in_array( $this->pod_data['type'], array(
1230
						'post_type',
1231
						'media',
1232
						'taxonomy',
1233
						'user',
1234
						'comment',
1235
					), true ) ) {
1236
						$id = $this->id();
1237
1238
						$metadata_type = $this->pod_data['type'];
1239
1240
						if ( in_array( $this->pod_data['type'], array( 'post_type', 'media' ), true ) ) {
1241
							$metadata_type = 'post';
1242
1243
							// Support for WPML 'duplicated' translation handling.
1244
							if ( did_action( 'wpml_loaded' ) && apply_filters( 'wpml_is_translated_post_type', false, $this->pod_data['name'] ) ) {
1245
								$master_post_id = (int) apply_filters( 'wpml_master_post_from_duplicate', $id );
1246
1247
								if ( 0 < $master_post_id ) {
1248
									$id = $master_post_id;
1249
								}
1250
							}
1251
						} elseif ( 'taxonomy' === $this->pod_data['type'] ) {
1252
							$metadata_type = 'term';
1253
						}
1254
1255
						$value = get_metadata( $metadata_type, $id, $params->name, $params->single );
1256
1257
						$single_multi = 'single';
1258
1259
						if ( isset( $this->fields[ $params->name ] ) ) {
1260
							$single_multi = pods_v( $this->fields[ $params->name ]['type'] . '_format_type', $this->fields[ $params->name ]['options'], 'single' );
1261
						}
1262
1263
						if ( $simple && ! is_array( $value ) && 'single' !== $single_multi ) {
1264
							$value = get_metadata( $metadata_type, $id, $params->name );
1265
						}
1266
					} elseif ( 'settings' === $this->pod_data['type'] ) {
1267
						$value = get_option( $this->pod_data['name'] . '_' . $params->name, null );
1268
					}//end if
1269
1270
					// Handle Simple Relationships.
1271
					if ( $simple ) {
1272
						if ( null === $params->single ) {
1273
							$params->single = false;
1274
						}
1275
1276
						$value = PodsForm::field_method( 'pick', 'simple_value', $params->name, $value, $this->fields[ $params->name ], $this->pod_data, $this->id(), true );
1277
					}
1278
1279
					if ( ! $no_conflict ) {
1280
						pods_no_conflict_off( $this->pod_data['type'] );
1281
					}
1282
				} else {
1283
					// Dot-traversal.
1284
					$pod        = $this->pod;
1285
					$ids        = array( $this->id() );
1286
					$all_fields = array();
1287
1288
					$lookup = $params->traverse;
1289
1290
					// Get fields matching traversal names.
1291
					if ( ! empty( $lookup ) ) {
1292
						$fields = $this->api->load_fields( array(
1293
							'name'          => $lookup,
1294
							'type'          => $tableless_field_types,
1295
							'object_fields' => true,
1296
							// @todo support object fields too.
1297
						) );
1298
1299
						if ( ! empty( $fields ) ) {
1300
							foreach ( $fields as $field ) {
1301
								if ( ! empty( $field ) ) {
1302
									if ( ! isset( $all_fields[ $field['pod'] ] ) ) {
1303
										$all_fields[ $field['pod'] ] = array();
1304
									}
1305
1306
									$all_fields[ $field['pod'] ][ $field['name'] ] = $field;
1307
								}
1308
							}
1309
						}
1310
1311
						if ( ! empty( $this->pod_data['object_fields'] ) ) {
1312
							$object_fields = (array) $this->pod_data['object_fields'];
1313
1314
							foreach ( $object_fields as $object_field => $object_field_opt ) {
1315
								if ( in_array( $object_field_opt['type'], $tableless_field_types, true ) ) {
1316
									$all_fields[ $this->pod ][ $object_field ] = $object_field_opt;
1317
								}
1318
							}
1319
						}
1320
					}//end if
1321
1322
					$last_type     = '';
1323
					$last_object   = '';
1324
					$last_pick_val = '';
1325
1326
					$single_multi = pods_v( $this->fields[ $params->name ]['type'] . '_format_type', $this->fields[ $params->name ]['options'], 'single' );
1327
1328
					if ( 'multi' === $single_multi ) {
1329
						$limit = (int) pods_v( $this->fields[ $params->name ]['type'] . '_limit', $this->fields[ $params->name ]['options'], 0 );
1330
					} else {
1331
						$limit = 1;
1332
					}
1333
1334
					// Loop through each traversal level.
1335
					foreach ( $params->traverse as $key => $field ) {
1336
						$last_loop = false;
1337
1338
						if ( count( $params->traverse ) <= ( $key + 1 ) ) {
1339
							$last_loop = true;
1340
						}
1341
1342
						$field_exists = isset( $all_fields[ $pod ][ $field ] );
1343
1344
						$simple       = false;
1345
						$last_options = array();
1346
1347
						if ( $field_exists && 'pick' === $all_fields[ $pod ][ $field ]['type'] && in_array( $all_fields[ $pod ][ $field ]['pick_object'], $simple_tableless_objects, true ) ) {
1348
							$simple       = true;
1349
							$last_options = $all_fields[ $pod ][ $field ];
1350
						}
1351
1352
						// Tableless handler.
1353
						if ( $field_exists && ( ! $simple || ! in_array( $all_fields[ $pod ][ $field ]['type'], array(
1354
							'pick',
1355
							'taxonomy',
1356
							'comment',
1357
						), true ) ) ) {
1358
							$type        = $all_fields[ $pod ][ $field ]['type'];
1359
							$pick_object = $all_fields[ $pod ][ $field ]['pick_object'];
1360
							$pick_val    = $all_fields[ $pod ][ $field ]['pick_val'];
1361
1362
							if ( 'table' === $pick_object ) {
1363
								$pick_val = pods_v( 'pick_table', $all_fields[ $pod ][ $field ]['options'], $pick_val, true );
1364
							} elseif ( '__current__' === $pick_val ) {
1365
								$pick_val = $pod;
1366
							}
1367
1368
							$last_limit = 0;
1369
1370
							if ( in_array( $type, $tableless_field_types, true ) ) {
1371
								$single_multi = pods_v( "{$type}_format_type", $all_fields[ $pod ][ $field ]['options'], 'single' );
1372
1373
								if ( 'multi' === $single_multi ) {
1374
									$last_limit = (int) pods_v( "{$type}_limit", $all_fields[ $pod ][ $field ]['options'], 0 );
1375
								} else {
1376
									$last_limit = 1;
1377
								}
1378
							}
1379
1380
							$last_type     = $type;
1381
							$last_object   = $pick_object;
1382
							$last_pick_val = $pick_val;
1383
							$last_options  = $all_fields[ $pod ][ $field ];
1384
1385
							// Temporary hack until there's some better handling here.
1386
							$last_limit *= count( $ids );
1387
1388
							// Get related IDs.
1389
							if ( ! isset( $all_fields[ $pod ][ $field ]['pod_id'] ) ) {
1390
								$all_fields[ $pod ][ $field ]['pod_id'] = 0;
1391
							}
1392
1393
							if ( isset( $all_fields[ $pod ][ $field ]['id'] ) ) {
1394
								$ids = $this->api->lookup_related_items( $all_fields[ $pod ][ $field ]['id'], $all_fields[ $pod ][ $field ]['pod_id'], $ids, $all_fields[ $pod ][ $field ] );
1395
							}
1396
1397
							// No items found.
1398
							if ( empty( $ids ) ) {
1399
								return false;
1400
							} elseif ( 0 < $last_limit ) {
1401
								// @todo This should return array() if not $params->single.
1402
								$ids = array_slice( $ids, 0, $last_limit );
1403
							}
1404
1405
							// Get $pod if related to a Pod.
1406
							if ( ! empty( $pick_object ) && ( ! empty( $pick_val ) || in_array( $pick_object, array(
1407
								'user',
1408
								'media',
1409
								'comment',
1410
							), true ) ) ) {
1411
								if ( 'pod' === $pick_object ) {
1412
									$pod = $pick_val;
1413
								} else {
1414
									$check = $this->api->get_table_info( $pick_object, $pick_val );
1415
1416
									if ( ! empty( $check ) && ! empty( $check['pod'] ) ) {
1417
										$pod = $check['pod']['name'];
1418
									}
1419
								}
1420
							}
1421
						} else {
1422
							// Assume last iteration.
1423
							if ( 0 === $key ) {
1424
								// Invalid field.
1425
								return false;
1426
							}
1427
1428
							$last_loop = true;
1429
						}//end if
1430
1431
						if ( $last_loop ) {
1432
							$object_type = $last_object;
1433
							$object      = $last_pick_val;
1434
1435
							if ( in_array( $last_type, PodsForm::file_field_types(), true ) ) {
1436
								$object_type = 'media';
1437
								$object      = 'attachment';
1438
							}
1439
1440
							$data = array();
1441
1442
							$table = $this->api->get_table_info( $object_type, $object, null, null, $last_options );
1443
1444
							$join  = array();
1445
							$where = array();
1446
1447
							if ( ! empty( $table['join'] ) ) {
1448
								$join = (array) $table['join'];
1449
							}
1450
1451
							if ( ! empty( $ids ) || ! empty( $table['where'] ) ) {
1452
								foreach ( $ids as $id ) {
1453
									$where[ $id ] = '`t`.`' . $table['field_id'] . '` = ' . (int) $id;
1454
								}
1455
1456
								if ( ! empty( $where ) ) {
1457
									$where = array( implode( ' OR ', $where ) );
1458
								}
1459
1460
								if ( ! empty( $table['where'] ) ) {
1461
									// @codingStandardsIgnoreLine.
1462
									$where = array_merge( $where, array_values( (array) $table['where'] ) );
1463
								}
1464
							}
1465
1466
							/**
1467
							 * Related object.
1468
							 *
1469
							 * @var $related_obj Pods|false
1470
							 */
1471
							$related_obj = false;
1472
1473
							// Check if we can return the full object/array or if we need to traverse into it.
1474
							$is_field_output_full = false;
1475
1476
							if ( false !== $field_exists && ( in_array( $last_type, $tableless_field_types, true ) && ! $simple ) ) {
1477
								$is_field_output_full = true;
1478
							}
1479
1480
							if ( 'pod' === $object_type ) {
1481
								$related_obj = pods( $object, null, false );
1482
							} elseif ( ! empty( $table['pod'] ) ) {
1483
								$related_obj = pods( $table['pod']['name'], null, false );
1484
							}
1485
1486
							if ( $related_obj || ! empty( $table['table'] ) ) {
1487
								$sql = array(
1488
									'select'     => '*, `t`.`' . $table['field_id'] . '` AS `pod_item_id`',
1489
									'table'      => $table['table'],
1490
									'join'       => $join,
1491
									'where'      => $where,
1492
									'orderby'    => $params->orderby,
1493
									'pagination' => false,
1494
									'search'     => false,
1495
									'limit'      => - 1,
1496
									'expires'    => 180,
1497
									// @todo This could potentially cause issues if someone changes the data within this time and persistent storage is used.
1498
								);
1499
1500
								if ( ! empty( $table['where_default'] ) ) {
1501
									$sql['where_default'] = $table['where_default'];
1502
								}
1503
1504
								// Output types.
1505
								if ( in_array( $params->output, array( 'ids', 'objects', 'pods' ), true ) ) {
1506
									$sql['select'] = '`t`.`' . $table['field_id'] . '` AS `pod_item_id`';
1507
								} elseif ( 'names' === $params->output && ! empty( $table['field_index'] ) ) {
1508
									$sql['select'] = '`t`.`' . $table['field_index'] . '` AS `pod_item_index`, `t`.`' . $table['field_id'] . '` AS `pod_item_id`';
1509
								}
1510
1511
								if ( ! empty( $params->params ) && is_array( $params->params ) ) {
1512
									$where = $sql['where'];
1513
1514
									// @codingStandardsIgnoreLine.
1515
									$sql = array_merge( $sql, $params->params );
1516
1517
									if ( isset( $params->params['where'] ) ) {
1518
										// @codingStandardsIgnoreLine.
1519
										$sql['where'] = array_merge( (array) $where, (array) $params->params['where'] );
1520
									}
1521
								}
1522
1523
								$item_data = array();
1524
1525
								if ( ! $related_obj ) {
1526
									if ( ! is_object( $this->alt_data ) ) {
1527
										$this->alt_data = pods_data( null, 0, true, true );
1528
									}
1529
1530
									$item_data = $this->alt_data->select( $sql );
1531
								} else {
1532
									// Support 'find' output ordering.
1533
									if ( $ids && 'find' === $params->output && $is_field_output_full && empty( $sql['orderby'] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ids of type integer[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1534
										// Handle default orderby for ordering by the IDs.
1535
										$order_ids = implode( ', ', array_map( 'absint', $ids ) );
1536
1537
										$sql['orderby'] = 'FIELD( `t`.`' . $table['field_id'] . '`, ' . $order_ids . ' )';
1538
									}
1539
1540
									$related_obj->find( $sql );
1541
1542
									// Support 'find' output.
1543
									if ( 'find' === $params->output && $is_field_output_full ) {
1544
										$data = $related_obj;
1545
1546
										$is_field_output_full = true;
1547
									} else {
1548
										$item_data = $related_obj->data();
1549
									}
1550
								}//end if
1551
1552
								$items = array();
1553
1554
								if ( ! empty( $item_data ) ) {
1555
									foreach ( $item_data as $item ) {
1556
										if ( is_array( $item ) ) {
1557
											$item = (object) $item;
1558
										}
1559
1560
										if ( empty( $item->pod_item_id ) ) {
1561
											continue;
1562
										}
1563
1564
										// Bypass pass field.
1565
										if ( isset( $item->user_pass ) ) {
1566
											unset( $item->user_pass );
1567
										}
1568
1569
										// Get Item ID.
1570
										$item_id = $item->pod_item_id;
1571
1572
										// Output types.
1573
										if ( 'ids' === $params->output ) {
1574
											$item = (int) $item_id;
1575
										} elseif ( 'names' === $params->output && ! empty( $table['field_index'] ) ) {
1576
											$item = $item->pod_item_index;
1577
										} elseif ( 'objects' === $params->output ) {
1578
											if ( in_array( $object_type, array( 'post_type', 'media' ), true ) ) {
1579
												$item = get_post( $item_id );
1580
											} elseif ( 'taxonomy' === $object_type ) {
1581
												$item = get_term( $item_id, $object );
1582
											} elseif ( 'user' === $object_type ) {
1583
												$item = get_userdata( $item_id );
1584
1585
												if ( ! empty( $item ) ) {
1586
													// Get other vars.
1587
													$roles   = $item->roles;
1588
													$caps    = $item->caps;
1589
													$allcaps = $item->allcaps;
1590
1591
													$item = $item->data;
1592
1593
													// Set other vars.
1594
													$item->roles   = $roles;
1595
													$item->caps    = $caps;
1596
													$item->allcaps = $allcaps;
1597
1598
													unset( $item->user_pass );
1599
												}
1600
											} elseif ( 'comment' === $object_type ) {
1601
												$item = get_comment( $item_id );
1602
											} else {
1603
												$item = (object) $item;
1604
											}//end if
1605
										} elseif ( 'pods' === $params->output ) {
1606
											if ( in_array( $object_type, array( 'user', 'media' ), true ) ) {
1607
												$item = pods( $object_type, (int) $item_id );
1608
											} else {
1609
												$item = pods( $object, (int) $item_id );
1610
											}
1611
										} else {
1612
											// arrays.
1613
											$item = get_object_vars( (object) $item );
1614
										}//end if
1615
1616
										// Pass item data into $data.
1617
										$items[ $item_id ] = $item;
1618
									}//end foreach
1619
1620
									// Cleanup.
1621
									unset( $item_data );
1622
1623
									// Return all of the data in the order expected.
1624
									if ( empty( $params->orderby ) ) {
1625
										foreach ( $ids as $id ) {
1626
											if ( isset( $items[ $id ] ) ) {
1627
												$data[ $id ] = $items[ $id ];
1628
											}
1629
										}
1630
									} else {
1631
										// Use order set by orderby.
1632
										foreach ( $items as $id => $v ) {
1633
											// @codingStandardsIgnoreLine.
1634
											if ( in_array( $id, $ids ) ) {
1635
												$data[ $id ] = $v;
1636
											}
1637
										}
1638
									}
1639
								}//end if
1640
							}//end if
1641
1642
							if ( in_array( $last_type, $tableless_field_types, true ) || in_array( $last_type, array(
1643
								'boolean',
1644
								'number',
1645
								'currency',
1646
							), true ) ) {
1647
								$params->raw = true;
1648
							}
1649
1650
							if ( empty( $data ) ) {
1651
								$value = false;
1652
							} else {
1653
								$object_type = $table['type'];
1654
1655
								if ( in_array( $table['type'], array( 'post_type', 'attachment', 'media' ), true ) ) {
1656
									$object_type = 'post';
1657
								}
1658
1659
								$no_conflict = true;
1660
1661
								if ( in_array( $object_type, array(
1662
									'post',
1663
									'taxonomy',
1664
									'user',
1665
									'comment',
1666
									'settings',
1667
								), true ) ) {
1668
									$no_conflict = pods_no_conflict_check( $object_type );
1669
1670
									if ( ! $no_conflict ) {
1671
										pods_no_conflict_on( $object_type );
1672
									}
1673
								}
1674
1675
								if ( $is_field_output_full ) {
1676
									// Return entire array.
1677
									$value = $data;
1678
								} else {
1679
									// Return an array of single column values.
1680
									$value = array();
1681
1682
									foreach ( $data as $item_id => $item ) {
1683
										// $field is 123x123, needs to be _src.123x123
1684
										$full_field = implode( '.', array_splice( $params->traverse, $key ) );
1685
1686
										if ( is_array( $item ) && isset( $item[ $field ] ) ) {
1687
											if ( $table['field_id'] === $field ) {
1688
												$value[] = (int) $item[ $field ];
1689
											} else {
1690
												$value[] = $item[ $field ];
1691
											}
1692
										} elseif ( is_object( $item ) && isset( $item->{$field} ) ) {
1693
											if ( $table['field_id'] === $field ) {
1694
												$value[] = (int) $item->{$field};
1695
											} else {
1696
												$value[] = $item->{$field};
1697
											}
1698
										} elseif ( ( ( false !== strpos( $full_field, '_src' ) || 'guid' === $field ) && ( in_array( $table['type'], array(
1699
											'attachment',
1700
											'media',
1701
										), true ) || in_array( $last_type, PodsForm::file_field_types(), true ) ) ) || ( in_array( $field, array(
1702
											'_link',
1703
											'detail_url',
1704
										), true ) || in_array( $field, array(
1705
											'permalink',
1706
											'the_permalink',
1707
										), true ) && in_array( $last_type, PodsForm::file_field_types(), true ) ) ) {
1708
											// @todo Refactor the above condition statement.
1709
											$size = 'full';
1710
1711
											if ( false === strpos( 'image', get_post_mime_type( $item_id ) ) ) {
1712
												// No default sizes for non-images.
1713
												// When a size is defined this will be overwritten.
1714
												$size = null;
1715
											}
1716
1717
											if ( false !== strpos( $full_field, '_src.' ) && 5 < strlen( $full_field ) ) {
1718
												$size = substr( $full_field, 5 );
1719
											} elseif ( false !== strpos( $full_field, '_src_relative.' ) && 14 < strlen( $full_field ) ) {
1720
												$size = substr( $full_field, 14 );
1721
											} elseif ( false !== strpos( $full_field, '_src_schemeless.' ) && 16 < strlen( $full_field ) ) {
1722
												$size = substr( $full_field, 16 );
1723
											}
1724
1725
											if ( $size ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $size of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1726
												$value_url = pods_image_url( $item_id, $size );
1727
											} else {
1728
												$value_url = wp_get_attachment_url( $item_id );
1729
											}
1730
1731
											if ( ! empty( $value_url ) ) {
1732
												if ( false !== strpos( $full_field, '_src_relative' ) ) {
1733
													$value_url_parsed = wp_parse_url( $value_url );
1734
													$value_url        = $value_url_parsed['path'];
1735
												} elseif ( false !== strpos( $full_field, '_src_schemeless' ) ) {
1736
													$value_url = str_replace( array(
1737
														'http://',
1738
														'https://',
1739
													), '//', $value_url );
1740
												}
1741
											}
1742
1743
											if ( ! empty( $value_url ) ) {
1744
												$value[] = $value_url;
1745
											}
1746
1747
											$params->raw_display = true;
1748
										} elseif ( false !== strpos( $full_field, '_img' ) && ( in_array( $table['type'], array(
1749
											'attachment',
1750
											'media',
1751
										), true ) || in_array( $last_type, PodsForm::file_field_types(), true ) ) ) {
1752
											$size = 'full';
1753
1754
											if ( false !== strpos( $full_field, '_img.' ) && 5 < strlen( $full_field ) ) {
1755
												$size = substr( $full_field, 5 );
1756
											}
1757
1758
											$value[] = pods_image( $item_id, $size );
1759
1760
											$params->raw_display = true;
1761
										} elseif ( in_array( $field, array(
1762
											'_link',
1763
											'detail_url',
1764
										), true ) || in_array( $field, array( 'permalink', 'the_permalink' ), true ) ) {
1765
											if ( 'pod' === $object_type ) {
1766
												if ( is_object( $related_obj ) ) {
1767
													$related_obj->fetch( $item_id );
1768
1769
													$value[] = $related_obj->field( 'detail_url' );
1770
												} else {
1771
													$value[] = '';
1772
												}
1773
											} elseif ( 'post' === $object_type ) {
1774
												$value[] = get_permalink( $item_id );
1775
											} elseif ( 'taxonomy' === $object_type ) {
1776
												$value[] = get_term_link( $item_id, $object );
1777
											} elseif ( 'user' === $object_type ) {
1778
												$value[] = get_author_posts_url( $item_id );
1779
											} elseif ( 'comment' === $object_type ) {
1780
												$value[] = get_comment_link( $item_id );
1781
											} else {
1782
												$value[] = '';
1783
											}
1784
1785
											$params->raw_display = true;
1786
										} elseif ( in_array( $object_type, array(
1787
											'post',
1788
											'taxonomy',
1789
											'user',
1790
											'comment',
1791
										), true ) ) {
1792
											$metadata_object_id = $item_id;
1793
1794
											$metadata_type = $object_type;
1795
1796
											if ( 'post' === $object_type ) {
1797
												// Support for WPML 'duplicated' translation handling.
1798
												if ( did_action( 'wpml_loaded' ) && apply_filters( 'wpml_is_translated_post_type', false, $object ) ) {
1799
													$master_post_id = (int) apply_filters( 'wpml_master_post_from_duplicate', $metadata_object_id );
1800
1801
													if ( 0 < $master_post_id ) {
1802
														$metadata_object_id = $master_post_id;
1803
													}
1804
												}
1805
											} elseif ( 'taxonomy' === $object_type ) {
1806
												$metadata_type = 'term';
1807
											}
1808
1809
											$value[] = get_metadata( $metadata_type, $metadata_object_id, $field, true );
1810
										} elseif ( 'settings' === $object_type ) {
1811
											$value[] = get_option( $object . '_' . $field );
1812
										}//end if
1813
									}//end foreach
1814
								}//end if
1815
1816
								if ( ! $no_conflict && in_array( $object_type, array(
1817
									'post',
1818
									'taxonomy',
1819
									'user',
1820
									'comment',
1821
									'settings',
1822
								), true ) ) {
1823
									pods_no_conflict_off( $object_type );
1824
								}
1825
1826
								// Handle Simple Relationships.
1827
								if ( $simple ) {
1828
									if ( null === $params->single ) {
1829
										$params->single = false;
1830
									}
1831
1832
									$value = PodsForm::field_method( 'pick', 'simple_value', $field, $value, $last_options, $all_fields[ $pod ], 0, true );
1833
								} elseif ( false === $params->in_form && ! empty( $value ) && is_array( $value ) ) {
1834
									$value = array_values( $value );
1835
								}
1836
1837
								// Return a single column value.
1838
								if ( false === $params->in_form && 1 === $limit && ! empty( $value ) && is_array( $value ) && 1 === count( $value ) ) {
1839
									$value = current( $value );
1840
								}
1841
							}//end if
1842
1843
							if ( $last_options ) {
1844
								$last_field_data = $last_options;
1845
							}
1846
1847
							break;
1848
						}//end if
1849
					}//end foreach
1850
				}//end if
1851
			}//end if
1852
		}//end if
1853
1854
		if ( ! empty( $params->traverse ) && 1 < count( $params->traverse ) ) {
1855
			$field_names = implode( '.', $params->traverse );
1856
1857
			$this->row[ $field_names ] = $value;
1858
		} elseif ( 'arrays' !== $params->output && in_array( $field_data['type'], $tableless_field_types, true ) ) {
1859
			$this->row[ '_' . $params->output . '_' . $params->full_name ] = $value;
1860
		} elseif ( 'arrays' === $params->output || ! in_array( $field_data['type'], $tableless_field_types, true ) ) {
1861
			$this->row[ $params->full_name ] = $value;
1862
		}
1863
1864
		if ( $params->single && is_array( $value ) && 1 === count( $value ) ) {
1865
			$value = current( $value );
1866
		}
1867
1868
		if ( ! empty( $last_field_data ) ) {
1869
			$field_data = $last_field_data;
1870
		}
1871
1872
		// @todo Expand this into traversed fields too.
1873
		if ( ! empty( $field_data ) && ( $params->display || ! $params->raw ) && ! $params->in_form && ! $params->raw_display ) {
1874
			if ( $params->display || ( ( $params->get_meta || $params->deprecated ) && ! in_array( $field_data['type'], $tableless_field_types, true ) ) ) {
1875
				$field_data['options'] = pods_v( 'options', $field_data, array(), true );
1876
1877
				$post_temp   = false;
1878
				$old_post    = null;
1879
				$old_post_id = null;
1880
1881
				if ( empty( $GLOBALS['post'] ) && 'post_type' === pods_v( 'type', $this->pod_data ) && 0 < $this->id() ) {
1882
					global $post_ID, $post;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1883
1884
					$post_temp = true;
1885
1886
					$old_post    = $post;
1887
					$old_post_id = $post_ID;
1888
1889
					// @codingStandardsIgnoreLine.
1890
					$post    = get_post( $this->id() );
1891
					// @codingStandardsIgnoreLine.
1892
					$post_ID = $this->id();
1893
				}
1894
1895
				$filter = pods_v( 'display_filter', $field_data['options'] );
1896
1897
				if ( 0 < strlen( $filter ) ) {
1898
					$args = array(
1899
						$filter,
1900
						$value,
1901
					);
1902
1903
					$filter_args = pods_v( 'display_filter_args', $field_data['options'] );
1904
1905
					if ( ! empty( $filter_args ) ) {
1906
						$args = array_merge( $args, compact( $filter_args ) );
1907
					}
1908
1909
					$value = call_user_func_array( 'apply_filters', $args );
1910
				} elseif ( 1 === (int) pods_v( 'display_process', $field_data['options'], 1 ) ) {
1911
					$value = PodsForm::display( $field_data['type'], $value, $params->name, array_merge( $field_data, $field_data['options'] ), $this->pod_data, $this->id() );
1912
				}
1913
1914
				if ( $post_temp ) {
1915
					// @codingStandardsIgnoreLine.
1916
					$post    = $old_post;
1917
					// @codingStandardsIgnoreLine.
1918
					$post_ID = $old_post_id;
1919
				}
1920
			} else {
1921
				$value = PodsForm::value( $field_data['type'], $value, $params->name, array_merge( $field_data, $field_data['options'] ), $this->pod_data, $this->id() );
1922
			}//end if
1923
		}//end if
1924
1925
		/**
1926
		 * Modify value returned by field() directly before output.
1927
		 *
1928
		 * Will not run if value was null
1929
		 *
1930
		 * @since unknown
1931
		 *
1932
		 * @param array|string|null $value  Value to be returned.
1933
		 * @param array|object      $row    Current row being outputted.
1934
		 * @param array             $params Params array passed to field().
1935
		 * @param object|Pods       $this   Current Pods object.
1936
		 */
1937
		$value = apply_filters( 'pods_pods_field', $value, $this->row, $params, $this );
1938
1939
		return $value;
1940
	}
1941
1942
	/**
1943
	 * Check if an item field has a specific value in it
1944
	 *
1945
	 * @param string $field Field name.
1946
	 * @param mixed  $value Value to check.
1947
	 * @param int    $id    (optional) ID of the pod item to check.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1948
	 *
1949
	 * @return bool Whether the value was found
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1950
	 *
1951
	 * @since 2.3.3
1952
	 */
1953
	public function has( $field, $value, $id = null ) {
1954
1955
		$pod =& $this;
1956
1957
		if ( null === $id ) {
1958
			$id = $this->id();
1959
			// @codingStandardsIgnoreLine.
1960
		} elseif ( $id != $this->id() ) {
1961
			$pod = pods( $this->pod, $id );
1962
		}
1963
1964
		$this->do_hook( 'has', $field, $value, $id );
1965
1966
		if ( ! isset( $this->fields[ $field ] ) ) {
1967
			return false;
1968
		}
1969
1970
		// Tableless fields.
1971
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
1972
			if ( ! is_array( $value ) ) {
1973
				$value = explode( ',', $value );
1974
			}
1975
1976
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
1977
				$current_value = $pod->raw( $field );
1978
1979
				if ( ! empty( $current_value ) ) {
1980
					$current_value = (array) $current_value;
1981
				}
1982
1983
				foreach ( $current_value as $v ) {
1984
					// @codingStandardsIgnoreLine.
1985
					if ( in_array( $v, $value ) ) {
1986
						return true;
1987
					}
1988
				}
1989
			} else {
1990
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
1991
1992
				foreach ( $value as $k => $v ) {
1993
					if ( ! preg_match( '/[^\D]/', $v ) ) {
1994
						$value[ $k ] = (int) $v;
1995
					}
1996
1997
					// @todo Convert slugs into IDs.
1998
				}
1999
2000
				foreach ( $related_ids as $v ) {
2001
					// @codingStandardsIgnoreLine.
2002
					if ( in_array( $v, $value ) ) {
2003
						return true;
2004
					}
2005
				}
2006
			}//end if
2007
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
2008
			// Text fields.
2009
			$current_value = $pod->raw( $field );
2010
2011
			if ( 0 < strlen( $current_value ) ) {
2012
				return stripos( $current_value, $value );
2013
			}
2014
		} else {
2015
			// All other fields.
2016
			return $this->is( $field, $value, $id );
2017
		}//end if
2018
2019
		return false;
2020
	}
2021
2022
	/**
2023
	 * Check if an item field is a specific value
2024
	 *
2025
	 * @param string $field Field name.
2026
	 * @param mixed  $value Value to check.
2027
	 * @param int    $id    (optional) ID of the pod item to check.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2028
	 *
2029
	 * @return bool Whether the value was found
2030
	 *
2031
	 * @since 2.3.3
2032
	 */
2033
	public function is( $field, $value, $id = null ) {
0 ignored issues
show
Coding Style introduced by
This method's name is shorter than the configured minimum length of 3 characters.

Even though PHP does not care about the name of your methods, it is generally a good practice to choose method names which can be easily understood by other human readers.

Loading history...
2034
2035
		$pod =& $this;
2036
2037
		if ( null === $id ) {
2038
			$id = $this->id();
2039
			// @codingStandardsIgnoreLine.
2040
		} elseif ( $id != $this->id() ) {
2041
			$pod = pods( $this->pod, $id );
2042
		}
2043
2044
		$this->do_hook( 'is', $field, $value, $id );
2045
2046
		if ( ! isset( $this->fields[ $field ] ) ) {
2047
			return false;
2048
		}
2049
2050
		// Tableless fields.
2051
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
2052
			if ( ! is_array( $value ) ) {
2053
				$value = explode( ',', $value );
2054
			}
2055
2056
			$current_value = array();
2057
2058
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
2059
				$current_value = $pod->raw( $field );
2060
2061
				if ( ! empty( $current_value ) ) {
2062
					$current_value = (array) $current_value;
2063
				}
2064
2065
				foreach ( $current_value as $v ) {
2066
					// @codingStandardsIgnoreLine.
2067
					if ( in_array( $v, $value ) ) {
2068
						return true;
2069
					}
2070
				}
2071
			} else {
2072
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
2073
2074
				foreach ( $value as $k => $v ) {
2075
					if ( ! preg_match( '/[^\D]/', $v ) ) {
2076
						$value[ $k ] = (int) $v;
2077
					}
2078
2079
					// @todo Convert slugs into IDs.
2080
				}
2081
2082
				foreach ( $related_ids as $v ) {
2083
					// @codingStandardsIgnoreLine.
2084
					if ( in_array( $v, $value ) ) {
2085
						return true;
2086
					}
2087
				}
2088
			}//end if
2089
2090
			if ( ! empty( $current_value ) ) {
2091
				$current_value = array_filter( array_unique( $current_value ) );
2092
			} else {
2093
				$current_value = array();
2094
			}
2095
2096
			if ( ! empty( $value ) ) {
2097
				$value = array_filter( array_unique( $value ) );
2098
			} else {
2099
				$value = array();
2100
			}
2101
2102
			sort( $current_value );
2103
			sort( $value );
2104
2105
			if ( $value === $current_value ) {
2106
				return true;
2107
			}
2108
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
2109
			// Number fields.
2110
			$current_value = $pod->raw( $field );
2111
2112
			if ( (float) $current_value === (float) $value ) {
2113
				return true;
2114
			}
2115
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
2116
			// Date fields.
2117
			$current_value = $pod->raw( $field );
2118
2119
			if ( 0 < strlen( $current_value ) ) {
2120
				if ( strtotime( $current_value ) === strtotime( $value ) ) {
2121
					return true;
2122
				}
2123
			} elseif ( empty( $value ) ) {
2124
				return true;
2125
			}
2126
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
2127
			// Text fields.
2128
			$current_value = $pod->raw( $field );
2129
2130
			if ( (string) $current_value === (string) $value ) {
2131
				return true;
2132
			}
2133
		} else {
2134
			// All other fields.
2135
			$current_value = $pod->raw( $field );
2136
2137
			if ( $current_value === $value ) {
2138
				return true;
2139
			}
2140
		}//end if
2141
2142
		return false;
2143
	}
2144
2145
	/**
2146
	 * Return the item ID
2147
	 *
2148
	 * @return int
2149
	 * @since 2.0
2150
	 */
2151
	public function id() {
0 ignored issues
show
Coding Style introduced by
This method's name is shorter than the configured minimum length of 3 characters.

Even though PHP does not care about the name of your methods, it is generally a good practice to choose method names which can be easily understood by other human readers.

Loading history...
2152
2153
		if ( isset( $this->data->row, $this->data->row['id'] ) ) {
2154
			// If we already have data loaded return that ID.
2155
			return $this->data->row['id'];
2156
		}
2157
2158
		return $this->field( $this->data->field_id );
2159
	}
2160
2161
	/**
2162
	 * Return the previous item ID, loops at the last id to return the first
2163
	 *
2164
	 * @param int|null          $id              ID to start from.
2165
	 * @param array|object|null $params_override Override the find() parameters.
2166
	 *
2167
	 * @return int
2168
	 * @since 2.0
2169
	 */
2170
	public function prev_id( $id = null, $params_override = null ) {
2171
2172
		if ( null === $id ) {
2173
			$id = $this->id();
2174
		}
2175
2176
		$id = (int) $id;
2177
2178
		$params = array(
2179
			'select'  => "`t`.`{$this->data->field_id}`",
2180
			'where'   => "`t`.`{$this->data->field_id}` < {$id}",
2181
			'orderby' => "`t`.`{$this->data->field_id}` DESC",
2182
			'limit'   => 1,
2183
		);
2184
2185
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2186
			if ( ! empty( $params_override ) ) {
2187
				$params = $params_override;
2188
			} elseif ( ! empty( $this->params ) ) {
2189
				$params = $this->params;
2190
			}
2191
2192
			if ( is_object( $params ) ) {
2193
				$params = get_object_vars( $params );
2194
			}
2195
2196
			if ( 0 < $id ) {
2197
				if ( isset( $params['where'] ) && ! empty( $params['where'] ) ) {
2198
					$params['where']   = (array) $params['where'];
2199
					$params['where'][] = "`t`.`{$this->data->field_id}` < {$id}";
2200
				} else {
2201
					$params['where'] = "`t`.`{$this->data->field_id}` < {$id}";
2202
				}
2203
			} elseif ( isset( $params['offset'] ) && 0 < $params['offset'] ) {
2204
				$params['offset'] --;
2205
			} elseif ( 0 < $this->row_number && ! isset( $params['offset'] ) && ! empty( $this->params ) ) {
2206
				$params['offset'] = $this->row_number - 1;
2207
			} else {
2208
				return 0;
2209
			}
2210
2211
			if ( isset( $params['orderby'] ) && ! empty( $params['orderby'] ) ) {
2212
				if ( is_array( $params['orderby'] ) ) {
2213
					foreach ( $params['orderby'] as $orderby => $dir ) {
2214
						$dir = strtoupper( $dir );
2215
2216
						if ( ! in_array( $dir, array( 'ASC', 'DESC' ), true ) ) {
2217
							continue;
2218
						}
2219
2220
						if ( 'ASC' === $dir ) {
2221
							$params['orderby'][ $orderby ] = 'DESC';
2222
						} else {
2223
							$params['orderby'][ $orderby ] = 'ASC';
2224
						}
2225
					}
2226
2227
					$params['orderby'][ $this->data->field_id ] = 'DESC';
2228
				} elseif ( "`t`.`{$this->data->field_id}` DESC" !== $params['orderby'] ) {
2229
					$params['orderby'] .= ", `t`.`{$this->data->field_id}` DESC";
2230
				}
2231
			}//end if
2232
2233
			$params['select'] = "`t`.`{$this->data->field_id}`";
2234
			$params['limit']  = 1;
2235
		}//end if
2236
2237
		$pod = pods( $this->pod, $params );
2238
2239
		$new_id = 0;
2240
2241
		if ( $pod->fetch() ) {
2242
			$new_id = $pod->id();
2243
		}
2244
2245
		$new_id = $this->do_hook( 'prev_id', $new_id, $id, $pod, $params_override );
2246
2247
		return $new_id;
2248
	}
2249
2250
	/**
2251
	 * Return the next item ID, loops at the first id to return the last
2252
	 *
2253
	 * @param int|null          $id              ID to start from.
2254
	 * @param array|object|null $params_override Override the find() parameters.
2255
	 *
2256
	 * @return int
2257
	 * @since 2.0
2258
	 */
2259
	public function next_id( $id = null, $params_override = null ) {
2260
2261
		if ( null === $id ) {
2262
			$id = $this->id();
2263
		}
2264
2265
		$id = (int) $id;
2266
2267
		$params = array(
2268
			'select'  => "`t`.`{$this->data->field_id}`",
2269
			'where'   => "{$id} < `t`.`{$this->data->field_id}`",
2270
			'orderby' => "`t`.`{$this->data->field_id}` ASC",
2271
			'limit'   => 1,
2272
		);
2273
2274
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2275
			if ( ! empty( $params_override ) ) {
2276
				$params = $params_override;
2277
			} elseif ( ! empty( $this->params ) ) {
2278
				$params = $this->params;
2279
			}
2280
2281
			if ( is_object( $params ) ) {
2282
				$params = get_object_vars( $params );
2283
			}
2284
2285
			if ( 0 < $id ) {
2286
				if ( isset( $params['where'] ) && ! empty( $params['where'] ) ) {
2287
					$params['where']   = (array) $params['where'];
2288
					$params['where'][] = "{$id} < `t`.`{$this->data->field_id}`";
2289
				} else {
2290
					$params['where'] = "{$id} < `t`.`{$this->data->field_id}`";
2291
				}
2292
			} elseif ( ! isset( $params['offset'] ) ) {
2293
				if ( ! empty( $this->params ) && - 1 < $this->row_number ) {
2294
					$params['offset'] = $this->row_number + 1;
2295
				} else {
2296
					$params['offset'] = 1;
2297
				}
2298
			} else {
2299
				$params['offset'] ++;
2300
			}
2301
2302
			$params['select'] = "`t`.`{$this->data->field_id}`";
2303
			$params['limit']  = 1;
2304
		}//end if
2305
2306
		$pod = pods( $this->pod, $params );
2307
2308
		$new_id = 0;
2309
2310
		if ( $pod->fetch() ) {
2311
			$new_id = $pod->id();
2312
		}
2313
2314
		$new_id = $this->do_hook( 'next_id', $new_id, $id, $pod, $params_override );
2315
2316
		return $new_id;
2317
	}
2318
2319
	/**
2320
	 * Return the first item ID
2321
	 *
2322
	 * @param array|object|null $params_override Override the find() parameters.
2323
	 *
2324
	 * @return int
2325
	 * @since 2.3
2326
	 */
2327
	public function first_id( $params_override = null ) {
2328
2329
		$params = array(
2330
			'select'  => "`t`.`{$this->data->field_id}`",
2331
			'orderby' => "`t`.`{$this->data->field_id}` ASC",
2332
			'limit'   => 1,
2333
		);
2334
2335
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2336
			if ( ! empty( $params_override ) ) {
2337
				$params = $params_override;
2338
			} elseif ( ! empty( $this->params ) ) {
2339
				$params = $this->params;
2340
			}
2341
2342
			if ( is_object( $params ) ) {
2343
				$params = get_object_vars( $params );
2344
			}
2345
2346
			$params['select'] = "`t`.`{$this->data->field_id}`";
2347
			$params['offset'] = 0;
2348
			$params['limit']  = 1;
2349
		}
2350
2351
		$pod = pods( $this->pod, $params );
2352
2353
		$new_id = 0;
2354
2355
		if ( $pod->fetch() ) {
2356
			$new_id = $pod->id();
2357
		}
2358
2359
		$new_id = $this->do_hook( 'first_id', $new_id, $pod, $params_override );
2360
2361
		return $new_id;
2362
	}
2363
2364
	/**
2365
	 * Return the last item ID
2366
	 *
2367
	 * @param array|object|null $params_override Override the find() parameters.
2368
	 *
2369
	 * @return int
2370
	 * @since 2.3
2371
	 */
2372
	public function last_id( $params_override = null ) {
2373
2374
		$params = array(
2375
			'select'  => "`t`.`{$this->data->field_id}`",
2376
			'orderby' => "`t`.`{$this->data->field_id}` DESC",
2377
			'limit'   => 1,
2378
		);
2379
2380
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2381
			if ( ! empty( $params_override ) ) {
2382
				$params = $params_override;
2383
			} elseif ( ! empty( $this->params ) ) {
2384
				$params = $this->params;
2385
			}
2386
2387
			if ( is_object( $params ) ) {
2388
				$params = get_object_vars( $params );
2389
			}
2390
2391
			if ( isset( $params['total_found'] ) ) {
2392
				$params['offset'] = $params['total_found'] - 1;
2393
			} else {
2394
				$params['offset'] = $this->total_found() - 1;
2395
			}
2396
2397
			if ( isset( $params['orderby'] ) && ! empty( $params['orderby'] ) ) {
2398
				if ( is_array( $params['orderby'] ) ) {
2399
					foreach ( $params['orderby'] as $orderby => $dir ) {
2400
						$dir = strtoupper( $dir );
2401
2402
						if ( ! in_array( $dir, array( 'ASC', 'DESC' ), true ) ) {
2403
							continue;
2404
						}
2405
2406
						if ( 'ASC' === $dir ) {
2407
							$params['orderby'][ $orderby ] = 'DESC';
2408
						} else {
2409
							$params['orderby'][ $orderby ] = 'ASC';
2410
						}
2411
					}
2412
2413
					$params['orderby'][ $this->data->field_id ] = 'DESC';
2414
				} elseif ( "`t`.`{$this->data->field_id}` DESC" !== $params['orderby'] ) {
2415
					$params['orderby'] .= ", `t`.`{$this->data->field_id}` DESC";
2416
				}
2417
			}//end if
2418
2419
			$params['select'] = "`t`.`{$this->data->field_id}`";
2420
			$params['limit']  = 1;
2421
		}//end if
2422
2423
		$pod = pods( $this->pod, $params );
2424
2425
		$new_id = 0;
2426
2427
		if ( $pod->fetch() ) {
2428
			$new_id = $pod->id();
2429
		}
2430
2431
		$new_id = $this->do_hook( 'last_id', $new_id, $pod, $params_override );
2432
2433
		return $new_id;
2434
	}
2435
2436
	/**
2437
	 * Return the item name
2438
	 *
2439
	 * @return string
2440
	 * @since 2.0
2441
	 */
2442
	public function index() {
2443
2444
		return $this->field( $this->data->field_index );
2445
	}
2446
2447
	/**
2448
	 * Find items of a pod, much like WP_Query, but with advanced table handling.
2449
	 *
2450
	 * @param array|object $params An associative array of parameters.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|object|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2451
	 * @param int          $limit  (deprecated) Limit the number of items to find, -1 to return all items with no limit.
2452
	 * @param string       $where  (deprecated) SQL WHERE declaration to use.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $where not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2453
	 * @param string       $sql    (deprecated) For advanced use, a custom SQL query to run.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $sql not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2454
	 *
2455
	 * @return \Pods The pod object
2456
	 * @since 2.0
2457
	 * @link  https://pods.io/docs/find/
2458
	 */
2459
	public function find( $params = null, $limit = 15, $where = null, $sql = null ) {
2460
2461
		$tableless_field_types    = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
2462
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
2463
2464
		$this->params = $params;
2465
2466
		$select = '`t`.*';
2467
2468
		if ( 'table' === $this->pod_data['storage'] && ! in_array( $this->pod_data['type'], array(
2469
			'pod',
2470
			'table',
2471
		), true ) ) {
2472
			$select .= ', `d`.*';
2473
		}
2474
2475
		if ( empty( $this->data->table ) ) {
2476
			return $this;
2477
		}
2478
2479
		$defaults = array(
2480
			// Optimization parameters.
2481
			'table'               => $this->data->table,
2482
			'select'              => $select,
2483
			'join'                => null,
2484
			// Main query parameters.
2485
			'where'               => $where,
2486
			'groupby'             => null,
2487
			'having'              => null,
2488
			'orderby'             => null,
2489
			// Pagination parameters.
2490
			'limit'               => (int) $limit,
2491
			'offset'              => null,
2492
			'page'                => (int) $this->page,
2493
			'page_var'            => $this->page_var,
2494
			'pagination'          => (boolean) $this->pagination,
2495
			// Search parameters.
2496
			'search'              => (boolean) $this->search,
2497
			'search_var'          => $this->search_var,
2498
			'search_query'        => null,
2499
			'search_mode'         => $this->search_mode,
2500
			'search_across'       => false,
2501
			'search_across_picks' => false,
2502
			'search_across_files' => false,
2503
			// Advanced parameters.
2504
			'filters'             => $this->filters,
2505
			'sql'                 => $sql,
2506
			// Caching parameters.
2507
			'expires'             => null,
2508
			'cache_mode'          => 'cache',
2509
		);
2510
2511
		if ( is_array( $params ) ) {
2512
			$params = (object) array_merge( $defaults, $params );
2513
		} elseif ( is_object( $params ) ) {
2514
			$params = (object) array_merge( $defaults, get_object_vars( $params ) );
2515
		} else {
2516
			$defaults['orderby'] = $params;
2517
2518
			$params = (object) $defaults;
2519
		}
2520
2521
		/**
2522
		 * Filter the Pods::find() parameters.
2523
		 *
2524
		 * @param object $params Parameters to make lookup with.
2525
		 */
2526
		$params = apply_filters( 'pods_pods_find', $params );
2527
2528
		$params->limit = (int) $params->limit;
2529
2530
		if ( 0 === $params->limit ) {
2531
			$params->limit = - 1;
2532
		}
2533
2534
		$this->limit      = (int) $params->limit;
2535
		$this->offset     = (int) $params->offset;
2536
		$this->page       = (int) $params->page;
2537
		$this->page_var   = $params->page_var;
2538
		$this->pagination = (boolean) $params->pagination;
2539
		$this->search     = (boolean) $params->search;
2540
		$this->search_var = $params->search_var;
2541
		$params->join     = (array) $params->join;
2542
2543
		if ( empty( $params->search_query ) ) {
2544
			$params->search_query = pods_v_sanitized( $this->search_var, 'get', '' );
2545
		}
2546
2547
		// Allow orderby array ( 'field' => 'asc|desc' ).
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
2548
		if ( ! empty( $params->orderby ) && is_array( $params->orderby ) ) {
2549
			foreach ( $params->orderby as $k => $orderby ) {
2550
				if ( ! is_numeric( $k ) ) {
2551
					$key = '';
2552
2553
					$order = 'ASC';
2554
2555
					if ( 'DESC' === strtoupper( $orderby ) ) {
2556
						$order = 'DESC';
2557
					}
2558
2559
					if ( isset( $this->fields[ $k ] ) && in_array( $this->fields[ $k ]['type'], $tableless_field_types, true ) ) {
2560
						if ( in_array( $this->fields[ $k ]['pick_object'], $simple_tableless_objects, true ) ) {
2561
							if ( 'table' === $this->pod_data['storage'] ) {
2562
								if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2563
									$key = "`d`.`{$k}`";
2564
								} else {
2565
									$key = "`t`.`{$k}`";
2566
								}
2567
							} else {
2568
								$key = "`{$k}`.`meta_value`";
2569
							}
2570
						} else {
2571
							$pick_val = $this->fields[ $k ]['pick_val'];
2572
2573
							if ( '__current__' === $pick_val ) {
2574
								$pick_val = $this->pod;
2575
							}
2576
2577
							$table = $this->api->get_table_info( $this->fields[ $k ]['pick_object'], $pick_val );
2578
2579
							if ( ! empty( $table ) ) {
2580
								$key = "`{$k}`.`" . $table['field_index'] . '`';
2581
							}
2582
						}//end if
2583
					}//end if
2584
2585
					if ( empty( $key ) ) {
2586
						if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2587
							if ( isset( $this->pod_data['object_fields'][ $k ] ) ) {
2588
								$key = "`t`.`{$k}`";
2589
							} elseif ( isset( $this->fields[ $k ] ) ) {
2590
								if ( 'table' === $this->pod_data['storage'] ) {
2591
									$key = "`d`.`{$k}`";
2592
								} else {
2593
									$key = "`{$k}`.`meta_value`";
2594
								}
2595
							} else {
2596
								$object_fields = (array) $this->pod_data['object_fields'];
2597
2598
								foreach ( $object_fields as $object_field => $object_field_opt ) {
2599
									if ( $object_field === $k || in_array( $k, $object_field_opt['alias'], true ) ) {
2600
										$key = "`t`.`{$object_field}`";
2601
									}
2602
								}
2603
							}
2604
						} elseif ( isset( $this->fields[ $k ] ) ) {
2605
							if ( 'table' === $this->pod_data['storage'] ) {
2606
								$key = "`t`.`{$k}`";
2607
							} else {
2608
								$key = "`{$k}`.`meta_value`";
2609
							}
2610
						}//end if
2611
2612
						if ( empty( $key ) ) {
2613
							$key = $k;
2614
2615
							if ( false === strpos( $key, ' ' ) && false === strpos( $key, '`' ) ) {
2616
								$key = '`' . str_replace( '.', '`.`', $key ) . '`';
2617
							}
2618
						}
2619
					}//end if
2620
2621
					$orderby = $key;
2622
2623
					if ( false === strpos( $orderby, ' ' ) ) {
2624
						$orderby .= ' ' . $order;
2625
					}
2626
2627
					$params->orderby[ $k ] = $orderby;
2628
				}//end if
2629
			}//end foreach
2630
		}//end if
2631
2632
		// Add prefix to $params->orderby if needed.
2633
		if ( ! empty( $params->orderby ) ) {
2634
			if ( ! is_array( $params->orderby ) ) {
2635
				$params->orderby = array( $params->orderby );
2636
			}
2637
2638
			foreach ( $params->orderby as $ok => $prefix_orderby ) {
2639
				if ( false === strpos( $prefix_orderby, ',' ) && false === strpos( $prefix_orderby, '(' ) && false === stripos( $prefix_orderby, ' AS ' ) && false === strpos( $prefix_orderby, '`' ) && false === strpos( $prefix_orderby, '.' ) ) {
2640
					if ( false !== stripos( $prefix_orderby, ' DESC' ) ) {
2641
						$k   = trim( str_ireplace( array( '`', ' DESC' ), '', $prefix_orderby ) );
2642
						$dir = 'DESC';
2643
					} else {
2644
						$k   = trim( str_ireplace( array( '`', ' ASC' ), '', $prefix_orderby ) );
2645
						$dir = 'ASC';
2646
					}
2647
2648
					$key = $k;
2649
2650
					if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2651
						if ( isset( $this->pod_data['object_fields'][ $k ] ) ) {
2652
							$key = "`t`.`{$k}`";
2653
						} elseif ( isset( $this->fields[ $k ] ) ) {
2654
							if ( 'table' === $this->pod_data['storage'] ) {
2655
								$key = "`d`.`{$k}`";
2656
							} else {
2657
								$key = "`{$k}`.`meta_value`";
2658
							}
2659
						} else {
2660
							$object_fields = (array) $this->pod_data['object_fields'];
2661
2662
							foreach ( $object_fields as $object_field => $object_field_opt ) {
2663
								if ( $object_field === $k || in_array( $k, $object_field_opt['alias'], true ) ) {
2664
									$key = "`t`.`{$object_field}`";
2665
								}
2666
							}
2667
						}
2668
					} elseif ( isset( $this->fields[ $k ] ) ) {
2669
						if ( 'table' === $this->pod_data['storage'] ) {
2670
							$key = "`t`.`{$k}`";
2671
						} else {
2672
							$key = "`{$k}`.`meta_value`";
2673
						}
2674
					}//end if
2675
2676
					$prefix_orderby = "{$key} {$dir}";
2677
2678
					$params->orderby[ $ok ] = $prefix_orderby;
2679
				}//end if
2680
			}//end foreach
2681
		}//end if
2682
2683
		$this->data->select( $params );
2684
2685
		return $this;
2686
	}
2687
2688
	/**
2689
	 * Fetch an item from a Pod. If $id is null, it will return the next item in the list after running find().
2690
	 * You can rewind the list back to the start by using reset().
2691
	 *
2692
	 * Providing an $id will fetch a specific item from a Pod, much like a call to pods(), and can handle either an id
2693
	 * or slug.
2694
	 *
2695
	 * @see   PodsData::fetch
2696
	 *
2697
	 * @param int  $id           ID or slug of the item to fetch.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2698
	 * @param bool $explicit_set Whether to set explicitly (use false when in loop).
2699
	 *
2700
	 * @return array An array of fields from the row
2701
	 *
2702
	 * @since 2.0
2703
	 * @link  https://pods.io/docs/fetch/
2704
	 */
2705
	public function fetch( $id = null, $explicit_set = true ) {
2706
2707
		/**
2708
		 * Runs directly before an item is fetched by fetch().
2709
		 *
2710
		 * @since unknown
2711
		 *
2712
		 * @param int|string|null $id   Item ID being fetched or null.
2713
		 * @param object|Pods     $this Current Pods object.
2714
		 */
2715
		do_action( 'pods_pods_fetch', $id, $this );
2716
2717
		if ( ! empty( $id ) ) {
2718
			$this->params = array();
2719
		}
2720
2721
		$this->data->fetch( $id, $explicit_set );
2722
2723
		return $this->row;
2724
	}
2725
2726
	/**
2727
	 * (Re)set the MySQL result pointer
2728
	 *
2729
	 * @see   PodsData::reset
2730
	 *
2731
	 * @param int $row ID of the row to reset to.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $row not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2732
	 *
2733
	 * @return \Pods The pod object
2734
	 *
2735
	 * @since 2.0
2736
	 * @link  https://pods.io/docs/reset/
2737
	 */
2738
	public function reset( $row = null ) {
2739
2740
		/**
2741
		 * Runs directly before the Pods object is reset by reset()
2742
		 *
2743
		 * @since unknown
2744
		 *
2745
		 * @param int|string|null The ID of the row being reset to or null if being reset to the beginning.
2746
		 * @param object|Pods $this Current Pods object.
2747
		 */
2748
		do_action( 'pods_pods_reset', $row, $this );
2749
2750
		$this->data->reset( $row );
2751
2752
		return $this;
2753
	}
2754
2755
	/**
2756
	 * Fetch the total row count returned by the last call to find(), based on the 'limit' parameter set.
2757
	 *
2758
	 * This is different than the total number of rows found in the database, which you can get with total_found().
2759
	 *
2760
	 * @see   PodsData::total
2761
	 *
2762
	 * @return int Number of rows returned by find(), based on the 'limit' parameter set
2763
	 * @since 2.0
2764
	 * @link  https://pods.io/docs/total/
2765
	 */
2766
	public function total() {
2767
2768
		do_action( 'pods_pods_total', $this );
2769
2770
		$this->data->total();
0 ignored issues
show
Unused Code introduced by
The call to the method PodsData::total() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
2771
2772
		$this->total =& $this->data->total;
2773
2774
		return $this->total;
2775
	}
2776
2777
	/**
2778
	 * Fetch the total amount of rows found by the last call to find(), regardless of the 'limit' parameter set.
2779
	 *
2780
	 * This is different than the total number of rows limited by the current call, which you can get with total().
2781
	 *
2782
	 * @see   PodsData::total_found
2783
	 *
2784
	 * @return int Number of rows returned by find(), regardless of the 'limit' parameter
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2785
	 * @since 2.0
2786
	 * @link  https://pods.io/docs/total-found/
2787
	 */
2788
	public function total_found() {
2789
2790
		/**
2791
		 * Runs directly before the value of total_found() is determined and returned.
2792
		 *
2793
		 * @since unknown
2794
		 *
2795
		 * @param object|Pods $this Current Pods object.
2796
		 */
2797
		do_action( 'pods_pods_total_found', $this );
2798
2799
		$this->data->total_found();
2800
2801
		$this->total_found =& $this->data->total_found;
2802
2803
		return $this->total_found;
2804
	}
2805
2806
	/**
2807
	 * Fetch the total number of pages, based on total rows found and the last find() limit
2808
	 *
2809
	 * @param null|int $limit  Rows per page.
2810
	 * @param null|int $offset Offset of rows.
2811
	 * @param null|int $total  Total rows.
2812
	 *
2813
	 * @return int Number of pages
0 ignored issues
show
Documentation introduced by
Should the return type not be double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2814
	 * @since 2.3.10
2815
	 */
2816
	public function total_pages( $limit = null, $offset = null, $total = null ) {
2817
2818
		$this->do_hook( 'total_pages' );
2819
2820
		if ( null === $limit ) {
2821
			$limit = $this->limit;
2822
		}
2823
2824
		if ( null === $offset ) {
2825
			$offset = $this->offset;
2826
		}
2827
2828
		if ( null === $total ) {
2829
			$total = $this->total_found();
2830
		}
2831
2832
		return ceil( ( $total - $offset ) / $limit );
2833
2834
	}
2835
2836
	/**
2837
	 * Fetch the zebra switch
2838
	 *
2839
	 * @see   PodsData::zebra
2840
	 *
2841
	 * @return bool Zebra state
2842
	 * @since 1.12
2843
	 */
2844
	public function zebra() {
2845
2846
		$this->do_hook( 'zebra' );
2847
2848
		return $this->data->zebra();
2849
	}
2850
2851
	/**
2852
	 * Fetch the nth state
2853
	 *
2854
	 * @see   PodsData::nth
2855
	 *
2856
	 * @param int|string $nth The $nth to match on the PodsData::row_number.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $nth not be integer|string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2857
	 *
2858
	 * @return bool Whether $nth matches
2859
	 * @since 2.3
2860
	 */
2861
	public function nth( $nth = null ) {
2862
2863
		$this->do_hook( 'nth', $nth );
2864
2865
		return $this->data->nth( $nth );
2866
	}
2867
2868
	/**
2869
	 * Fetch the current position in the loop (starting at 1)
2870
	 *
2871
	 * @see   PodsData::position
2872
	 *
2873
	 * @return int Current row number (+1)
2874
	 * @since 2.3
2875
	 */
2876
	public function position() {
2877
2878
		$this->do_hook( 'position' );
2879
2880
		return $this->data->position();
2881
	}
2882
2883
	/**
2884
	 * Add an item to a Pod by giving an array of field data or set a specific field to
2885
	 * a specific value if you're just wanting to add a new item but only set one field.
2886
	 *
2887
	 * You may be looking for save() in most cases where you're setting a specific field.
2888
	 *
2889
	 * @see   PodsAPI::save_pod_item
2890
	 *
2891
	 * @param array|string $data  Either an associative array of field information or a field name.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $data not be array|string|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2892
	 * @param mixed        $value (optional) Value of the field, if $data is a field name.
2893
	 *
2894
	 * @return int The item ID
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
2895
	 *
2896
	 * @since 2.0
2897
	 * @link  https://pods.io/docs/add/
2898
	 */
2899
	public function add( $data = null, $value = null ) {
2900
2901
		if ( null !== $value ) {
2902
			$data = array( $data => $value );
2903
		}
2904
2905
		$data = (array) $this->do_hook( 'add', $data );
2906
2907
		if ( empty( $data ) ) {
2908
			return 0;
2909
		}
2910
2911
		$params = array(
2912
			'pod'                 => $this->pod,
2913
			'data'                => $data,
2914
			'allow_custom_fields' => true,
2915
		);
2916
2917
		return $this->api->save_pod_item( $params );
2918
	}
2919
2920
	/**
2921
	 * Add an item to the values of a relationship field, add a value to a number field (field+1), add time to a date
2922
	 * field, or add text to a text field
2923
	 *
2924
	 * @see   PodsAPI::save_pod_item
2925
	 *
2926
	 * @param string $field Field name.
2927
	 * @param mixed  $value IDs to add, int|float to add to number field, string for dates (+1 day), or string for text.
2928
	 * @param int    $id    (optional) ID of the pod item to update.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2929
	 *
2930
	 * @return int The item ID
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
2931
	 *
2932
	 * @since 2.3
2933
	 */
2934
	public function add_to( $field, $value, $id = null ) {
2935
2936
		$pod =& $this;
2937
2938
		$fetch = false;
2939
2940
		if ( null === $id ) {
2941
			$fetch = true;
2942
2943
			$id = $pod->id();
2944
			// @codingStandardsIgnoreLine
2945
		} elseif ( $id != $this->id() ) {
2946
			$pod = pods( $this->pod, $id );
2947
		}
2948
2949
		$this->do_hook( 'add_to', $field, $value, $id );
2950
2951
		if ( ! isset( $this->fields[ $field ] ) ) {
2952
			return $id;
2953
		}
2954
2955
		// Tableless fields.
2956
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
2957
			if ( ! is_array( $value ) ) {
2958
				$value = explode( ',', $value );
2959
			}
2960
2961
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
2962
				$current_value = $pod->raw( $field );
2963
2964
				if ( ! empty( $current_value ) || ( ! is_array( $current_value ) && 0 < strlen( $current_value ) ) ) {
2965
					$current_value = (array) $current_value;
2966
				} else {
2967
					$current_value = array();
2968
				}
2969
2970
				$value = array_merge( $current_value, $value );
2971
			} else {
2972
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
2973
2974
				foreach ( $value as $k => $v ) {
2975
					if ( ! preg_match( '/[^\D]/', $v ) ) {
2976
						$value[ $k ] = (int) $v;
2977
					}
2978
				}
2979
2980
				$value = array_merge( $related_ids, $value );
2981
			}//end if
2982
2983
			if ( ! empty( $value ) ) {
2984
				$value = array_filter( array_unique( $value ) );
2985
			} else {
2986
				$value = array();
2987
			}
2988
2989
			if ( empty( $value ) ) {
2990
				return $id;
2991
			}
2992
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
2993
			// Number fields.
2994
			$current_value = (float) $pod->raw( $field );
2995
2996
			$value = ( $current_value + (float) $value );
2997
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
2998
			// Date fields.
2999
			$current_value = $pod->raw( $field );
3000
3001
			if ( 0 < strlen( $current_value ) ) {
3002
				$value = strtotime( $value, strtotime( $current_value ) );
3003
			} else {
3004
				$value = strtotime( $value );
3005
			}
3006
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
3007
			// Text fields.
3008
			$current_value = $pod->raw( $field );
3009
3010
			if ( 0 < strlen( $current_value ) ) {
3011
				$value = $current_value . $value;
3012
			}
3013
		}//end if
3014
3015
		// @todo handle object fields and taxonomies.
3016
		$params = array(
3017
			'pod'  => $this->pod,
3018
			'id'   => $id,
3019
			'data' => array(
3020
				$field => $value,
3021
			),
3022
		);
3023
3024
		$id = $this->api->save_pod_item( $params );
3025
3026
		if ( 0 < $id && $fetch ) {
3027
			// Clear local var cache of field values.
3028
			$pod->data->row = array();
3029
3030
			$pod->fetch( $id, false );
3031
		}
3032
3033
		return $id;
3034
	}
3035
3036
	/**
3037
	 * Remove an item from the values of a relationship field, remove a value from a number field (field-1), remove
3038
	 * time to a date field
3039
	 *
3040
	 * @see   PodsAPI::save_pod_item
3041
	 *
3042
	 * @param string $field Field name.
3043
	 * @param mixed  $value IDs to add, int|float to add to number field, string for dates (-1 day), or string for text.
3044
	 * @param int    $id    (optional) ID of the pod item to update.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3045
	 *
3046
	 * @return int The item ID
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
3047
	 *
3048
	 * @since 2.3.3
3049
	 */
3050
	public function remove_from( $field, $value = null, $id = null ) {
3051
3052
		$pod =& $this;
3053
3054
		$fetch = false;
3055
3056
		if ( null === $id ) {
3057
			$fetch = true;
3058
3059
			$id = $this->id();
3060
			// @codingStandardsIgnoreLine
3061
		} elseif ( $id != $this->id() ) {
3062
			$pod = pods( $this->pod, $id );
3063
		}
3064
3065
		$this->do_hook( 'remove_from', $field, $value, $id );
3066
3067
		if ( ! isset( $this->fields[ $field ] ) ) {
3068
			return $id;
3069
		}
3070
3071
		// Tableless fields.
3072
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
3073
			if ( empty( $value ) ) {
3074
				$value = array();
3075
			}
3076
3077
			if ( ! empty( $value ) ) {
3078
				if ( ! is_array( $value ) ) {
3079
					$value = explode( ',', $value );
3080
				}
3081
3082
				if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
3083
					$current_value = $pod->raw( $field );
3084
3085
					if ( ! empty( $current_value ) ) {
3086
						$current_value = (array) $current_value;
3087
					}
3088
3089
					foreach ( $current_value as $k => $v ) {
3090
						// @codingStandardsIgnoreLine.
3091
						if ( in_array( $v, $value ) ) {
3092
							unset( $current_value[ $k ] );
3093
						}
3094
					}
3095
3096
					$value = $current_value;
3097
				} else {
3098
					$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
3099
3100
					foreach ( $value as $k => $v ) {
3101
						if ( ! preg_match( '/[^\D]/', $v ) ) {
3102
							$value[ $k ] = (int) $v;
3103
						}
3104
3105
						// @todo Convert slugs into IDs.
3106
					}
3107
3108
					foreach ( $related_ids as $k => $v ) {
3109
						// @codingStandardsIgnoreLine.
3110
						if ( in_array( $v, $value ) ) {
3111
							unset( $related_ids[ $k ] );
3112
						}
3113
					}
3114
3115
					$value = $related_ids;
3116
				}//end if
3117
3118
				if ( ! empty( $value ) ) {
3119
					$value = array_filter( array_unique( $value ) );
3120
				} else {
3121
					$value = array();
3122
				}
3123
			}//end if
3124
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
3125
			// Number fields.
3126
			// Date fields don't support empty for removing.
3127
			if ( empty( $value ) ) {
3128
				return $id;
3129
			}
3130
3131
			$current_value = (float) $pod->raw( $field );
3132
3133
			$value = ( $current_value - (float) $value );
3134
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
3135
			// Date fields.
3136
			// Date fields don't support empty for removing.
3137
			if ( empty( $value ) ) {
3138
				return $id;
3139
			}
3140
3141
			$current_value = $pod->raw( $field );
3142
3143
			if ( 0 < strlen( $current_value ) ) {
3144
				$value = strtotime( $value, strtotime( $current_value ) );
3145
			} else {
3146
				$value = strtotime( $value );
3147
			}
3148
3149
			$value = date_i18n( 'Y-m-d h:i:s', $value );
3150
		}//end if
3151
3152
		// @todo handle object fields and taxonomies.
3153
		$params = array(
3154
			'pod'  => $this->pod,
3155
			'id'   => $id,
3156
			'data' => array(
3157
				$field => $value,
3158
			),
3159
		);
3160
3161
		$id = $this->api->save_pod_item( $params );
3162
3163
		if ( 0 < $id && $fetch ) {
3164
			// Clear local var cache of field values.
3165
			$pod->data->row = array();
3166
3167
			$pod->fetch( $id, false );
3168
		}
3169
3170
		return $id;
3171
3172
	}
3173
3174
	/**
3175
	 * Save an item by giving an array of field data or set a specific field to a specific value.
3176
	 *
3177
	 * Though this function has the capacity to add new items, best practice should direct you
3178
	 * to use add() for that instead.
3179
	 *
3180
	 * @see   PodsAPI::save_pod_item
3181
	 *
3182
	 * @param array|string $data   Either an associative array of field information or a field name.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $data not be array|string|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3183
	 * @param mixed        $value  (optional) Value of the field, if $data is a field name.
3184
	 * @param int          $id     (optional) ID of the pod item to update.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3185
	 * @param array        $params (optional) Additional params to send to save_pod_item.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3186
	 *
3187
	 * @return int The item ID
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
3188
	 *
3189
	 * @since 2.0
3190
	 * @link  https://pods.io/docs/save/
3191
	 */
3192
	public function save( $data = null, $value = null, $id = null, $params = null ) {
3193
3194
		if ( null !== $value ) {
3195
			$data = array( $data => $value );
3196
		}
3197
3198
		$fetch = false;
3199
3200
		// @codingStandardsIgnoreLine
3201
		if ( null === $id || ( $this->row && $id == $this->id() ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3202
			$fetch = true;
3203
3204
			if ( null === $id ) {
3205
				$id = $this->id();
3206
			}
3207
		}
3208
3209
		$data = (array) $this->do_hook( 'save', $data, $id );
3210
3211
		if ( empty( $data ) && empty( $params['is_new_item'] ) ) {
3212
			return $id;
3213
		}
3214
3215
		$default = array();
3216
3217
		if ( ! empty( $params ) && is_array( $params ) ) {
3218
			$default = $params;
3219
		}
3220
3221
		$params = array(
3222
			'pod'                 => $this->pod,
3223
			'id'                  => $id,
3224
			'data'                => $data,
3225
			'allow_custom_fields' => true,
3226
			'clear_slug_cache'    => false,
3227
		);
3228
3229
		if ( ! empty( $default ) ) {
3230
			$params = array_merge( $params, $default );
3231
		}
3232
3233
		$id = $this->api->save_pod_item( $params );
3234
3235
		if ( 0 < $id && $fetch ) {
3236
			// Clear local var cache of field values.
3237
			$this->data->row = array();
3238
3239
			$this->fetch( $id, false );
3240
		}
3241
3242
		if ( ! empty( $this->pod_data['field_slug'] ) ) {
3243
			if ( 0 < $id && $fetch ) {
3244
				$slug = $this->field( $this->pod_data['field_slug'] );
3245
			} else {
3246
				$slug = pods( $this->pod, $id )->field( $this->pod_data['field_slug'] );
3247
			}
3248
3249
			if ( 0 < strlen( $slug ) ) {
3250
				pods_cache_clear( $slug, 'pods_items_' . $this->pod );
3251
			}
3252
		}
3253
3254
		return $id;
3255
	}
3256
3257
	/**
3258
	 * Delete an item
3259
	 *
3260
	 * @see   PodsAPI::delete_pod_item
3261
	 *
3262
	 * @param int $id ID of the Pod item to delete.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3263
	 *
3264
	 * @return bool Whether the item was successfully deleted
3265
	 *
3266
	 * @since 2.0
3267
	 * @link  https://pods.io/docs/delete/
3268
	 */
3269
	public function delete( $id = null ) {
3270
3271
		if ( null === $id ) {
3272
			$id = $this->id();
3273
		}
3274
3275
		$id = (int) $this->do_hook( 'delete', $id );
3276
3277
		if ( empty( $id ) ) {
3278
			return false;
3279
		}
3280
3281
		$params = array(
3282
			'pod' => $this->pod,
3283
			'id'  => $id,
3284
		);
3285
3286
		return $this->api->delete_pod_item( $params );
3287
	}
3288
3289
	/**
3290
	 * Reset Pod
3291
	 *
3292
	 * @see   PodsAPI::reset_pod
3293
	 *
3294
	 * @return bool Whether the Pod was successfully reset
3295
	 *
3296
	 * @since 2.1.1
3297
	 */
3298
	public function reset_pod() {
3299
3300
		$params = array( 'id' => $this->pod_id );
3301
3302
		$this->data->id   = null;
3303
		$this->data->row  = array();
3304
		$this->data->data = array();
3305
3306
		$this->data->total       = 0;
3307
		$this->data->total_found = 0;
3308
3309
		return $this->api->reset_pod( $params );
3310
	}
3311
3312
	/**
3313
	 * Duplicate an item
3314
	 *
3315
	 * @see   PodsAPI::duplicate_pod_item
3316
	 *
3317
	 * @param int $id ID of the pod item to duplicate.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3318
	 *
3319
	 * @return int|bool ID of the new pod item
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|integer.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
3320
	 *
3321
	 * @since 2.0
3322
	 * @link  https://pods.io/docs/duplicate/
3323
	 */
3324
	public function duplicate( $id = null ) {
3325
3326
		if ( null === $id ) {
3327
			$id = $this->id();
3328
		}
3329
3330
		$id = (int) $this->do_hook( 'duplicate', $id );
3331
3332
		if ( empty( $id ) ) {
3333
			return false;
3334
		}
3335
3336
		$params = array(
3337
			'pod' => $this->pod,
3338
			'id'  => $id,
3339
		);
3340
3341
		return $this->api->duplicate_pod_item( $params );
3342
	}
3343
3344
	/**
3345
	 * Import data / Save multiple rows of data at once
3346
	 *
3347
	 * @see   PodsAPI::import
3348
	 *
3349
	 * @param mixed  $import_data  PHP associative array or CSV input.
3350
	 * @param bool   $numeric_mode Use IDs instead of the name field when matching.
3351
	 * @param string $format       Format of import data, options are php or csv.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $format not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3352
	 *
3353
	 * @return array IDs of imported items
3354
	 *
3355
	 * @since 2.3
3356
	 */
3357
	public function import( $import_data, $numeric_mode = false, $format = null ) {
3358
3359
		return $this->api->import( $import_data, $numeric_mode, $format );
3360
	}
3361
3362
	/**
3363
	 * Export an item's data
3364
	 *
3365
	 * @see   PodsAPI::export_pod_item
3366
	 *
3367
	 * @param array       $fields (optional) Fields to export.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $fields not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3368
	 * @param int         $id     (optional) ID of the pod item to export.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3369
	 * @param null|string $format (optional) The format of the export (php | json).
3370
	 *
3371
	 * @return array|bool Data array of the exported pod item
3372
	 *
3373
	 * @since 2.0
3374
	 * @link  https://pods.io/docs/export/
3375
	 */
3376
	public function export( $fields = null, $id = null, $format = null ) {
3377
3378
		$params = array(
3379
			'pod'     => $this->pod,
3380
			'id'      => $id,
3381
			'fields'  => null,
3382
			'depth'   => 2,
3383
			'flatten' => false,
3384
			'context' => null,
3385
			'format'  => $format,
3386
		);
3387
3388
		if ( is_array( $fields ) && ( isset( $fields['fields'] ) || isset( $fields['depth'] ) ) ) {
3389
			$params = array_merge( $params, $fields );
3390
		} else {
3391
			$params['fields'] = $fields;
3392
		}
3393
3394
		if ( isset( $params['fields'] ) && is_array( $params['fields'] ) && ! in_array( $this->pod_data['field_id'], $params['fields'], true ) ) {
3395
			$params['fields'] = array_merge( array( $this->pod_data['field_id'] ), $params['fields'] );
3396
		}
3397
3398
		if ( null === $params['id'] ) {
3399
			$params['id'] = $this->id();
3400
		}
3401
3402
		$params = (array) $this->do_hook( 'export', $params );
3403
3404
		if ( empty( $params['id'] ) ) {
3405
			return false;
3406
		}
3407
3408
		$data = $this->api->export_pod_item( $params );
3409
3410
		if ( ! empty( $params['format'] ) && 'json' === $params['format'] ) {
3411
			$data = wp_json_encode( (array) $data );
3412
		}
3413
3414
		return $data;
3415
	}
3416
3417
	/**
3418
	 * Export data from all items
3419
	 *
3420
	 * @see   PodsAPI::export
3421
	 *
3422
	 * @param array $params An associative array of parameters.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3423
	 *
3424
	 * @return array Data arrays of all exported pod items
3425
	 *
3426
	 * @since 2.3
3427
	 */
3428
	public function export_data( $params = null ) {
3429
3430
		$defaults = array(
3431
			'fields' => null,
3432
			'depth'  => 2,
3433
			'params' => null,
3434
		);
3435
3436
		if ( empty( $params ) ) {
3437
			$params = $defaults;
3438
		} else {
3439
			$params = array_merge( $defaults, (array) $params );
3440
		}
3441
3442
		return $this->api->export( $this, $params );
3443
	}
3444
3445
	/**
3446
	 * Display the pagination controls, types supported by default
3447
	 * are simple, paginate and advanced. The base and format parameters
3448
	 * are used only for the paginate view.
3449
	 *
3450
	 * @param array|object $params Associative array of parameters.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|object|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3451
	 *
3452
	 * @return string Pagination HTML
3453
	 * @since 2.0
3454
	 * @link  https://pods.io/docs/pagination/
3455
	 */
3456
	public function pagination( $params = null ) {
3457
3458
		if ( empty( $params ) ) {
3459
			$params = array();
3460
		} elseif ( ! is_array( $params ) ) {
3461
			$params = array( 'label' => $params );
3462
		}
3463
3464
		$this->page_var = pods_v( 'page_var', $params, $this->page_var );
3465
3466
		$url = pods_query_arg( null, null, $this->page_var );
3467
3468
		$append = '?';
3469
3470
		if ( false !== strpos( $url, '?' ) ) {
3471
			$append = '&';
3472
		}
3473
3474
		$defaults = array(
3475
			'type'        => 'advanced',
3476
			'label'       => __( 'Go to page:', 'pods' ),
3477
			'show_label'  => true,
3478
			'first_text'  => __( '&laquo; First', 'pods' ),
3479
			'prev_text'   => __( '&lsaquo; Previous', 'pods' ),
3480
			'next_text'   => __( 'Next &rsaquo;', 'pods' ),
3481
			'last_text'   => __( 'Last &raquo;', 'pods' ),
3482
			'prev_next'   => true,
3483
			'first_last'  => true,
3484
			'limit'       => (int) $this->limit,
3485
			'offset'      => (int) $this->offset,
3486
			'page'        => max( 1, (int) $this->page ),
3487
			'mid_size'    => 2,
3488
			'end_size'    => 1,
3489
			'total_found' => $this->total_found(),
3490
			'page_var'    => $this->page_var,
3491
			'base'        => "{$url}{$append}%_%",
3492
			'format'      => "{$this->page_var}=%#%",
3493
			'class'       => '',
3494
			'link_class'  => '',
3495
		);
3496
3497
		if ( is_object( $params ) ) {
3498
			$params = get_object_vars( $params );
3499
		}
3500
3501
		$params = (object) array_merge( $defaults, (array) $params );
3502
3503
		$params->total = $this->total_pages( $params->limit, $params->offset, $params->total_found );
3504
3505
		if ( $params->limit < 1 || $params->total_found < 1 || 1 === $params->total || $params->total_found <= $params->offset ) {
3506
			return $this->do_hook( 'pagination', $this->do_hook( 'pagination_' . $params->type, '', $params ), $params );
3507
		}
3508
3509
		$pagination = $params->type;
3510
3511
		if ( ! in_array( $params->type, array( 'simple', 'advanced', 'paginate', 'list' ), true ) ) {
3512
			$pagination = 'advanced';
3513
		}
3514
3515
		ob_start();
3516
3517
		pods_view( PODS_DIR . 'ui/front/pagination/' . $pagination . '.php', compact( array_keys( get_defined_vars() ) ) );
3518
3519
		$output = ob_get_clean();
3520
3521
		return $this->do_hook( 'pagination', $this->do_hook( 'pagination_' . $params->type, $output, $params ), $params );
3522
3523
	}
3524
3525
	/**
3526
	 * Return a filter form for searching a Pod
3527
	 *
3528
	 * @param array|string $params Comma-separated list of fields or array of parameters.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|string|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3529
	 *
3530
	 * @return string Filters HTML
3531
	 *
3532
	 * @since 2.0
3533
	 * @link  https://pods.io/docs/filters/
3534
	 */
3535
	public function filters( $params = null ) {
3536
3537
		$defaults = array(
3538
			'fields' => $params,
3539
			'label'  => '',
3540
			'action' => '',
3541
			'search' => '',
3542
		);
3543
3544
		if ( is_array( $params ) ) {
3545
			$params = array_merge( $defaults, $params );
3546
		} else {
3547
			$params = $defaults;
3548
		}
3549
3550
		$pod =& $this;
3551
3552
		/**
3553
		 * Filter the Pods::filters() parameters.
3554
		 *
3555
		 * @param array $params Parameters to filter with.
3556
		 * @param Pods  $pod    Pods object.
3557
		 */
3558
		$params = apply_filters( 'pods_filters_params', $params, $pod );
3559
3560
		$fields = $params['fields'];
3561
3562
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
3563
			$fields = explode( ',', $fields );
3564
		}
3565
3566
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
3567
3568
		// Force array.
3569
		if ( empty( $fields ) ) {
3570
			$fields = array();
3571
		} else {
3572
			// Temporary.
3573
			$filter_fields = $fields;
3574
3575
			$fields = array();
3576
3577
			foreach ( $filter_fields as $k => $field ) {
3578
				$name = $k;
3579
3580
				$defaults = array(
3581
					'name' => $name,
3582
				);
3583
3584
				if ( ! is_array( $field ) ) {
3585
					$name = $field;
3586
3587
					$field = array(
3588
						'name' => $name,
3589
					);
3590
				}
3591
3592
				// @codingStandardsIgnoreLine.
3593
				$field = array_merge( $defaults, $field );
3594
3595
				$field['name'] = trim( $field['name'] );
3596
3597
				if ( pods_v( 'hidden', $field, false, true ) ) {
3598
					$field['type'] = 'hidden';
3599
				}
3600
3601
				if ( isset( $object_fields[ $field['name'] ] ) ) {
3602
					// @codingStandardsIgnoreLine.
3603
					$fields[ $field['name'] ] = array_merge( $object_fields[ $field['name'] ], $field );
3604
				} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
3605
					// @codingStandardsIgnoreLine.
3606
					$fields[ $field['name'] ] = array_merge( $this->fields[ $field['name'] ], $field );
3607
				}
3608
			}//end foreach
3609
3610
			// Cleanup.
3611
			unset( $filter_fields );
3612
		}//end if
3613
3614
		$this->filters = array_keys( $fields );
3615
3616
		$label = $params['label'];
3617
3618
		if ( '' === $label ) {
3619
			$label = __( 'Search', 'pods' );
3620
		}
3621
3622
		$action = $params['action'];
3623
3624
		$search = trim( $params['search'] );
3625
3626
		if ( '' === $search ) {
3627
			$search = pods_v_sanitized( $pod->search_var, 'get', '' );
3628
		}
3629
3630
		ob_start();
3631
3632
		pods_view( PODS_DIR . 'ui/front/filters.php', compact( array_keys( get_defined_vars() ) ) );
3633
3634
		$output = ob_get_clean();
3635
3636
		/**
3637
		 * Filter the HTML output of filters()
3638
		 *
3639
		 * @since unknown
3640
		 *
3641
		 * @param string      $output Filter output.
3642
		 * @param array       $params Params array passed to filters().
3643
		 * @param object|Pods $this   Current Pods object.
3644
		 */
3645
		return apply_filters( 'pods_pods_filters', $output, $params, $this );
3646
	}
3647
3648
	/**
3649
	 * Run a helper within a Pod Page or WP Template
3650
	 *
3651
	 * @see   Pods_Helpers::helper
3652
	 *
3653
	 * @param string|array $helper Helper name.
3654
	 * @param string       $value  Value to run the helper on.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $value not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3655
	 * @param string       $name   Field name.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $name not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3656
	 *
3657
	 * @return mixed Anything returned by the helper
3658
	 * @since 2.0
3659
	 */
3660
	public function helper( $helper, $value = null, $name = null ) {
3661
3662
		$params = array(
3663
			'helper'     => $helper,
3664
			'value'      => $value,
3665
			'name'       => $name,
3666
			'deprecated' => false,
3667
		);
3668
3669
		if ( class_exists( 'Pods_Templates' ) ) {
3670
			$params['deprecated'] = Pods_Templates::$deprecated;
3671
		}
3672
3673
		if ( is_array( $helper ) ) {
3674
			$params = array_merge( $params, $helper );
3675
		}
3676
3677
		if ( class_exists( 'Pods_Helpers' ) ) {
3678
			$value = Pods_Helpers::helper( $params, $this );
3679
		} elseif ( function_exists( $params['helper'] ) ) {
3680
			$disallowed = array(
3681
				'system',
3682
				'exec',
3683
				'popen',
3684
				'eval',
3685
				'preg_replace',
3686
				'create_function',
3687
				'include',
3688
				'include_once',
3689
				'require',
3690
				'require_once',
3691
			);
3692
3693
			$allowed = array();
3694
3695
			/**
3696
			 * Allows adjusting the disallowed callbacks as needed.
3697
			 *
3698
			 * @param array $disallowed List of callbacks not allowed.
3699
			 * @param array $params     Parameters used by Pods::helper() method.
3700
			 *
3701
			 * @since 2.7
3702
			 */
3703
			$disallowed = apply_filters( 'pods_helper_disallowed_callbacks', $disallowed, $params );
3704
3705
			/**
3706
			 * Allows adjusting the allowed allowed callbacks as needed.
3707
			 *
3708
			 * @param array $allowed List of callbacks explicitly allowed.
3709
			 * @param array $params  Parameters used by Pods::helper() method.
3710
			 *
3711
			 * @since 2.7
3712
			 */
3713
			$allowed = apply_filters( 'pods_helper_allowed_callbacks', $allowed, $params );
3714
3715
			// Clean up helper callback (if string).
3716
			if ( is_string( $params['helper'] ) ) {
3717
				$params['helper'] = strip_tags( str_replace( array( '`', chr( 96 ) ), "'", $params['helper'] ) );
3718
			}
3719
3720
			$is_allowed = false;
3721
3722
			if ( ! empty( $allowed ) ) {
3723
				if ( in_array( $params['helper'], $allowed, true ) ) {
3724
					$is_allowed = true;
3725
				}
3726
			} elseif ( ! in_array( $params['helper'], $disallowed, true ) ) {
3727
				$is_allowed = true;
3728
			}
3729
3730
			if ( $is_allowed ) {
3731
				$value = call_user_func( $params['helper'], $value );
3732
			}
3733
		} else {
3734
			$value = apply_filters( $params['helper'], $value );
3735
		}//end if
3736
3737
		return $value;
3738
	}
3739
3740
	/**
3741
	 * Display the page template
3742
	 *
3743
	 * @see   Pods_Templates::template
3744
	 *
3745
	 * @param string      $template_name The template name.
3746
	 * @param string|null $code          Custom template code to use instead.
3747
	 * @param bool        $deprecated    Whether to use deprecated functionality based on old function usage.
3748
	 *
3749
	 * @return mixed Template output
3750
	 *
3751
	 * @since 2.0
3752
	 * @link  https://pods.io/docs/template/
3753
	 */
3754
	public function template( $template_name, $code = null, $deprecated = false ) {
3755
3756
		$out = null;
3757
3758
		$obj =& $this;
3759
3760
		if ( ! empty( $code ) ) {
3761
			// backwards compatibility.
3762
			$code = str_replace( '$this->', '$obj->', $code );
3763
3764
			/**
3765
			 * Filter the template code before running it.
3766
			 *
3767
			 * @param string $code          Template code.
3768
			 * @param string $template_name Template name.
3769
			 * @param Pods  $pod            Pods object.
3770
			 */
3771
			$code = apply_filters( 'pods_templates_pre_template', $code, $template_name, $this );
3772
3773
			/**
3774
			 * Filter the template code before running it.
3775
			 *
3776
			 * @param string $code          Template code.
3777
			 * @param string $template_name Template name.
3778
			 * @param Pods  $pod            Pods object.
3779
			 */
3780
			$code = apply_filters( "pods_templates_pre_template_{$template_name}", $code, $template_name, $this );
3781
3782
			ob_start();
3783
3784
			if ( ! empty( $code ) ) {
3785
				// Only detail templates need $this->id.
3786
				if ( empty( $this->id ) ) {
3787
					while ( $this->fetch() ) {
3788
						// @codingStandardsIgnoreLine
3789
						echo $this->do_magic_tags( $code );
3790
					}
3791
				} else {
3792
					// @codingStandardsIgnoreLine
3793
					echo $this->do_magic_tags( $code );
3794
				}
3795
			}
3796
3797
			$out = ob_get_clean();
3798
3799
			/**
3800
			 * Filter the template output.
3801
			 *
3802
			 * @param string $out           Template output.
3803
			 * @param string $code          Template code.
3804
			 * @param string $template_name Template name.
3805
			 * @param Pods   $pod           Pods object.
3806
			 */
3807
			$out = apply_filters( 'pods_templates_post_template', $out, $code, $template_name, $this );
3808
3809
			/**
3810
			 * Filter the template output.
3811
			 *
3812
			 * @param string $out           Template output.
3813
			 * @param string $code          Template code.
3814
			 * @param string $template_name Template name.
3815
			 * @param Pods   $pod           Pods object.
3816
			 */
3817
			$out = apply_filters( "pods_templates_post_template_{$template_name}", $out, $code, $template_name, $this );
3818
		} elseif ( class_exists( 'Pods_Templates' ) ) {
3819
			$out = Pods_Templates::template( $template_name, $code, $this, $deprecated );
3820
		} elseif ( trim( preg_replace( '/[^a-zA-Z0-9_\-\/]/', '', $template_name ), ' /-' ) === $template_name ) {
3821
			ob_start();
3822
3823
			$default_templates = array(
3824
				'pods/' . $template_name,
3825
				'pods-' . $template_name,
3826
				$template_name,
3827
			);
3828
3829
			/**
3830
			 * Filter the default Pods Template files.
3831
			 *
3832
			 * @param array $default_templates Default Pods Template files.
3833
			 */
3834
			$default_templates = apply_filters( 'pods_template_default_templates', $default_templates );
3835
3836
			// Only detail templates need $this->id.
3837
			if ( empty( $this->id ) ) {
3838
				while ( $this->fetch() ) {
3839
					pods_template_part( $default_templates, compact( array_keys( get_defined_vars() ) ) );
3840
				}
3841
			} else {
3842
				pods_template_part( $default_templates, compact( array_keys( get_defined_vars() ) ) );
3843
			}
3844
3845
			$out = ob_get_clean();
3846
3847
			/**
3848
			 * Filter the template output.
3849
			 *
3850
			 * @param string $out           Template output.
3851
			 * @param string $code          Template code.
3852
			 * @param string $template_name Template name.
3853
			 * @param Pods   $pod           Pods object.
3854
			 */
3855
			$out = apply_filters( 'pods_templates_post_template', $out, $code, $template_name, $this );
3856
3857
			/**
3858
			 * Filter the template output.
3859
			 *
3860
			 * @param string $out           Template output.
3861
			 * @param string $code          Template code.
3862
			 * @param string $template_name Template name.
3863
			 * @param Pods   $pod           Pods object.
3864
			 */
3865
			$out = apply_filters( "pods_templates_post_template_{$template_name}", $out, $code, $template_name, $this );
3866
		}//end if
3867
3868
		return $out;
3869
	}
3870
3871
	/**
3872
	 * Embed a form to add / edit a pod item from within your theme. Provide an array of $fields to include
3873
	 * and override options where needed. For WP object based Pods, you can pass through the WP object
3874
	 * field names too, such as "post_title" or "post_content" for example.
3875
	 *
3876
	 * @param array  $params    (optional) Fields to show on the form, defaults to all fields.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3877
	 * @param string $label     (optional) Save button label, defaults to "Save Changes".
0 ignored issues
show
Documentation introduced by
Should the type for parameter $label not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3878
	 * @param string $thank_you (optional) Thank you URL to send to upon success.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $thank_you not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3879
	 *
3880
	 * @return bool|mixed
3881
	 * @since 2.0
3882
	 * @link  https://pods.io/docs/form/
3883
	 */
3884
	public function form( $params = null, $label = null, $thank_you = null ) {
3885
3886
		$defaults = array(
3887
			'fields'      => $params,
3888
			'label'       => $label,
3889
			'thank_you'   => $thank_you,
3890
			'fields_only' => false,
3891
		);
3892
3893
		if ( is_array( $params ) ) {
3894
			$params = array_merge( $defaults, $params );
3895
		} else {
3896
			$params = $defaults;
3897
		}
3898
3899
		$pod =& $this;
3900
3901
		$params = $this->do_hook( 'form_params', $params );
3902
3903
		$fields = $params['fields'];
3904
3905
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
3906
			$fields = explode( ',', $fields );
3907
		}
3908
3909
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
3910
3911
		if ( empty( $fields ) ) {
3912
			// Add core object fields if $fields is empty.
3913
			$fields = array_merge( $object_fields, $this->fields );
3914
		}
3915
3916
		// Temporary.
3917
		$form_fields = $fields;
3918
3919
		$fields = array();
3920
3921
		foreach ( $form_fields as $k => $field ) {
3922
			$name = $k;
3923
3924
			$defaults = array(
3925
				'name' => $name,
3926
			);
3927
3928
			if ( ! is_array( $field ) ) {
3929
				$name = $field;
3930
3931
				$field = array(
3932
					'name' => $name,
3933
				);
3934
			}
3935
3936
			// @codingStandardsIgnoreLine.
3937
			$field = array_merge( $defaults, $field );
3938
3939
			$field['name'] = trim( $field['name'] );
3940
3941
			$default_value = pods_v( 'default', $field );
3942
			$value         = pods_v( 'value', $field );
3943
3944
			if ( empty( $field['name'] ) ) {
3945
				$field['name'] = trim( $name );
3946
			}
3947
3948
			if ( isset( $object_fields[ $field['name'] ] ) ) {
3949
				// @codingStandardsIgnoreLine.
3950
				$field = array_merge( $object_fields[ $field['name'] ], $field );
3951
			} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
3952
				// @codingStandardsIgnoreLine.
3953
				$field = array_merge( $this->fields[ $field['name'] ], $field );
3954
			}
3955
3956
			if ( pods_v( 'hidden', $field, false, true ) ) {
3957
				$field['type'] = 'hidden';
3958
			}
3959
3960
			$fields[ $field['name'] ] = $field;
3961
3962
			if ( empty( $this->id ) && null !== $default_value ) {
3963
				$this->row_override[ $field['name'] ] = $default_value;
3964
			} elseif ( ! empty( $this->id ) && null !== $value ) {
3965
				$this->row[ $field['name'] ] = $value;
3966
			}
3967
		}//end foreach
3968
3969
		// Cleanup.
3970
		unset( $form_fields );
3971
3972
		$fields = $this->do_hook( 'form_fields', $fields, $params );
3973
3974
		$label = $params['label'];
3975
3976
		if ( empty( $label ) ) {
3977
			$label = __( 'Save Changes', 'pods' );
3978
		}
3979
3980
		$thank_you   = $params['thank_you'];
3981
		$fields_only = $params['fields_only'];
3982
3983
		PodsForm::$form_counter ++;
3984
3985
		ob_start();
3986
3987
		if ( empty( $thank_you ) ) {
3988
			$success = 'success';
3989
3990
			if ( 1 < PodsForm::$form_counter ) {
3991
				$success .= PodsForm::$form_counter;
3992
			}
3993
3994
			$thank_you = pods_query_arg( array(
3995
				'success*' => null,
3996
				$success   => 1,
3997
			) );
3998
3999
			if ( 1 === (int) pods_v( $success, 'get', 0 ) ) {
4000
				$message = __( 'Form submitted successfully', 'pods' );
4001
4002
				/**
4003
				 * Change the text of the message that appears on successful form submission.
4004
				 *
4005
				 * @param string $message Success message.
4006
				 *
4007
				 * @since 2.7
4008
				 */
4009
				$message = apply_filters( 'pods_pod_form_success_message', $message );
4010
4011
				echo '<div id="message" class="pods-form-front-success">' . wp_kses_post( $message ) . '</div>';
4012
			}
4013
		}//end if
4014
4015
		pods_view( PODS_DIR . 'ui/front/form.php', compact( array_keys( get_defined_vars() ) ) );
4016
4017
		$output = ob_get_clean();
4018
4019
		if ( empty( $this->id ) ) {
4020
			$this->row_override = array();
4021
		}
4022
4023
		return $this->do_hook( 'form', $output, $fields, $label, $thank_you, $this, $this->id() );
4024
	}
4025
4026
	/**
4027
	 * Render a singular view for the Pod item content.
4028
	 *
4029
	 * @param array|string|null $fields (optional) Fields to show in the view, defaults to all fields.
4030
	 *
4031
	 * @return mixed
4032
	 * @since 2.3.10
4033
	 */
4034
	public function view( $fields = null ) {
4035
4036
		$pod =& $this;
4037
4038
		// Convert comma separated list of fields to an array.
4039
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
4040
			$fields = explode( ',', $fields );
4041
		}
4042
4043
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
4044
4045
		if ( empty( $fields ) ) {
4046
			// Add core object fields if $fields is empty.
4047
			$fields = array_merge( $object_fields, $this->fields );
4048
		}
4049
4050
		// Temporary.
4051
		$view_fields = $fields;
4052
4053
		$fields = array();
4054
4055
		foreach ( $view_fields as $name => $field ) {
0 ignored issues
show
Bug introduced by
The expression $view_fields of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
4056
			$defaults = array(
4057
				'name' => $name,
4058
			);
4059
4060
			if ( ! is_array( $field ) ) {
4061
				$name = $field;
4062
4063
				$field = array(
4064
					'name' => $name,
4065
				);
4066
			}
4067
4068
			// @codingStandardsIgnoreLine.
4069
			$field = array_merge( $defaults, $field );
4070
4071
			$field['name'] = trim( $field['name'] );
4072
4073
			if ( empty( $field['name'] ) ) {
4074
				$field['name'] = trim( $name );
4075
			}
4076
4077
			if ( isset( $object_fields[ $field['name'] ] ) ) {
4078
				// @codingStandardsIgnoreLine.
4079
				$field = array_merge( $field, $object_fields[ $field['name'] ] );
4080
			} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
4081
				// @codingStandardsIgnoreLine.
4082
				$field = array_merge( $this->fields[ $field['name'] ], $field );
4083
			}
4084
4085
			if ( pods_v( 'hidden', $field, false, true ) || 'hidden' === $field['type'] ) {
4086
				continue;
4087
			} elseif ( ! PodsForm::permission( $field['type'], $field['name'], $field['options'], $fields, $pod, $pod->id() ) ) {
4088
				continue;
4089
			}
4090
4091
			$fields[ $field['name'] ] = $field;
4092
		}//end foreach
4093
4094
		// Cleanup.
4095
		unset( $view_fields );
4096
4097
		$output = pods_view( PODS_DIR . 'ui/front/view.php', compact( array_keys( get_defined_vars() ) ), false, 'cache', true );
4098
4099
		return $this->do_hook( 'view', $output, $fields, $this->id() );
4100
4101
	}
4102
4103
	/**
4104
	 * Replace magic tags with their values
4105
	 *
4106
	 * @param string $code The content to evaluate.
4107
	 *
4108
	 * @return string Code with Magic Tags evaluated
4109
	 *
4110
	 * @since 2.0
4111
	 */
4112
	public function do_magic_tags( $code ) {
4113
4114
		/**
4115
		 * Filters the Pods magic tags content before the default function.
4116
		 * Allows complete replacement of the Pods magic tag engine.
4117
		 *
4118
		 * @param null   $pre  Default is null which processes magic tags normally. Return any other value to override.
4119
		 * @param string $code The content to evaluate.
4120
		 * @param Pods   $pods The Pods Object.
4121
		 *
4122
		 * @since 2.7
4123
		 */
4124
		$pre = apply_filters( 'pods_pre_do_magic_tags', null, $code, $this );
4125
		if ( null !== $pre ) {
4126
			return $pre;
4127
		}
4128
4129
		return preg_replace_callback( '/({@(.*?)})/m', array( $this, 'process_magic_tags' ), $code );
4130
	}
4131
4132
	/**
4133
	 * Replace magic tags with their values
4134
	 *
4135
	 * @param string|array $tag The magic tag to process.
4136
	 *
4137
	 * @return string Code with Magic Tags evaluated
4138
	 *
4139
	 * @since 2.0.2
4140
	 */
4141
	private function process_magic_tags( $tag ) {
4142
4143
		if ( is_array( $tag ) ) {
4144
			if ( ! isset( $tag[2] ) && '' === trim( $tag[2] ) ) {
4145
				return '';
4146
			}
4147
4148
			$tag = $tag[2];
4149
		}
4150
4151
		$tag = trim( $tag, ' {@}' );
4152
		$tag = explode( ',', $tag );
4153
4154
		if ( empty( $tag ) || ! isset( $tag[0] ) || '' === trim( $tag[0] ) ) {
4155
			return '';
4156
		}
4157
4158
		foreach ( $tag as $k => $v ) {
4159
			$tag[ $k ] = trim( $v );
4160
		}
4161
4162
		$field_name = $tag[0];
4163
4164
		$helper_name = '';
4165
		$before      = '';
4166
		$after       = '';
4167
4168
		if ( isset( $tag[1] ) && ! empty( $tag[1] ) ) {
4169
			$value = $this->field( $field_name );
4170
4171
			$helper_name = $tag[1];
4172
4173
			$value = $this->helper( $helper_name, $value, $field_name );
4174
		} else {
4175
			$value = $this->display( $field_name );
4176
		}
4177
4178
		if ( isset( $tag[2] ) && ! empty( $tag[2] ) ) {
4179
			$before = $tag[2];
4180
		}
4181
4182
		if ( isset( $tag[3] ) && ! empty( $tag[3] ) ) {
4183
			$after = $tag[3];
4184
		}
4185
4186
		/**
4187
		 * Filter the magic tag output for a value.
4188
		 *
4189
		 * @param string $value      Magic tag output for value.
4190
		 * @param string $field_name Magic tag field name.
4191
		 * @param string $before     Before content.
4192
		 * @param string $after      After content.
4193
		 */
4194
		$value = apply_filters( 'pods_do_magic_tags', $value, $field_name, $helper_name, $before, $after );
4195
4196
		if ( is_array( $value ) ) {
4197
			$value = pods_serial_comma( $value, array(
4198
				'field'  => $field_name,
4199
				'fields' => $this->fields,
4200
			) );
4201
		}
4202
4203
		if ( null !== $value && false !== $value ) {
4204
			return $before . $value . $after;
4205
		}
4206
4207
		return '';
4208
	}
4209
4210
	/**
4211
	 *
4212
	 * Generate UI for Data Management
4213
	 *
4214
	 * @param mixed $options Array or String containing Pod or Options to be used.
4215
	 * @param bool  $amend   Whether to amend the default UI options or replace entirely.
4216
	 *
4217
	 * @return PodsUI|null UI object or null if custom UI used
4218
	 *
4219
	 * @since 2.3.10
4220
	 */
4221
	public function ui( $options = null, $amend = false ) {
0 ignored issues
show
Coding Style introduced by
This method's name is shorter than the configured minimum length of 3 characters.

Even though PHP does not care about the name of your methods, it is generally a good practice to choose method names which can be easily understood by other human readers.

Loading history...
4222
4223
		$num = '';
4224
4225
		if ( empty( $options ) ) {
4226
			$options = array();
4227
		} else {
4228
			$num = pods_v_sanitized( 'num', $options, '' );
4229
4230
			if ( empty( $num ) ) {
4231
				$num = '';
4232
			}
4233
		}
4234
4235
		$check_id = pods_v( 'id' . $num, 'get', null, true );
4236
4237
		// @codingStandardsIgnoreLine
4238
		if ( $this->id() != $check_id ) {
4239
			$this->fetch( $check_id );
4240
		}
4241
4242
		if ( ! empty( $options ) && ! $amend ) {
4243
			$this->ui = $options;
4244
4245
			return pods_ui( $this );
4246
		} elseif ( ! empty( $options ) || 'custom' !== pods_v( 'ui_style', $this->pod_data['options'], 'post_type', true ) ) {
4247
			$actions_enabled = pods_v( 'ui_actions_enabled', $this->pod_data['options'] );
4248
4249
			if ( ! empty( $actions_enabled ) ) {
4250
				$actions_enabled = (array) $actions_enabled;
4251
			} else {
4252
				$actions_enabled = array();
4253
			}
4254
4255
			$available_actions = array(
4256
				'add',
4257
				'edit',
4258
				'duplicate',
4259
				'delete',
4260
				'reorder',
4261
				'export',
4262
			);
4263
4264
			if ( ! empty( $actions_enabled ) ) {
4265
				$actions_disabled = array(
4266
					'view' => 'view',
4267
				);
4268
4269
				foreach ( $available_actions as $action ) {
4270
					if ( ! in_array( $action, $actions_enabled, true ) ) {
4271
						$actions_disabled[ $action ] = $action;
4272
					}
4273
				}
4274
			} else {
4275
				$actions_disabled = array(
4276
					'duplicate' => 'duplicate',
4277
					'view'      => 'view',
4278
					'export'    => 'export',
4279
				);
4280
4281
				if ( 1 === pods_v( 'ui_export', $this->pod_data['options'], 0 ) ) {
4282
					unset( $actions_disabled['export'] );
4283
				}
4284
			}//end if
4285
4286
			if ( empty( $options ) ) {
4287
				$author_restrict = false;
4288
4289
				if ( isset( $this->fields['author'] ) && 'pick' === $this->fields['author']['type'] && 'user' === $this->fields['author']['pick_object'] ) {
4290
					$author_restrict = 'author.ID';
4291
				}
4292
4293
				if ( ! pods_is_admin( array( 'pods', 'pods_content' ) ) ) {
4294
					if ( ! current_user_can( 'pods_add_' . $this->pod ) ) {
4295
						$actions_disabled['add'] = 'add';
4296
4297
						if ( 'add' === pods_v( 'action' . $num, 'get' ) ) {
4298
							$_GET[ 'action' . $num ] = 'manage';
4299
						}
4300
					}
4301
4302
					if ( ! $author_restrict && ! current_user_can( 'pods_edit_' . $this->pod ) && ! current_user_can( 'pods_edit_others_' . $this->pod ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $author_restrict of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
4303
						$actions_disabled['edit'] = 'edit';
4304
					}
4305
4306
					if ( ! $author_restrict && ! current_user_can( 'pods_delete_' . $this->pod ) && ! current_user_can( 'pods_delete_others_' . $this->pod ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $author_restrict of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
4307
						$actions_disabled['delete'] = 'delete';
4308
					}
4309
4310
					if ( ! current_user_can( 'pods_reorder_' . $this->pod ) ) {
4311
						$actions_disabled['reorder'] = 'reorder';
4312
					}
4313
4314
					if ( ! current_user_can( 'pods_export_' . $this->pod ) ) {
4315
						$actions_disabled['export'] = 'export';
4316
					}
4317
				}//end if
4318
			}//end if
4319
4320
			$_GET[ 'action' . $num ] = pods_v_sanitized( 'action' . $num, 'get', pods_v( 'action', $options, 'manage' ), true );
4321
4322
			$index = $this->pod_data['field_id'];
4323
			$label = __( 'ID', 'pods' );
4324
4325
			if ( isset( $this->pod_data['fields'][ $this->pod_data['field_index'] ] ) ) {
4326
				$index = $this->pod_data['field_index'];
4327
				$label = $this->pod_data['fields'][ $this->pod_data['field_index'] ];
4328
			}
4329
4330
			$manage = array(
4331
				$index => $label,
4332
			);
4333
4334
			if ( isset( $this->pod_data['fields']['modified'] ) ) {
4335
				$manage['modified'] = $this->pod_data['fields']['modified']['label'];
4336
			}
4337
4338
			$manage_fields = (array) pods_v( 'ui_fields_manage', $this->pod_data['options'] );
4339
4340
			if ( ! empty( $manage_fields ) ) {
4341
				$manage_new = array();
4342
4343
				foreach ( $manage_fields as $manage_field ) {
4344
					if ( isset( $this->pod_data['fields'][ $manage_field ] ) ) {
4345
						$manage_new[ $manage_field ] = $this->pod_data['fields'][ $manage_field ];
4346
					} elseif ( isset( $this->pod_data['object_fields'][ $manage_field ] ) ) {
4347
						$manage_new[ $manage_field ] = $this->pod_data['object_fields'][ $manage_field ];
4348
					} elseif ( $manage_field === $this->pod_data['field_id'] ) {
4349
						$field = array(
4350
							'name'  => $manage_field,
4351
							'label' => 'ID',
4352
							'type'  => 'number',
4353
							'width' => '8%',
4354
						);
4355
4356
						$manage_new[ $manage_field ] = PodsForm::field_setup( $field, null, $field['type'] );
4357
					}
4358
				}
4359
4360
				if ( ! empty( $manage_new ) ) {
4361
					$manage = $manage_new;
4362
				}
4363
			}//end if
4364
4365
			$manage = apply_filters( 'pods_admin_ui_fields_' . $this->pod, apply_filters( 'pods_admin_ui_fields', $manage, $this->pod, $this ), $this->pod, $this );
4366
4367
			$icon = pods_v( 'ui_icon', $this->pod_data['options'] );
4368
4369
			if ( ! empty( $icon ) ) {
4370
				$icon = pods_image_url( $icon, '32x32' );
4371
			}
4372
4373
			$filters = pods_v( 'ui_filters', $this->pod_data['options'] );
4374
4375
			if ( ! empty( $filters ) ) {
4376
				$filters_new = array();
4377
4378
				$filters = (array) $filters;
4379
4380
				foreach ( $filters as $filter_field ) {
4381
					if ( isset( $this->pod_data['fields'][ $filter_field ] ) ) {
4382
						$filters_new[ $filter_field ] = $this->pod_data['fields'][ $filter_field ];
4383
					} elseif ( isset( $this->pod_data['object_fields'][ $filter_field ] ) ) {
4384
						$filters_new[ $filter_field ] = $this->pod_data['object_fields'][ $filter_field ];
4385
					}
4386
				}
4387
4388
				$filters = $filters_new;
4389
			}
4390
4391
			$ui = array(
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ui. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4392
				'fields'           => array(
4393
					'manage'    => $manage,
4394
					'add'       => $this->pod_data['fields'],
4395
					'edit'      => $this->pod_data['fields'],
4396
					'duplicate' => $this->pod_data['fields'],
4397
				),
4398
				'icon'             => $icon,
4399
				'actions_disabled' => $actions_disabled,
4400
				'actions_bulk'     => array(),
4401
			);
4402
4403
			if ( ! empty( $filters ) ) {
4404
				$ui['fields']['search'] = $filters;
4405
				$ui['filters']          = array_keys( $filters );
4406
				$ui['filters_enhanced'] = true;
4407
			}
4408
4409
			$reorder_field = pods_v( 'ui_reorder_field', $this->pod_data['options'] );
4410
4411
			if ( ! empty( $reorder_field ) && in_array( 'reorder', $actions_enabled, true ) && ! in_array( 'reorder', $actions_disabled, true ) && ( ( ! empty( $this->pod_data['object_fields'] ) && isset( $this->pod_data['object_fields'][ $reorder_field ] ) ) || isset( $this->pod_data['fields'][ $reorder_field ] ) ) ) {
4412
				$ui['reorder']     = array( 'on' => $reorder_field );
4413
				$ui['orderby']     = $reorder_field;
4414
				$ui['orderby_dir'] = 'ASC';
4415
			}
4416
4417
			if ( ! empty( $author_restrict ) ) {
4418
				$ui['restrict'] = array( 'author_restrict' => $author_restrict );
4419
			}
4420
4421
			if ( ! in_array( 'export', $ui['actions_disabled'], true ) ) {
4422
				$ui['actions_bulk']['export'] = array(
4423
					'label' => __( 'Export', 'pods' ),
4424
					// callback not needed, Pods has this built-in for export.
4425
				);
4426
			}
4427
4428
			if ( ! in_array( 'delete', $ui['actions_disabled'], true ) ) {
4429
				$ui['actions_bulk']['delete'] = array(
4430
					'label' => __( 'Delete', 'pods' ),
4431
					// callback not needed, Pods has this built-in for delete.
4432
				);
4433
			}
4434
4435
			$detail_url = pods_v( 'detail_url', $this->pod_data['options'] );
4436
4437
			if ( 0 < strlen( $detail_url ) ) {
4438
				$ui['actions_custom'] = array(
4439
					'view_url' => array(
4440
						'label' => 'View',
4441
						'link'  => get_site_url() . '/' . $detail_url,
4442
					),
4443
				);
4444
			}
4445
4446
			// @todo Customize the Add New / Manage links to point to their correct menu items.
4447
			$ui = apply_filters( 'pods_admin_ui_' . $this->pod, apply_filters( 'pods_admin_ui', $ui, $this->pod, $this ), $this->pod, $this );
4448
4449
			// Override UI options.
4450
			foreach ( $options as $option => $value ) {
4451
				$ui[ $option ] = $value;
4452
			}
4453
4454
			$this->ui = $ui;
4455
4456
			return pods_ui( $this );
4457
		}//end if
4458
4459
		do_action( 'pods_admin_ui_custom', $this );
4460
		do_action( 'pods_admin_ui_custom_' . $this->pod, $this );
4461
4462
		return null;
4463
	}
4464
4465
	/**
4466
	 * Handle filters / actions for the class
4467
	 *
4468
	 * @see   pods_do_hook
4469
	 *
4470
	 * @param string $name Hook name.
4471
	 *
4472
	 * @return mixed Value filtered
4473
	 *
4474
	 * @since 2.0
4475
	 */
4476
	private function do_hook( $name ) {
4477
4478
		$args = func_get_args();
4479
4480
		if ( empty( $args ) ) {
4481
			return false;
4482
		}
4483
4484
		// Remove first argument.
4485
		array_shift( $args );
4486
4487
		return pods_do_hook( 'pods', $name, $args, $this );
4488
	}
4489
4490
	/**
4491
	 * Handle variables that have been deprecated and PodsData vars
4492
	 *
4493
	 * @param string $name Property name.
4494
	 *
4495
	 * @return mixed
4496
	 *
4497
	 * @since 2.0
4498
	 */
4499
	public function __get( $name ) {
4500
4501
		$name = (string) $name;
4502
4503
		// PodsData vars.
4504
		if ( isset( $this->data->{$name} ) && 0 === strpos( $name, 'field_' ) ) {
4505
			return $this->data->{$name};
4506
		}
4507
4508
		if ( ! $this->deprecated ) {
4509
			require_once PODS_DIR . 'deprecated/classes/Pods.php';
4510
4511
			$this->deprecated = new Pods_Deprecated( $this );
4512
		}
4513
4514
		$var = null;
4515
4516
		$pod_class_exists = class_exists( 'Pod' );
4517
4518
		if ( isset( $this->deprecated->{$name} ) ) {
4519
			if ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4520
				pods_deprecated( "Pods->{$name}", '2.0' );
4521
			}
4522
4523
			$var = $this->deprecated->{$name};
4524
		} elseif ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4525
			pods_deprecated( "Pods->{$name}", '2.0' );
4526
		}
4527
4528
		return $var;
4529
	}
4530
4531
	/**
4532
	 * Handle methods that have been deprecated and any aliasing.
4533
	 *
4534
	 * @param string $name Function name.
4535
	 * @param array  $args Arguments passed to function.
4536
	 *
4537
	 * @return mixed|null
4538
	 *
4539
	 * @since 2.0
4540
	 */
4541
	public function __call( $name, $args ) {
4542
4543
		$name = (string) $name;
4544
4545
		// select > find alias.
4546
		if ( 'select' === $name ) {
4547
			return call_user_func_array( array( $this, 'find' ), $args );
4548
		}
4549
4550
		if ( ! $this->deprecated ) {
4551
			require_once PODS_DIR . 'deprecated/classes/Pods.php';
4552
4553
			$this->deprecated = new Pods_Deprecated( $this );
4554
		}
4555
4556
		$pod_class_exists = class_exists( 'Pod' );
4557
4558
		if ( method_exists( $this->deprecated, $name ) ) {
4559
			return call_user_func_array( array( $this->deprecated, $name ), $args );
4560
			// @codingStandardsIgnoreLine
4561
		} elseif ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4562
			pods_deprecated( "Pods::{$name}", '2.0' );
4563
		}
4564
4565
		return null;
4566
	}
4567
4568
	/**
4569
	 * Handle casting a Pods() object to string
4570
	 *
4571
	 * @return string Pod type and name in CURIE notation
4572
	 */
4573
	public function __toString() {
4574
4575
		$string = '';
4576
4577
		if ( ! empty( $this->pod_data ) ) {
4578
			$string = sprintf( '%s:%s', $this->pod_data['type'], $this->pod_data['name'] );
4579
		}
4580
4581
		return $string;
4582
4583
	}
4584
}
4585