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

Pods::template()   D

Complexity

Conditions 9
Paths 7

Size

Total Lines 116
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 35
nc 7
nop 3
dl 0
loc 116
rs 4.8196
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
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( pods_absint( $this->page, 1 ) );
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 = in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true );
1209
1210
				if ( $simple || ! $is_field_set || ! $is_tableless_field ) {
1211
					if ( null === $params->single ) {
1212
						if ( $is_field_set && $is_tableless_field ) {
1213
							$params->single = true;
1214
						} else {
1215
							$params->single = false;
1216
						}
1217
					}
1218
1219
					$no_conflict = pods_no_conflict_check( $this->pod_data['type'] );
1220
1221
					if ( ! $no_conflict ) {
1222
						pods_no_conflict_on( $this->pod_data['type'] );
1223
					}
1224
1225
					if ( in_array( $this->pod_data['type'], array(
1226
						'post_type',
1227
						'media',
1228
						'taxonomy',
1229
						'user',
1230
						'comment',
1231
					), true ) ) {
1232
						$id = $this->id();
1233
1234
						$metadata_type = $this->pod_data['type'];
1235
1236
						if ( in_array( $this->pod_data['type'], array( 'post_type', 'media' ), true ) ) {
1237
							$metadata_type = 'post';
1238
1239
							// Support for WPML 'duplicated' translation handling.
1240
							if ( did_action( 'wpml_loaded' ) && apply_filters( 'wpml_is_translated_post_type', false, $this->pod_data['name'] ) ) {
1241
								$master_post_id = (int) apply_filters( 'wpml_master_post_from_duplicate', $id );
1242
1243
								if ( 0 < $master_post_id ) {
1244
									$id = $master_post_id;
1245
								}
1246
							}
1247
						} elseif ( 'taxonomy' === $this->pod_data['type'] ) {
1248
							$metadata_type = 'term';
1249
						}
1250
1251
						$value = get_metadata( $metadata_type, $id, $params->name, $params->single );
1252
1253
						$single_multi = 'single';
1254
1255
						if ( isset( $this->fields[ $params->name ] ) ) {
1256
							$single_multi = pods_v( $this->fields[ $params->name ]['type'] . '_format_type', $this->fields[ $params->name ]['options'], 'single' );
1257
						}
1258
1259
						if ( $simple && ! is_array( $value ) && 'single' !== $single_multi ) {
1260
							$value = get_metadata( $metadata_type, $id, $params->name );
1261
						}
1262
					} elseif ( 'settings' === $this->pod_data['type'] ) {
1263
						$value = get_option( $this->pod_data['name'] . '_' . $params->name, null );
1264
					}//end if
1265
1266
					// Handle Simple Relationships.
1267
					if ( $simple ) {
1268
						if ( null === $params->single ) {
1269
							$params->single = false;
1270
						}
1271
1272
						$value = PodsForm::field_method( 'pick', 'simple_value', $params->name, $value, $this->fields[ $params->name ], $this->pod_data, $this->id(), true );
1273
					}
1274
1275
					if ( ! $no_conflict ) {
1276
						pods_no_conflict_off( $this->pod_data['type'] );
1277
					}
1278
				} else {
1279
					// Dot-traversal.
1280
					$pod        = $this->pod;
1281
					$ids        = array( $this->id() );
1282
					$all_fields = array();
1283
1284
					$lookup = $params->traverse;
1285
1286
					// Get fields matching traversal names.
1287
					if ( ! empty( $lookup ) ) {
1288
						$fields = $this->api->load_fields( array(
1289
							'name'          => $lookup,
1290
							'type'          => $tableless_field_types,
1291
							'object_fields' => true,
1292
							// @todo support object fields too.
1293
						) );
1294
1295
						if ( ! empty( $fields ) ) {
1296
							foreach ( $fields as $field ) {
1297
								if ( ! empty( $field ) ) {
1298
									if ( ! isset( $all_fields[ $field['pod'] ] ) ) {
1299
										$all_fields[ $field['pod'] ] = array();
1300
									}
1301
1302
									$all_fields[ $field['pod'] ][ $field['name'] ] = $field;
1303
								}
1304
							}
1305
						}
1306
1307
						if ( ! empty( $this->pod_data['object_fields'] ) ) {
1308
							$object_fields = (array) $this->pod_data['object_fields'];
1309
1310
							foreach ( $object_fields as $object_field => $object_field_opt ) {
1311
								if ( in_array( $object_field_opt['type'], $tableless_field_types, true ) ) {
1312
									$all_fields[ $this->pod ][ $object_field ] = $object_field_opt;
1313
								}
1314
							}
1315
						}
1316
					}//end if
1317
1318
					$last_type     = '';
1319
					$last_object   = '';
1320
					$last_pick_val = '';
1321
1322
					$single_multi = pods_v( $this->fields[ $params->name ]['type'] . '_format_type', $this->fields[ $params->name ]['options'], 'single' );
1323
1324
					if ( 'multi' === $single_multi ) {
1325
						$limit = (int) pods_v( $this->fields[ $params->name ]['type'] . '_limit', $this->fields[ $params->name ]['options'], 0 );
1326
					} else {
1327
						$limit = 1;
1328
					}
1329
1330
					// Loop through each traversal level.
1331
					foreach ( $params->traverse as $key => $field ) {
1332
						$last_loop = false;
1333
1334
						if ( count( $params->traverse ) <= ( $key + 1 ) ) {
1335
							$last_loop = true;
1336
						}
1337
1338
						$field_exists = isset( $all_fields[ $pod ][ $field ] );
1339
1340
						$simple       = false;
1341
						$last_options = array();
1342
1343
						if ( $field_exists && 'pick' === $all_fields[ $pod ][ $field ]['type'] && in_array( $all_fields[ $pod ][ $field ]['pick_object'], $simple_tableless_objects, true ) ) {
1344
							$simple       = true;
1345
							$last_options = $all_fields[ $pod ][ $field ];
1346
						}
1347
1348
						// Tableless handler.
1349
						if ( $field_exists && ( ! $simple || ! in_array( $all_fields[ $pod ][ $field ]['type'], array(
1350
							'pick',
1351
							'taxonomy',
1352
							'comment',
1353
						), true ) ) ) {
1354
							$type        = $all_fields[ $pod ][ $field ]['type'];
1355
							$pick_object = $all_fields[ $pod ][ $field ]['pick_object'];
1356
							$pick_val    = $all_fields[ $pod ][ $field ]['pick_val'];
1357
1358
							if ( 'table' === $pick_object ) {
1359
								$pick_val = pods_v( 'pick_table', $all_fields[ $pod ][ $field ]['options'], $pick_val, true );
1360
							} elseif ( '__current__' === $pick_val ) {
1361
								$pick_val = $pod;
1362
							}
1363
1364
							$last_limit = 0;
1365
1366
							if ( in_array( $type, $tableless_field_types, true ) ) {
1367
								$single_multi = pods_v( "{$type}_format_type", $all_fields[ $pod ][ $field ]['options'], 'single' );
1368
1369
								if ( 'multi' === $single_multi ) {
1370
									$last_limit = (int) pods_v( "{$type}_limit", $all_fields[ $pod ][ $field ]['options'], 0 );
1371
								} else {
1372
									$last_limit = 1;
1373
								}
1374
							}
1375
1376
							$last_type     = $type;
1377
							$last_object   = $pick_object;
1378
							$last_pick_val = $pick_val;
1379
							$last_options  = $all_fields[ $pod ][ $field ];
1380
1381
							// Temporary hack until there's some better handling here.
1382
							$last_limit *= count( $ids );
1383
1384
							// Get related IDs.
1385
							if ( ! isset( $all_fields[ $pod ][ $field ]['pod_id'] ) ) {
1386
								$all_fields[ $pod ][ $field ]['pod_id'] = 0;
1387
							}
1388
1389
							if ( isset( $all_fields[ $pod ][ $field ]['id'] ) ) {
1390
								$ids = $this->api->lookup_related_items( $all_fields[ $pod ][ $field ]['id'], $all_fields[ $pod ][ $field ]['pod_id'], $ids, $all_fields[ $pod ][ $field ] );
1391
							}
1392
1393
							// No items found.
1394
							if ( empty( $ids ) ) {
1395
								return false;
1396
							} elseif ( 0 < $last_limit ) {
1397
								// @todo This should return array() if not $params->single.
1398
								$ids = array_slice( $ids, 0, $last_limit );
1399
							}
1400
1401
							// Get $pod if related to a Pod.
1402
							if ( ! empty( $pick_object ) && ( ! empty( $pick_val ) || in_array( $pick_object, array(
1403
								'user',
1404
								'media',
1405
								'comment',
1406
							), true ) ) ) {
1407
								if ( 'pod' === $pick_object ) {
1408
									$pod = $pick_val;
1409
								} else {
1410
									$check = $this->api->get_table_info( $pick_object, $pick_val );
1411
1412
									if ( ! empty( $check ) && ! empty( $check['pod'] ) ) {
1413
										$pod = $check['pod']['name'];
1414
									}
1415
								}
1416
							}
1417
						} else {
1418
							// Assume last iteration.
1419
							if ( 0 === $key ) {
1420
								// Invalid field.
1421
								return false;
1422
							}
1423
1424
							$last_loop = true;
1425
						}//end if
1426
1427
						if ( $last_loop ) {
1428
							$object_type = $last_object;
1429
							$object      = $last_pick_val;
1430
1431
							if ( in_array( $last_type, PodsForm::file_field_types(), true ) ) {
1432
								$object_type = 'media';
1433
								$object      = 'attachment';
1434
							}
1435
1436
							$data = array();
1437
1438
							$table = $this->api->get_table_info( $object_type, $object, null, null, $last_options );
1439
1440
							$join  = array();
1441
							$where = array();
1442
1443
							if ( ! empty( $table['join'] ) ) {
1444
								$join = (array) $table['join'];
1445
							}
1446
1447
							if ( ! empty( $ids ) || ! empty( $table['where'] ) ) {
1448
								foreach ( $ids as $id ) {
1449
									$where[ $id ] = '`t`.`' . $table['field_id'] . '` = ' . (int) $id;
1450
								}
1451
1452
								if ( ! empty( $where ) ) {
1453
									$where = array( implode( ' OR ', $where ) );
1454
								}
1455
1456
								if ( ! empty( $table['where'] ) ) {
1457
									// @codingStandardsIgnoreLine.
1458
									$where = array_merge( $where, array_values( (array) $table['where'] ) );
1459
								}
1460
							}
1461
1462
							/**
1463
							 * Related object.
1464
							 *
1465
							 * @var $related_obj Pods|false
1466
							 */
1467
							$related_obj = false;
1468
1469
							// Check if we can return the full object/array or if we need to traverse into it.
1470
							$is_field_output_full = false;
1471
1472
							if ( false !== $field_exists && ( in_array( $last_type, $tableless_field_types, true ) && ! $simple ) ) {
1473
								$is_field_output_full = true;
1474
							}
1475
1476
							if ( 'pod' === $object_type ) {
1477
								$related_obj = pods( $object, null, false );
1478
							} elseif ( ! empty( $table['pod'] ) ) {
1479
								$related_obj = pods( $table['pod']['name'], null, false );
1480
							}
1481
1482
							if ( $related_obj || ! empty( $table['table'] ) ) {
1483
								$sql = array(
1484
									'select'     => '*, `t`.`' . $table['field_id'] . '` AS `pod_item_id`',
1485
									'table'      => $table['table'],
1486
									'join'       => $join,
1487
									'where'      => $where,
1488
									'orderby'    => $params->orderby,
1489
									'pagination' => false,
1490
									'search'     => false,
1491
									'limit'      => - 1,
1492
									'expires'    => 180,
1493
									// @todo This could potentially cause issues if someone changes the data within this time and persistent storage is used.
1494
								);
1495
1496
								if ( ! empty( $table['where_default'] ) ) {
1497
									$sql['where_default'] = $table['where_default'];
1498
								}
1499
1500
								// Output types.
1501
								if ( in_array( $params->output, array( 'ids', 'objects', 'pods' ), true ) ) {
1502
									$sql['select'] = '`t`.`' . $table['field_id'] . '` AS `pod_item_id`';
1503
								} elseif ( 'names' === $params->output && ! empty( $table['field_index'] ) ) {
1504
									$sql['select'] = '`t`.`' . $table['field_index'] . '` AS `pod_item_index`, `t`.`' . $table['field_id'] . '` AS `pod_item_id`';
1505
								}
1506
1507
								if ( ! empty( $params->params ) && is_array( $params->params ) ) {
1508
									$where = $sql['where'];
1509
1510
									// @codingStandardsIgnoreLine.
1511
									$sql = array_merge( $sql, $params->params );
1512
1513
									if ( isset( $params->params['where'] ) ) {
1514
										// @codingStandardsIgnoreLine.
1515
										$sql['where'] = array_merge( (array) $where, (array) $params->params['where'] );
1516
									}
1517
								}
1518
1519
								$item_data = array();
1520
1521
								if ( ! $related_obj ) {
1522
									if ( ! is_object( $this->alt_data ) ) {
1523
										$this->alt_data = pods_data( null, 0, true, true );
1524
									}
1525
1526
									$item_data = $this->alt_data->select( $sql );
1527
								} else {
1528
									// Support 'find' output ordering.
1529
									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...
1530
										// Handle default orderby for ordering by the IDs.
1531
										$order_ids = implode( ', ', array_map( 'absint', $ids ) );
1532
1533
										$sql['orderby'] = 'FIELD( `t`.`' . $table['field_id'] . '`, ' . $order_ids . ' )';
1534
									}
1535
1536
									$related_obj->find( $sql );
1537
1538
									// Support 'find' output.
1539
									if ( 'find' === $params->output && $is_field_output_full ) {
1540
										$data = $related_obj;
1541
1542
										$is_field_output_full = true;
1543
									} else {
1544
										$item_data = $related_obj->data();
1545
									}
1546
								}//end if
1547
1548
								$items = array();
1549
1550
								if ( ! empty( $item_data ) ) {
1551
									foreach ( $item_data as $item ) {
1552
										if ( is_array( $item ) ) {
1553
											$item = (object) $item;
1554
										}
1555
1556
										if ( empty( $item->pod_item_id ) ) {
1557
											continue;
1558
										}
1559
1560
										// Bypass pass field.
1561
										if ( isset( $item->user_pass ) ) {
1562
											unset( $item->user_pass );
1563
										}
1564
1565
										// Get Item ID.
1566
										$item_id = $item->pod_item_id;
1567
1568
										// Output types.
1569
										if ( 'ids' === $params->output ) {
1570
											$item = (int) $item_id;
1571
										} elseif ( 'names' === $params->output && ! empty( $table['field_index'] ) ) {
1572
											$item = $item->pod_item_index;
1573
										} elseif ( 'objects' === $params->output ) {
1574
											if ( in_array( $object_type, array( 'post_type', 'media' ), true ) ) {
1575
												$item = get_post( $item_id );
1576
											} elseif ( 'taxonomy' === $object_type ) {
1577
												$item = get_term( $item_id, $object );
1578
											} elseif ( 'user' === $object_type ) {
1579
												$item = get_userdata( $item_id );
1580
1581
												if ( ! empty( $item ) ) {
1582
													// Get other vars.
1583
													$roles   = $item->roles;
1584
													$caps    = $item->caps;
1585
													$allcaps = $item->allcaps;
1586
1587
													$item = $item->data;
1588
1589
													// Set other vars.
1590
													$item->roles   = $roles;
1591
													$item->caps    = $caps;
1592
													$item->allcaps = $allcaps;
1593
1594
													unset( $item->user_pass );
1595
												}
1596
											} elseif ( 'comment' === $object_type ) {
1597
												$item = get_comment( $item_id );
1598
											} else {
1599
												$item = (object) $item;
1600
											}//end if
1601
										} elseif ( 'pods' === $params->output ) {
1602
											if ( in_array( $object_type, array( 'user', 'media' ), true ) ) {
1603
												$item = pods( $object_type, (int) $item_id );
1604
											} else {
1605
												$item = pods( $object, (int) $item_id );
1606
											}
1607
										} else {
1608
											// arrays.
1609
											$item = get_object_vars( (object) $item );
1610
										}//end if
1611
1612
										// Pass item data into $data.
1613
										$items[ $item_id ] = $item;
1614
									}//end foreach
1615
1616
									// Cleanup.
1617
									unset( $item_data );
1618
1619
									// Return all of the data in the order expected.
1620
									if ( empty( $params->orderby ) ) {
1621
										foreach ( $ids as $id ) {
1622
											if ( isset( $items[ $id ] ) ) {
1623
												$data[ $id ] = $items[ $id ];
1624
											}
1625
										}
1626
									} else {
1627
										// Use order set by orderby.
1628
										foreach ( $items as $id => $v ) {
1629
											// @codingStandardsIgnoreLine.
1630
											if ( in_array( $id, $ids ) ) {
1631
												$data[ $id ] = $v;
1632
											}
1633
										}
1634
									}
1635
								}//end if
1636
							}//end if
1637
1638
							if ( in_array( $last_type, $tableless_field_types, true ) || in_array( $last_type, array(
1639
								'boolean',
1640
								'number',
1641
								'currency',
1642
							), true ) ) {
1643
								$params->raw = true;
1644
							}
1645
1646
							if ( empty( $data ) ) {
1647
								$value = false;
1648
							} else {
1649
								$object_type = $table['type'];
1650
1651
								if ( in_array( $table['type'], array( 'post_type', 'attachment', 'media' ), true ) ) {
1652
									$object_type = 'post';
1653
								}
1654
1655
								$no_conflict = true;
1656
1657
								if ( in_array( $object_type, array(
1658
									'post',
1659
									'taxonomy',
1660
									'user',
1661
									'comment',
1662
									'settings',
1663
								), true ) ) {
1664
									$no_conflict = pods_no_conflict_check( $object_type );
1665
1666
									if ( ! $no_conflict ) {
1667
										pods_no_conflict_on( $object_type );
1668
									}
1669
								}
1670
1671
								if ( $is_field_output_full ) {
1672
									// Return entire array.
1673
									$value = $data;
1674
								} else {
1675
									// Return an array of single column values.
1676
									$value = array();
1677
1678
									foreach ( $data as $item_id => $item ) {
1679
										// $field is 123x123, needs to be _src.123x123
1680
										$full_field = implode( '.', array_splice( $params->traverse, $key ) );
1681
1682
										if ( is_array( $item ) && isset( $item[ $field ] ) ) {
1683
											if ( $table['field_id'] === $field ) {
1684
												$value[] = (int) $item[ $field ];
1685
											} else {
1686
												$value[] = $item[ $field ];
1687
											}
1688
										} elseif ( is_object( $item ) && isset( $item->{$field} ) ) {
1689
											if ( $table['field_id'] === $field ) {
1690
												$value[] = (int) $item->{$field};
1691
											} else {
1692
												$value[] = $item->{$field};
1693
											}
1694
										} elseif ( ( ( false !== strpos( $full_field, '_src' ) || 'guid' === $field ) && ( in_array( $table['type'], array(
1695
											'attachment',
1696
											'media',
1697
										), true ) || in_array( $last_type, PodsForm::file_field_types(), true ) ) ) || ( in_array( $field, array(
1698
											'_link',
1699
											'detail_url',
1700
										), true ) || in_array( $field, array(
1701
											'permalink',
1702
											'the_permalink',
1703
										), true ) && in_array( $last_type, PodsForm::file_field_types(), true ) ) ) {
1704
											// @todo Refactor the above condition statement.
1705
											$size = 'full';
1706
1707
											if ( false === strpos( 'image', get_post_mime_type( $item_id ) ) ) {
1708
												// No default sizes for non-images.
1709
												// When a size is defined this will be overwritten.
1710
												$size = null;
1711
											}
1712
1713
											if ( false !== strpos( $full_field, '_src.' ) && 5 < strlen( $full_field ) ) {
1714
												$size = substr( $full_field, 5 );
1715
											} elseif ( false !== strpos( $full_field, '_src_relative.' ) && 14 < strlen( $full_field ) ) {
1716
												$size = substr( $full_field, 14 );
1717
											} elseif ( false !== strpos( $full_field, '_src_schemeless.' ) && 16 < strlen( $full_field ) ) {
1718
												$size = substr( $full_field, 16 );
1719
											}
1720
1721
											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...
1722
												$value_url = pods_image_url( $item_id, $size );
1723
											} else {
1724
												$value_url = wp_get_attachment_url( $item_id );
1725
											}
1726
1727
											if ( ! empty( $value_url ) ) {
1728
												if ( false !== strpos( $full_field, '_src_relative' ) ) {
1729
													$value_url_parsed = wp_parse_url( $value_url );
1730
													$value_url        = $value_url_parsed['path'];
1731
												} elseif ( false !== strpos( $full_field, '_src_schemeless' ) ) {
1732
													$value_url = str_replace( array(
1733
														'http://',
1734
														'https://',
1735
													), '//', $value_url );
1736
												}
1737
											}
1738
1739
											if ( ! empty( $value_url ) ) {
1740
												$value[] = $value_url;
1741
											}
1742
1743
											$params->raw_display = true;
1744
										} elseif ( false !== strpos( $full_field, '_img' ) && ( in_array( $table['type'], array(
1745
											'attachment',
1746
											'media',
1747
										), true ) || in_array( $last_type, PodsForm::file_field_types(), true ) ) ) {
1748
											$size = 'full';
1749
1750
											if ( false !== strpos( $full_field, '_img.' ) && 5 < strlen( $full_field ) ) {
1751
												$size = substr( $full_field, 5 );
1752
											}
1753
1754
											$value[] = pods_image( $item_id, $size );
1755
1756
											$params->raw_display = true;
1757
										} elseif ( in_array( $field, array(
1758
											'_link',
1759
											'detail_url',
1760
										), true ) || in_array( $field, array( 'permalink', 'the_permalink' ), true ) ) {
1761
											if ( 'pod' === $object_type ) {
1762
												if ( is_object( $related_obj ) ) {
1763
													$related_obj->fetch( $item_id );
1764
1765
													$value[] = $related_obj->field( 'detail_url' );
1766
												} else {
1767
													$value[] = '';
1768
												}
1769
											} elseif ( 'post' === $object_type ) {
1770
												$value[] = get_permalink( $item_id );
1771
											} elseif ( 'taxonomy' === $object_type ) {
1772
												$value[] = get_term_link( $item_id, $object );
1773
											} elseif ( 'user' === $object_type ) {
1774
												$value[] = get_author_posts_url( $item_id );
1775
											} elseif ( 'comment' === $object_type ) {
1776
												$value[] = get_comment_link( $item_id );
1777
											} else {
1778
												$value[] = '';
1779
											}
1780
1781
											$params->raw_display = true;
1782
										} elseif ( in_array( $object_type, array(
1783
											'post',
1784
											'taxonomy',
1785
											'user',
1786
											'comment',
1787
										), true ) ) {
1788
											$metadata_object_id = $item_id;
1789
1790
											$metadata_type = $object_type;
1791
1792
											if ( 'post' === $object_type ) {
1793
												// Support for WPML 'duplicated' translation handling.
1794
												if ( did_action( 'wpml_loaded' ) && apply_filters( 'wpml_is_translated_post_type', false, $object ) ) {
1795
													$master_post_id = (int) apply_filters( 'wpml_master_post_from_duplicate', $metadata_object_id );
1796
1797
													if ( 0 < $master_post_id ) {
1798
														$metadata_object_id = $master_post_id;
1799
													}
1800
												}
1801
											} elseif ( 'taxonomy' === $object_type ) {
1802
												$metadata_type = 'term';
1803
											}
1804
1805
											$value[] = get_metadata( $metadata_type, $metadata_object_id, $field, true );
1806
										} elseif ( 'settings' === $object_type ) {
1807
											$value[] = get_option( $object . '_' . $field );
1808
										}//end if
1809
									}//end foreach
1810
								}//end if
1811
1812
								if ( ! $no_conflict && in_array( $object_type, array(
1813
									'post',
1814
									'taxonomy',
1815
									'user',
1816
									'comment',
1817
									'settings',
1818
								), true ) ) {
1819
									pods_no_conflict_off( $object_type );
1820
								}
1821
1822
								// Handle Simple Relationships.
1823
								if ( $simple ) {
1824
									if ( null === $params->single ) {
1825
										$params->single = false;
1826
									}
1827
1828
									$value = PodsForm::field_method( 'pick', 'simple_value', $field, $value, $last_options, $all_fields[ $pod ], 0, true );
1829
								} elseif ( false === $params->in_form && ! empty( $value ) && is_array( $value ) ) {
1830
									$value = array_values( $value );
1831
								}
1832
1833
								// Return a single column value.
1834
								if ( false === $params->in_form && 1 === $limit && ! empty( $value ) && is_array( $value ) && 1 === count( $value ) ) {
1835
									$value = current( $value );
1836
								}
1837
							}//end if
1838
1839
							if ( $last_options ) {
1840
								$last_field_data = $last_options;
1841
							}
1842
1843
							break;
1844
						}//end if
1845
					}//end foreach
1846
				}//end if
1847
			}//end if
1848
		}//end if
1849
1850
		if ( ! empty( $params->traverse ) && 1 < count( $params->traverse ) ) {
1851
			$field_names = implode( '.', $params->traverse );
1852
1853
			$this->row[ $field_names ] = $value;
1854
		} elseif ( 'arrays' !== $params->output && in_array( $field_data['type'], $tableless_field_types, true ) ) {
1855
			$this->row[ '_' . $params->output . '_' . $params->full_name ] = $value;
1856
		} elseif ( 'arrays' === $params->output || ! in_array( $field_data['type'], $tableless_field_types, true ) ) {
1857
			$this->row[ $params->full_name ] = $value;
1858
		}
1859
1860
		if ( $params->single && is_array( $value ) && 1 === count( $value ) ) {
1861
			$value = current( $value );
1862
		}
1863
1864
		if ( ! empty( $last_field_data ) ) {
1865
			$field_data = $last_field_data;
1866
		}
1867
1868
		// @todo Expand this into traversed fields too.
1869
		if ( ! empty( $field_data ) && ( $params->display || ! $params->raw ) && ! $params->in_form && ! $params->raw_display ) {
1870
			if ( $params->display || ( ( $params->get_meta || $params->deprecated ) && ! in_array( $field_data['type'], $tableless_field_types, true ) ) ) {
1871
				$field_data['options'] = pods_v( 'options', $field_data, array(), true );
1872
1873
				$post_temp   = false;
1874
				$old_post    = null;
1875
				$old_post_id = null;
1876
1877
				if ( empty( $GLOBALS['post'] ) && 'post_type' === pods_v( 'type', $this->pod_data ) && 0 < $this->id() ) {
1878
					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...
1879
1880
					$post_temp = true;
1881
1882
					$old_post    = $post;
1883
					$old_post_id = $post_ID;
1884
1885
					// @codingStandardsIgnoreLine.
1886
					$post    = get_post( $this->id() );
1887
					// @codingStandardsIgnoreLine.
1888
					$post_ID = $this->id();
1889
				}
1890
1891
				$filter = pods_v( 'display_filter', $field_data['options'] );
1892
1893
				if ( 0 < strlen( $filter ) ) {
1894
					$args = array(
1895
						$filter,
1896
						$value,
1897
					);
1898
1899
					$filter_args = pods_v( 'display_filter_args', $field_data['options'] );
1900
1901
					if ( ! empty( $filter_args ) ) {
1902
						$args = array_merge( $args, compact( $filter_args ) );
1903
					}
1904
1905
					$value = call_user_func_array( 'apply_filters', $args );
1906
				} elseif ( 1 === (int) pods_v( 'display_process', $field_data['options'], 1 ) ) {
1907
					$value = PodsForm::display( $field_data['type'], $value, $params->name, array_merge( $field_data, $field_data['options'] ), $this->pod_data, $this->id() );
1908
				}
1909
1910
				if ( $post_temp ) {
1911
					// @codingStandardsIgnoreLine.
1912
					$post    = $old_post;
1913
					// @codingStandardsIgnoreLine.
1914
					$post_ID = $old_post_id;
1915
				}
1916
			} else {
1917
				$value = PodsForm::value( $field_data['type'], $value, $params->name, array_merge( $field_data, $field_data['options'] ), $this->pod_data, $this->id() );
1918
			}//end if
1919
		}//end if
1920
1921
		/**
1922
		 * Modify value returned by field() directly before output.
1923
		 *
1924
		 * Will not run if value was null
1925
		 *
1926
		 * @since unknown
1927
		 *
1928
		 * @param array|string|null $value  Value to be returned.
1929
		 * @param array|object      $row    Current row being outputted.
1930
		 * @param array             $params Params array passed to field().
1931
		 * @param object|Pods       $this   Current Pods object.
1932
		 */
1933
		$value = apply_filters( 'pods_pods_field', $value, $this->row, $params, $this );
1934
1935
		return $value;
1936
	}
1937
1938
	/**
1939
	 * Check if an item field has a specific value in it
1940
	 *
1941
	 * @param string $field Field name.
1942
	 * @param mixed  $value Value to check.
1943
	 * @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...
1944
	 *
1945
	 * @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...
1946
	 *
1947
	 * @since 2.3.3
1948
	 */
1949
	public function has( $field, $value, $id = null ) {
1950
1951
		$pod =& $this;
1952
1953
		if ( null === $id ) {
1954
			$id = $this->id();
1955
			// @codingStandardsIgnoreLine.
1956
		} elseif ( $id != $this->id() ) {
1957
			$pod = pods( $this->pod, $id );
1958
		}
1959
1960
		$this->do_hook( 'has', $field, $value, $id );
1961
1962
		if ( ! isset( $this->fields[ $field ] ) ) {
1963
			return false;
1964
		}
1965
1966
		// Tableless fields.
1967
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
1968
			if ( ! is_array( $value ) ) {
1969
				$value = explode( ',', $value );
1970
			}
1971
1972
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
1973
				$current_value = $pod->raw( $field );
1974
1975
				if ( ! empty( $current_value ) ) {
1976
					$current_value = (array) $current_value;
1977
				}
1978
1979
				foreach ( $current_value as $v ) {
1980
					// @codingStandardsIgnoreLine.
1981
					if ( in_array( $v, $value ) ) {
1982
						return true;
1983
					}
1984
				}
1985
			} else {
1986
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
1987
1988
				foreach ( $value as $k => $v ) {
1989
					if ( ! preg_match( '/[^\D]/', $v ) ) {
1990
						$value[ $k ] = (int) $v;
1991
					}
1992
1993
					// @todo Convert slugs into IDs.
1994
				}
1995
1996
				foreach ( $related_ids as $v ) {
1997
					// @codingStandardsIgnoreLine.
1998
					if ( in_array( $v, $value ) ) {
1999
						return true;
2000
					}
2001
				}
2002
			}//end if
2003
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
2004
			// Text fields.
2005
			$current_value = $pod->raw( $field );
2006
2007
			if ( 0 < strlen( $current_value ) ) {
2008
				return stripos( $current_value, $value );
2009
			}
2010
		} else {
2011
			// All other fields.
2012
			return $this->is( $field, $value, $id );
2013
		}//end if
2014
2015
		return false;
2016
	}
2017
2018
	/**
2019
	 * Check if an item field is a specific value
2020
	 *
2021
	 * @param string $field Field name.
2022
	 * @param mixed  $value Value to check.
2023
	 * @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...
2024
	 *
2025
	 * @return bool Whether the value was found
2026
	 *
2027
	 * @since 2.3.3
2028
	 */
2029
	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...
2030
2031
		$pod =& $this;
2032
2033
		if ( null === $id ) {
2034
			$id = $this->id();
2035
			// @codingStandardsIgnoreLine.
2036
		} elseif ( $id != $this->id() ) {
2037
			$pod = pods( $this->pod, $id );
2038
		}
2039
2040
		$this->do_hook( 'is', $field, $value, $id );
2041
2042
		if ( ! isset( $this->fields[ $field ] ) ) {
2043
			return false;
2044
		}
2045
2046
		// Tableless fields.
2047
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
2048
			if ( ! is_array( $value ) ) {
2049
				$value = explode( ',', $value );
2050
			}
2051
2052
			$current_value = array();
2053
2054
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
2055
				$current_value = $pod->raw( $field );
2056
2057
				if ( ! empty( $current_value ) ) {
2058
					$current_value = (array) $current_value;
2059
				}
2060
2061
				foreach ( $current_value as $v ) {
2062
					// @codingStandardsIgnoreLine.
2063
					if ( in_array( $v, $value ) ) {
2064
						return true;
2065
					}
2066
				}
2067
			} else {
2068
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
2069
2070
				foreach ( $value as $k => $v ) {
2071
					if ( ! preg_match( '/[^\D]/', $v ) ) {
2072
						$value[ $k ] = (int) $v;
2073
					}
2074
2075
					// @todo Convert slugs into IDs.
2076
				}
2077
2078
				foreach ( $related_ids as $v ) {
2079
					// @codingStandardsIgnoreLine.
2080
					if ( in_array( $v, $value ) ) {
2081
						return true;
2082
					}
2083
				}
2084
			}//end if
2085
2086
			if ( ! empty( $current_value ) ) {
2087
				$current_value = array_filter( array_unique( $current_value ) );
2088
			} else {
2089
				$current_value = array();
2090
			}
2091
2092
			if ( ! empty( $value ) ) {
2093
				$value = array_filter( array_unique( $value ) );
2094
			} else {
2095
				$value = array();
2096
			}
2097
2098
			sort( $current_value );
2099
			sort( $value );
2100
2101
			if ( $value === $current_value ) {
2102
				return true;
2103
			}
2104
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
2105
			// Number fields.
2106
			$current_value = $pod->raw( $field );
2107
2108
			if ( (float) $current_value === (float) $value ) {
2109
				return true;
2110
			}
2111
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
2112
			// Date fields.
2113
			$current_value = $pod->raw( $field );
2114
2115
			if ( 0 < strlen( $current_value ) ) {
2116
				if ( strtotime( $current_value ) === strtotime( $value ) ) {
2117
					return true;
2118
				}
2119
			} elseif ( empty( $value ) ) {
2120
				return true;
2121
			}
2122
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
2123
			// Text fields.
2124
			$current_value = $pod->raw( $field );
2125
2126
			if ( (string) $current_value === (string) $value ) {
2127
				return true;
2128
			}
2129
		} else {
2130
			// All other fields.
2131
			$current_value = $pod->raw( $field );
2132
2133
			if ( $current_value === $value ) {
2134
				return true;
2135
			}
2136
		}//end if
2137
2138
		return false;
2139
	}
2140
2141
	/**
2142
	 * Return the item ID
2143
	 *
2144
	 * @return int
2145
	 * @since 2.0
2146
	 */
2147
	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...
2148
2149
		if ( isset( $this->data->row, $this->data->row['id'] ) ) {
2150
			// If we already have data loaded return that ID.
2151
			return $this->data->row['id'];
2152
		}
2153
2154
		return $this->field( $this->data->field_id );
2155
	}
2156
2157
	/**
2158
	 * Return the previous item ID, loops at the last id to return the first
2159
	 *
2160
	 * @param int|null          $id              ID to start from.
2161
	 * @param array|object|null $params_override Override the find() parameters.
2162
	 *
2163
	 * @return int
2164
	 * @since 2.0
2165
	 */
2166
	public function prev_id( $id = null, $params_override = null ) {
2167
2168
		if ( null === $id ) {
2169
			$id = $this->id();
2170
		}
2171
2172
		$id = (int) $id;
2173
2174
		$params = array(
2175
			'select'  => "`t`.`{$this->data->field_id}`",
2176
			'where'   => "`t`.`{$this->data->field_id}` < {$id}",
2177
			'orderby' => "`t`.`{$this->data->field_id}` DESC",
2178
			'limit'   => 1,
2179
		);
2180
2181
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2182
			if ( ! empty( $params_override ) ) {
2183
				$params = $params_override;
2184
			} elseif ( ! empty( $this->params ) ) {
2185
				$params = $this->params;
2186
			}
2187
2188
			if ( is_object( $params ) ) {
2189
				$params = get_object_vars( $params );
2190
			}
2191
2192
			if ( 0 < $id ) {
2193
				if ( isset( $params['where'] ) && ! empty( $params['where'] ) ) {
2194
					$params['where']   = (array) $params['where'];
2195
					$params['where'][] = "`t`.`{$this->data->field_id}` < {$id}";
2196
				} else {
2197
					$params['where'] = "`t`.`{$this->data->field_id}` < {$id}";
2198
				}
2199
			} elseif ( isset( $params['offset'] ) && 0 < $params['offset'] ) {
2200
				$params['offset'] --;
2201
			} elseif ( 0 < $this->row_number && ! isset( $params['offset'] ) && ! empty( $this->params ) ) {
2202
				$params['offset'] = $this->row_number - 1;
2203
			} else {
2204
				return 0;
2205
			}
2206
2207
			if ( isset( $params['orderby'] ) && ! empty( $params['orderby'] ) ) {
2208
				if ( is_array( $params['orderby'] ) ) {
2209
					foreach ( $params['orderby'] as $orderby => $dir ) {
2210
						$dir = strtoupper( $dir );
2211
2212
						if ( ! in_array( $dir, array( 'ASC', 'DESC' ), true ) ) {
2213
							continue;
2214
						}
2215
2216
						if ( 'ASC' === $dir ) {
2217
							$params['orderby'][ $orderby ] = 'DESC';
2218
						} else {
2219
							$params['orderby'][ $orderby ] = 'ASC';
2220
						}
2221
					}
2222
2223
					$params['orderby'][ $this->data->field_id ] = 'DESC';
2224
				} elseif ( "`t`.`{$this->data->field_id}` DESC" !== $params['orderby'] ) {
2225
					$params['orderby'] .= ", `t`.`{$this->data->field_id}` DESC";
2226
				}
2227
			}//end if
2228
2229
			$params['select'] = "`t`.`{$this->data->field_id}`";
2230
			$params['limit']  = 1;
2231
		}//end if
2232
2233
		$pod = pods( $this->pod, $params );
2234
2235
		$new_id = 0;
2236
2237
		if ( $pod->fetch() ) {
2238
			$new_id = $pod->id();
2239
		}
2240
2241
		$new_id = $this->do_hook( 'prev_id', $new_id, $id, $pod, $params_override );
2242
2243
		return $new_id;
2244
	}
2245
2246
	/**
2247
	 * Return the next item ID, loops at the first id to return the last
2248
	 *
2249
	 * @param int|null          $id              ID to start from.
2250
	 * @param array|object|null $params_override Override the find() parameters.
2251
	 *
2252
	 * @return int
2253
	 * @since 2.0
2254
	 */
2255
	public function next_id( $id = null, $params_override = null ) {
2256
2257
		if ( null === $id ) {
2258
			$id = $this->id();
2259
		}
2260
2261
		$id = (int) $id;
2262
2263
		$params = array(
2264
			'select'  => "`t`.`{$this->data->field_id}`",
2265
			'where'   => "{$id} < `t`.`{$this->data->field_id}`",
2266
			'orderby' => "`t`.`{$this->data->field_id}` ASC",
2267
			'limit'   => 1,
2268
		);
2269
2270
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2271
			if ( ! empty( $params_override ) ) {
2272
				$params = $params_override;
2273
			} elseif ( ! empty( $this->params ) ) {
2274
				$params = $this->params;
2275
			}
2276
2277
			if ( is_object( $params ) ) {
2278
				$params = get_object_vars( $params );
2279
			}
2280
2281
			if ( 0 < $id ) {
2282
				if ( isset( $params['where'] ) && ! empty( $params['where'] ) ) {
2283
					$params['where']   = (array) $params['where'];
2284
					$params['where'][] = "{$id} < `t`.`{$this->data->field_id}`";
2285
				} else {
2286
					$params['where'] = "{$id} < `t`.`{$this->data->field_id}`";
2287
				}
2288
			} elseif ( ! isset( $params['offset'] ) ) {
2289
				if ( ! empty( $this->params ) && - 1 < $this->row_number ) {
2290
					$params['offset'] = $this->row_number + 1;
2291
				} else {
2292
					$params['offset'] = 1;
2293
				}
2294
			} else {
2295
				$params['offset'] ++;
2296
			}
2297
2298
			$params['select'] = "`t`.`{$this->data->field_id}`";
2299
			$params['limit']  = 1;
2300
		}//end if
2301
2302
		$pod = pods( $this->pod, $params );
2303
2304
		$new_id = 0;
2305
2306
		if ( $pod->fetch() ) {
2307
			$new_id = $pod->id();
2308
		}
2309
2310
		$new_id = $this->do_hook( 'next_id', $new_id, $id, $pod, $params_override );
2311
2312
		return $new_id;
2313
	}
2314
2315
	/**
2316
	 * Return the first item ID
2317
	 *
2318
	 * @param array|object|null $params_override Override the find() parameters.
2319
	 *
2320
	 * @return int
2321
	 * @since 2.3
2322
	 */
2323
	public function first_id( $params_override = null ) {
2324
2325
		$params = array(
2326
			'select'  => "`t`.`{$this->data->field_id}`",
2327
			'orderby' => "`t`.`{$this->data->field_id}` ASC",
2328
			'limit'   => 1,
2329
		);
2330
2331
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2332
			if ( ! empty( $params_override ) ) {
2333
				$params = $params_override;
2334
			} elseif ( ! empty( $this->params ) ) {
2335
				$params = $this->params;
2336
			}
2337
2338
			if ( is_object( $params ) ) {
2339
				$params = get_object_vars( $params );
2340
			}
2341
2342
			$params['select'] = "`t`.`{$this->data->field_id}`";
2343
			$params['offset'] = 0;
2344
			$params['limit']  = 1;
2345
		}
2346
2347
		$pod = pods( $this->pod, $params );
2348
2349
		$new_id = 0;
2350
2351
		if ( $pod->fetch() ) {
2352
			$new_id = $pod->id();
2353
		}
2354
2355
		$new_id = $this->do_hook( 'first_id', $new_id, $pod, $params_override );
2356
2357
		return $new_id;
2358
	}
2359
2360
	/**
2361
	 * Return the last item ID
2362
	 *
2363
	 * @param array|object|null $params_override Override the find() parameters.
2364
	 *
2365
	 * @return int
2366
	 * @since 2.3
2367
	 */
2368
	public function last_id( $params_override = null ) {
2369
2370
		$params = array(
2371
			'select'  => "`t`.`{$this->data->field_id}`",
2372
			'orderby' => "`t`.`{$this->data->field_id}` DESC",
2373
			'limit'   => 1,
2374
		);
2375
2376
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2377
			if ( ! empty( $params_override ) ) {
2378
				$params = $params_override;
2379
			} elseif ( ! empty( $this->params ) ) {
2380
				$params = $this->params;
2381
			}
2382
2383
			if ( is_object( $params ) ) {
2384
				$params = get_object_vars( $params );
2385
			}
2386
2387
			if ( isset( $params['total_found'] ) ) {
2388
				$params['offset'] = $params['total_found'] - 1;
2389
			} else {
2390
				$params['offset'] = $this->total_found() - 1;
2391
			}
2392
2393
			if ( isset( $params['orderby'] ) && ! empty( $params['orderby'] ) ) {
2394
				if ( is_array( $params['orderby'] ) ) {
2395
					foreach ( $params['orderby'] as $orderby => $dir ) {
2396
						$dir = strtoupper( $dir );
2397
2398
						if ( ! in_array( $dir, array( 'ASC', 'DESC' ), true ) ) {
2399
							continue;
2400
						}
2401
2402
						if ( 'ASC' === $dir ) {
2403
							$params['orderby'][ $orderby ] = 'DESC';
2404
						} else {
2405
							$params['orderby'][ $orderby ] = 'ASC';
2406
						}
2407
					}
2408
2409
					$params['orderby'][ $this->data->field_id ] = 'DESC';
2410
				} elseif ( "`t`.`{$this->data->field_id}` DESC" !== $params['orderby'] ) {
2411
					$params['orderby'] .= ", `t`.`{$this->data->field_id}` DESC";
2412
				}
2413
			}//end if
2414
2415
			$params['select'] = "`t`.`{$this->data->field_id}`";
2416
			$params['limit']  = 1;
2417
		}//end if
2418
2419
		$pod = pods( $this->pod, $params );
2420
2421
		$new_id = 0;
2422
2423
		if ( $pod->fetch() ) {
2424
			$new_id = $pod->id();
2425
		}
2426
2427
		$new_id = $this->do_hook( 'last_id', $new_id, $pod, $params_override );
2428
2429
		return $new_id;
2430
	}
2431
2432
	/**
2433
	 * Return the item name
2434
	 *
2435
	 * @return string
2436
	 * @since 2.0
2437
	 */
2438
	public function index() {
2439
2440
		return $this->field( $this->data->field_index );
2441
	}
2442
2443
	/**
2444
	 * Find items of a pod, much like WP_Query, but with advanced table handling.
2445
	 *
2446
	 * @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...
2447
	 * @param int          $limit  (deprecated) Limit the number of items to find, -1 to return all items with no limit.
2448
	 * @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...
2449
	 * @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...
2450
	 *
2451
	 * @return \Pods The pod object
2452
	 * @since 2.0
2453
	 * @link  https://pods.io/docs/find/
2454
	 */
2455
	public function find( $params = null, $limit = 15, $where = null, $sql = null ) {
2456
2457
		$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...
2458
		$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...
2459
2460
		$this->params = $params;
2461
2462
		$select = '`t`.*';
2463
2464
		if ( 'table' === $this->pod_data['storage'] && ! in_array( $this->pod_data['type'], array(
2465
			'pod',
2466
			'table',
2467
		), true ) ) {
2468
			$select .= ', `d`.*';
2469
		}
2470
2471
		if ( empty( $this->data->table ) ) {
2472
			return $this;
2473
		}
2474
2475
		$defaults = array(
2476
			// Optimization parameters.
2477
			'table'               => $this->data->table,
2478
			'select'              => $select,
2479
			'join'                => null,
2480
			// Main query parameters.
2481
			'where'               => $where,
2482
			'groupby'             => null,
2483
			'having'              => null,
2484
			'orderby'             => null,
2485
			// Pagination parameters.
2486
			'limit'               => (int) $limit,
2487
			'offset'              => null,
2488
			'page'                => (int) $this->page,
2489
			'page_var'            => $this->page_var,
2490
			'pagination'          => (boolean) $this->pagination,
2491
			// Search parameters.
2492
			'search'              => (boolean) $this->search,
2493
			'search_var'          => $this->search_var,
2494
			'search_query'        => null,
2495
			'search_mode'         => $this->search_mode,
2496
			'search_across'       => false,
2497
			'search_across_picks' => false,
2498
			'search_across_files' => false,
2499
			// Advanced parameters.
2500
			'filters'             => $this->filters,
2501
			'sql'                 => $sql,
2502
			// Caching parameters.
2503
			'expires'             => null,
2504
			'cache_mode'          => 'cache',
2505
		);
2506
2507
		if ( is_array( $params ) ) {
2508
			$params = (object) array_merge( $defaults, $params );
2509
		} elseif ( is_object( $params ) ) {
2510
			$params = (object) array_merge( $defaults, get_object_vars( $params ) );
2511
		} else {
2512
			$defaults['orderby'] = $params;
2513
2514
			$params = (object) $defaults;
2515
		}
2516
2517
		/**
2518
		 * Filter the Pods::find() parameters.
2519
		 *
2520
		 * @param object $params Parameters to make lookup with.
2521
		 */
2522
		$params = apply_filters( 'pods_pods_find', $params );
2523
2524
		$params->limit = (int) $params->limit;
2525
2526
		if ( 0 === $params->limit ) {
2527
			$params->limit = - 1;
2528
		}
2529
2530
		$this->limit      = (int) $params->limit;
2531
		$this->offset     = (int) $params->offset;
2532
		$this->page       = (int) $params->page;
2533
		$this->page_var   = $params->page_var;
2534
		$this->pagination = (boolean) $params->pagination;
2535
		$this->search     = (boolean) $params->search;
2536
		$this->search_var = $params->search_var;
2537
		$params->join     = (array) $params->join;
2538
2539
		if ( empty( $params->search_query ) ) {
2540
			$params->search_query = pods_v_sanitized( $this->search_var, 'get', '' );
2541
		}
2542
2543
		// 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...
2544
		if ( ! empty( $params->orderby ) && is_array( $params->orderby ) ) {
2545
			foreach ( $params->orderby as $k => $orderby ) {
2546
				if ( ! is_numeric( $k ) ) {
2547
					$key = '';
2548
2549
					$order = 'ASC';
2550
2551
					if ( 'DESC' === strtoupper( $orderby ) ) {
2552
						$order = 'DESC';
2553
					}
2554
2555
					if ( isset( $this->fields[ $k ] ) && in_array( $this->fields[ $k ]['type'], $tableless_field_types, true ) ) {
2556
						if ( in_array( $this->fields[ $k ]['pick_object'], $simple_tableless_objects, true ) ) {
2557
							if ( 'table' === $this->pod_data['storage'] ) {
2558
								if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2559
									$key = "`d`.`{$k}`";
2560
								} else {
2561
									$key = "`t`.`{$k}`";
2562
								}
2563
							} else {
2564
								$key = "`{$k}`.`meta_value`";
2565
							}
2566
						} else {
2567
							$pick_val = $this->fields[ $k ]['pick_val'];
2568
2569
							if ( '__current__' === $pick_val ) {
2570
								$pick_val = $this->pod;
2571
							}
2572
2573
							$table = $this->api->get_table_info( $this->fields[ $k ]['pick_object'], $pick_val );
2574
2575
							if ( ! empty( $table ) ) {
2576
								$key = "`{$k}`.`" . $table['field_index'] . '`';
2577
							}
2578
						}//end if
2579
					}//end if
2580
2581
					if ( empty( $key ) ) {
2582
						if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2583
							if ( isset( $this->pod_data['object_fields'][ $k ] ) ) {
2584
								$key = "`t`.`{$k}`";
2585
							} elseif ( isset( $this->fields[ $k ] ) ) {
2586
								if ( 'table' === $this->pod_data['storage'] ) {
2587
									$key = "`d`.`{$k}`";
2588
								} else {
2589
									$key = "`{$k}`.`meta_value`";
2590
								}
2591
							} else {
2592
								$object_fields = (array) $this->pod_data['object_fields'];
2593
2594
								foreach ( $object_fields as $object_field => $object_field_opt ) {
2595
									if ( $object_field === $k || in_array( $k, $object_field_opt['alias'], true ) ) {
2596
										$key = "`t`.`{$object_field}`";
2597
									}
2598
								}
2599
							}
2600
						} elseif ( isset( $this->fields[ $k ] ) ) {
2601
							if ( 'table' === $this->pod_data['storage'] ) {
2602
								$key = "`t`.`{$k}`";
2603
							} else {
2604
								$key = "`{$k}`.`meta_value`";
2605
							}
2606
						}//end if
2607
2608
						if ( empty( $key ) ) {
2609
							$key = $k;
2610
2611
							if ( false === strpos( $key, ' ' ) && false === strpos( $key, '`' ) ) {
2612
								$key = '`' . str_replace( '.', '`.`', $key ) . '`';
2613
							}
2614
						}
2615
					}//end if
2616
2617
					$orderby = $key;
2618
2619
					if ( false === strpos( $orderby, ' ' ) ) {
2620
						$orderby .= ' ' . $order;
2621
					}
2622
2623
					$params->orderby[ $k ] = $orderby;
2624
				}//end if
2625
			}//end foreach
2626
		}//end if
2627
2628
		// Add prefix to $params->orderby if needed.
2629
		if ( ! empty( $params->orderby ) ) {
2630
			if ( ! is_array( $params->orderby ) ) {
2631
				$params->orderby = array( $params->orderby );
2632
			}
2633
2634
			foreach ( $params->orderby as $ok => $prefix_orderby ) {
2635
				if ( false === strpos( $prefix_orderby, ',' ) && false === strpos( $prefix_orderby, '(' ) && false === stripos( $prefix_orderby, ' AS ' ) && false === strpos( $prefix_orderby, '`' ) && false === strpos( $prefix_orderby, '.' ) ) {
2636
					if ( false !== stripos( $prefix_orderby, ' DESC' ) ) {
2637
						$k   = trim( str_ireplace( array( '`', ' DESC' ), '', $prefix_orderby ) );
2638
						$dir = 'DESC';
2639
					} else {
2640
						$k   = trim( str_ireplace( array( '`', ' ASC' ), '', $prefix_orderby ) );
2641
						$dir = 'ASC';
2642
					}
2643
2644
					$key = $k;
2645
2646
					if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2647
						if ( isset( $this->pod_data['object_fields'][ $k ] ) ) {
2648
							$key = "`t`.`{$k}`";
2649
						} elseif ( isset( $this->fields[ $k ] ) ) {
2650
							if ( 'table' === $this->pod_data['storage'] ) {
2651
								$key = "`d`.`{$k}`";
2652
							} else {
2653
								$key = "`{$k}`.`meta_value`";
2654
							}
2655
						} else {
2656
							$object_fields = (array) $this->pod_data['object_fields'];
2657
2658
							foreach ( $object_fields as $object_field => $object_field_opt ) {
2659
								if ( $object_field === $k || in_array( $k, $object_field_opt['alias'], true ) ) {
2660
									$key = "`t`.`{$object_field}`";
2661
								}
2662
							}
2663
						}
2664
					} elseif ( isset( $this->fields[ $k ] ) ) {
2665
						if ( 'table' === $this->pod_data['storage'] ) {
2666
							$key = "`t`.`{$k}`";
2667
						} else {
2668
							$key = "`{$k}`.`meta_value`";
2669
						}
2670
					}//end if
2671
2672
					$prefix_orderby = "{$key} {$dir}";
2673
2674
					$params->orderby[ $ok ] = $prefix_orderby;
2675
				}//end if
2676
			}//end foreach
2677
		}//end if
2678
2679
		$this->data->select( $params );
2680
2681
		return $this;
2682
	}
2683
2684
	/**
2685
	 * Fetch an item from a Pod. If $id is null, it will return the next item in the list after running find().
2686
	 * You can rewind the list back to the start by using reset().
2687
	 *
2688
	 * Providing an $id will fetch a specific item from a Pod, much like a call to pods(), and can handle either an id
2689
	 * or slug.
2690
	 *
2691
	 * @see   PodsData::fetch
2692
	 *
2693
	 * @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...
2694
	 * @param bool $explicit_set Whether to set explicitly (use false when in loop).
2695
	 *
2696
	 * @return array An array of fields from the row
2697
	 *
2698
	 * @since 2.0
2699
	 * @link  https://pods.io/docs/fetch/
2700
	 */
2701
	public function fetch( $id = null, $explicit_set = true ) {
2702
2703
		/**
2704
		 * Runs directly before an item is fetched by fetch().
2705
		 *
2706
		 * @since unknown
2707
		 *
2708
		 * @param int|string|null $id   Item ID being fetched or null.
2709
		 * @param object|Pods     $this Current Pods object.
2710
		 */
2711
		do_action( 'pods_pods_fetch', $id, $this );
2712
2713
		if ( ! empty( $id ) ) {
2714
			$this->params = array();
2715
		}
2716
2717
		$this->data->fetch( $id, $explicit_set );
2718
2719
		return $this->row;
2720
	}
2721
2722
	/**
2723
	 * (Re)set the MySQL result pointer
2724
	 *
2725
	 * @see   PodsData::reset
2726
	 *
2727
	 * @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...
2728
	 *
2729
	 * @return \Pods The pod object
2730
	 *
2731
	 * @since 2.0
2732
	 * @link  https://pods.io/docs/reset/
2733
	 */
2734
	public function reset( $row = null ) {
2735
2736
		/**
2737
		 * Runs directly before the Pods object is reset by reset()
2738
		 *
2739
		 * @since unknown
2740
		 *
2741
		 * @param int|string|null The ID of the row being reset to or null if being reset to the beginning.
2742
		 * @param object|Pods $this Current Pods object.
2743
		 */
2744
		do_action( 'pods_pods_reset', $row, $this );
2745
2746
		$this->data->reset( $row );
2747
2748
		return $this;
2749
	}
2750
2751
	/**
2752
	 * Fetch the total row count returned by the last call to find(), based on the 'limit' parameter set.
2753
	 *
2754
	 * This is different than the total number of rows found in the database, which you can get with total_found().
2755
	 *
2756
	 * @see   PodsData::total
2757
	 *
2758
	 * @return int Number of rows returned by find(), based on the 'limit' parameter set
2759
	 * @since 2.0
2760
	 * @link  https://pods.io/docs/total/
2761
	 */
2762
	public function total() {
2763
2764
		do_action( 'pods_pods_total', $this );
2765
2766
		$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...
2767
2768
		$this->total =& $this->data->total;
2769
2770
		return $this->total;
2771
	}
2772
2773
	/**
2774
	 * Fetch the total amount of rows found by the last call to find(), regardless of the 'limit' parameter set.
2775
	 *
2776
	 * This is different than the total number of rows limited by the current call, which you can get with total().
2777
	 *
2778
	 * @see   PodsData::total_found
2779
	 *
2780
	 * @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...
2781
	 * @since 2.0
2782
	 * @link  https://pods.io/docs/total-found/
2783
	 */
2784
	public function total_found() {
2785
2786
		/**
2787
		 * Runs directly before the value of total_found() is determined and returned.
2788
		 *
2789
		 * @since unknown
2790
		 *
2791
		 * @param object|Pods $this Current Pods object.
2792
		 */
2793
		do_action( 'pods_pods_total_found', $this );
2794
2795
		$this->data->total_found();
2796
2797
		$this->total_found =& $this->data->total_found;
2798
2799
		return $this->total_found;
2800
	}
2801
2802
	/**
2803
	 * Fetch the total number of pages, based on total rows found and the last find() limit
2804
	 *
2805
	 * @param null|int $limit  Rows per page.
2806
	 * @param null|int $offset Offset of rows.
2807
	 * @param null|int $total  Total rows.
2808
	 *
2809
	 * @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...
2810
	 * @since 2.3.10
2811
	 */
2812
	public function total_pages( $limit = null, $offset = null, $total = null ) {
2813
2814
		$this->do_hook( 'total_pages' );
2815
2816
		if ( null === $limit ) {
2817
			$limit = $this->limit;
2818
		}
2819
2820
		if ( null === $offset ) {
2821
			$offset = $this->offset;
2822
		}
2823
2824
		if ( null === $total ) {
2825
			$total = $this->total_found();
2826
		}
2827
2828
		return ceil( ( $total - $offset ) / $limit );
2829
2830
	}
2831
2832
	/**
2833
	 * Fetch the zebra switch
2834
	 *
2835
	 * @see   PodsData::zebra
2836
	 *
2837
	 * @return bool Zebra state
2838
	 * @since 1.12
2839
	 */
2840
	public function zebra() {
2841
2842
		$this->do_hook( 'zebra' );
2843
2844
		return $this->data->zebra();
2845
	}
2846
2847
	/**
2848
	 * Fetch the nth state
2849
	 *
2850
	 * @see   PodsData::nth
2851
	 *
2852
	 * @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...
2853
	 *
2854
	 * @return bool Whether $nth matches
2855
	 * @since 2.3
2856
	 */
2857
	public function nth( $nth = null ) {
2858
2859
		$this->do_hook( 'nth', $nth );
2860
2861
		return $this->data->nth( $nth );
2862
	}
2863
2864
	/**
2865
	 * Fetch the current position in the loop (starting at 1)
2866
	 *
2867
	 * @see   PodsData::position
2868
	 *
2869
	 * @return int Current row number (+1)
2870
	 * @since 2.3
2871
	 */
2872
	public function position() {
2873
2874
		$this->do_hook( 'position' );
2875
2876
		return $this->data->position();
2877
	}
2878
2879
	/**
2880
	 * Add an item to a Pod by giving an array of field data or set a specific field to
2881
	 * a specific value if you're just wanting to add a new item but only set one field.
2882
	 *
2883
	 * You may be looking for save() in most cases where you're setting a specific field.
2884
	 *
2885
	 * @see   PodsAPI::save_pod_item
2886
	 *
2887
	 * @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...
2888
	 * @param mixed        $value (optional) Value of the field, if $data is a field name.
2889
	 *
2890
	 * @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...
2891
	 *
2892
	 * @since 2.0
2893
	 * @link  https://pods.io/docs/add/
2894
	 */
2895
	public function add( $data = null, $value = null ) {
2896
2897
		if ( null !== $value ) {
2898
			$data = array( $data => $value );
2899
		}
2900
2901
		$data = (array) $this->do_hook( 'add', $data );
2902
2903
		if ( empty( $data ) ) {
2904
			return 0;
2905
		}
2906
2907
		$params = array(
2908
			'pod'                 => $this->pod,
2909
			'data'                => $data,
2910
			'allow_custom_fields' => true,
2911
		);
2912
2913
		return $this->api->save_pod_item( $params );
2914
	}
2915
2916
	/**
2917
	 * Add an item to the values of a relationship field, add a value to a number field (field+1), add time to a date
2918
	 * field, or add text to a text field
2919
	 *
2920
	 * @see   PodsAPI::save_pod_item
2921
	 *
2922
	 * @param string $field Field name.
2923
	 * @param mixed  $value IDs to add, int|float to add to number field, string for dates (+1 day), or string for text.
2924
	 * @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...
2925
	 *
2926
	 * @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...
2927
	 *
2928
	 * @since 2.3
2929
	 */
2930
	public function add_to( $field, $value, $id = null ) {
2931
2932
		$pod =& $this;
2933
2934
		$fetch = false;
2935
2936
		if ( null === $id ) {
2937
			$fetch = true;
2938
2939
			$id = $pod->id();
2940
			// @codingStandardsIgnoreLine
2941
		} elseif ( $id != $this->id() ) {
2942
			$pod = pods( $this->pod, $id );
2943
		}
2944
2945
		$this->do_hook( 'add_to', $field, $value, $id );
2946
2947
		if ( ! isset( $this->fields[ $field ] ) ) {
2948
			return $id;
2949
		}
2950
2951
		// Tableless fields.
2952
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
2953
			if ( ! is_array( $value ) ) {
2954
				$value = explode( ',', $value );
2955
			}
2956
2957
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
2958
				$current_value = $pod->raw( $field );
2959
2960
				if ( ! empty( $current_value ) || ( ! is_array( $current_value ) && 0 < strlen( $current_value ) ) ) {
2961
					$current_value = (array) $current_value;
2962
				} else {
2963
					$current_value = array();
2964
				}
2965
2966
				$value = array_merge( $current_value, $value );
2967
			} else {
2968
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
2969
2970
				foreach ( $value as $k => $v ) {
2971
					if ( ! preg_match( '/[^\D]/', $v ) ) {
2972
						$value[ $k ] = (int) $v;
2973
					}
2974
				}
2975
2976
				$value = array_merge( $related_ids, $value );
2977
			}//end if
2978
2979
			if ( ! empty( $value ) ) {
2980
				$value = array_filter( array_unique( $value ) );
2981
			} else {
2982
				$value = array();
2983
			}
2984
2985
			if ( empty( $value ) ) {
2986
				return $id;
2987
			}
2988
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
2989
			// Number fields.
2990
			$current_value = (float) $pod->raw( $field );
2991
2992
			$value = ( $current_value + (float) $value );
2993
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
2994
			// Date fields.
2995
			$current_value = $pod->raw( $field );
2996
2997
			if ( 0 < strlen( $current_value ) ) {
2998
				$value = strtotime( $value, strtotime( $current_value ) );
2999
			} else {
3000
				$value = strtotime( $value );
3001
			}
3002
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
3003
			// Text fields.
3004
			$current_value = $pod->raw( $field );
3005
3006
			if ( 0 < strlen( $current_value ) ) {
3007
				$value = $current_value . $value;
3008
			}
3009
		}//end if
3010
3011
		// @todo handle object fields and taxonomies.
3012
		$params = array(
3013
			'pod'  => $this->pod,
3014
			'id'   => $id,
3015
			'data' => array(
3016
				$field => $value,
3017
			),
3018
		);
3019
3020
		$id = $this->api->save_pod_item( $params );
3021
3022
		if ( 0 < $id && $fetch ) {
3023
			// Clear local var cache of field values.
3024
			$pod->data->row = array();
3025
3026
			$pod->fetch( $id, false );
3027
		}
3028
3029
		return $id;
3030
	}
3031
3032
	/**
3033
	 * Remove an item from the values of a relationship field, remove a value from a number field (field-1), remove
3034
	 * time to a date field
3035
	 *
3036
	 * @see   PodsAPI::save_pod_item
3037
	 *
3038
	 * @param string $field Field name.
3039
	 * @param mixed  $value IDs to add, int|float to add to number field, string for dates (-1 day), or string for text.
3040
	 * @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...
3041
	 *
3042
	 * @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...
3043
	 *
3044
	 * @since 2.3.3
3045
	 */
3046
	public function remove_from( $field, $value = null, $id = null ) {
3047
3048
		$pod =& $this;
3049
3050
		$fetch = false;
3051
3052
		if ( null === $id ) {
3053
			$fetch = true;
3054
3055
			$id = $this->id();
3056
			// @codingStandardsIgnoreLine
3057
		} elseif ( $id != $this->id() ) {
3058
			$pod = pods( $this->pod, $id );
3059
		}
3060
3061
		$this->do_hook( 'remove_from', $field, $value, $id );
3062
3063
		if ( ! isset( $this->fields[ $field ] ) ) {
3064
			return $id;
3065
		}
3066
3067
		// Tableless fields.
3068
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
3069
			if ( empty( $value ) ) {
3070
				$value = array();
3071
			}
3072
3073
			if ( ! empty( $value ) ) {
3074
				if ( ! is_array( $value ) ) {
3075
					$value = explode( ',', $value );
3076
				}
3077
3078
				if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
3079
					$current_value = $pod->raw( $field );
3080
3081
					if ( ! empty( $current_value ) ) {
3082
						$current_value = (array) $current_value;
3083
					}
3084
3085
					foreach ( $current_value as $k => $v ) {
3086
						// @codingStandardsIgnoreLine.
3087
						if ( in_array( $v, $value ) ) {
3088
							unset( $current_value[ $k ] );
3089
						}
3090
					}
3091
3092
					$value = $current_value;
3093
				} else {
3094
					$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
3095
3096
					foreach ( $value as $k => $v ) {
3097
						if ( ! preg_match( '/[^\D]/', $v ) ) {
3098
							$value[ $k ] = (int) $v;
3099
						}
3100
3101
						// @todo Convert slugs into IDs.
3102
					}
3103
3104
					foreach ( $related_ids as $k => $v ) {
3105
						// @codingStandardsIgnoreLine.
3106
						if ( in_array( $v, $value ) ) {
3107
							unset( $related_ids[ $k ] );
3108
						}
3109
					}
3110
3111
					$value = $related_ids;
3112
				}//end if
3113
3114
				if ( ! empty( $value ) ) {
3115
					$value = array_filter( array_unique( $value ) );
3116
				} else {
3117
					$value = array();
3118
				}
3119
			}//end if
3120
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
3121
			// Number fields.
3122
			// Date fields don't support empty for removing.
3123
			if ( empty( $value ) ) {
3124
				return $id;
3125
			}
3126
3127
			$current_value = (float) $pod->raw( $field );
3128
3129
			$value = ( $current_value - (float) $value );
3130
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
3131
			// Date fields.
3132
			// Date fields don't support empty for removing.
3133
			if ( empty( $value ) ) {
3134
				return $id;
3135
			}
3136
3137
			$current_value = $pod->raw( $field );
3138
3139
			if ( 0 < strlen( $current_value ) ) {
3140
				$value = strtotime( $value, strtotime( $current_value ) );
3141
			} else {
3142
				$value = strtotime( $value );
3143
			}
3144
3145
			$value = date_i18n( 'Y-m-d h:i:s', $value );
3146
		}//end if
3147
3148
		// @todo handle object fields and taxonomies.
3149
		$params = array(
3150
			'pod'  => $this->pod,
3151
			'id'   => $id,
3152
			'data' => array(
3153
				$field => $value,
3154
			),
3155
		);
3156
3157
		$id = $this->api->save_pod_item( $params );
3158
3159
		if ( 0 < $id && $fetch ) {
3160
			// Clear local var cache of field values.
3161
			$pod->data->row = array();
3162
3163
			$pod->fetch( $id, false );
3164
		}
3165
3166
		return $id;
3167
3168
	}
3169
3170
	/**
3171
	 * Save an item by giving an array of field data or set a specific field to a specific value.
3172
	 *
3173
	 * Though this function has the capacity to add new items, best practice should direct you
3174
	 * to use add() for that instead.
3175
	 *
3176
	 * @see   PodsAPI::save_pod_item
3177
	 *
3178
	 * @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...
3179
	 * @param mixed        $value  (optional) Value of the field, if $data is a field name.
3180
	 * @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...
3181
	 * @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...
3182
	 *
3183
	 * @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...
3184
	 *
3185
	 * @since 2.0
3186
	 * @link  https://pods.io/docs/save/
3187
	 */
3188
	public function save( $data = null, $value = null, $id = null, $params = null ) {
3189
3190
		if ( null !== $value ) {
3191
			$data = array( $data => $value );
3192
		}
3193
3194
		$fetch = false;
3195
3196
		// @codingStandardsIgnoreLine
3197
		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...
3198
			$fetch = true;
3199
3200
			if ( null === $id ) {
3201
				$id = $this->id();
3202
			}
3203
		}
3204
3205
		$data = (array) $this->do_hook( 'save', $data, $id );
3206
3207
		if ( empty( $data ) && empty( $params['is_new_item'] ) ) {
3208
			return $id;
3209
		}
3210
3211
		$default = array();
3212
3213
		if ( ! empty( $params ) && is_array( $params ) ) {
3214
			$default = $params;
3215
		}
3216
3217
		$params = array(
3218
			'pod'                 => $this->pod,
3219
			'id'                  => $id,
3220
			'data'                => $data,
3221
			'allow_custom_fields' => true,
3222
			'clear_slug_cache'    => false,
3223
		);
3224
3225
		if ( ! empty( $default ) ) {
3226
			$params = array_merge( $params, $default );
3227
		}
3228
3229
		$id = $this->api->save_pod_item( $params );
3230
3231
		if ( 0 < $id && $fetch ) {
3232
			// Clear local var cache of field values.
3233
			$this->data->row = array();
3234
3235
			$this->fetch( $id, false );
3236
		}
3237
3238
		if ( ! empty( $this->pod_data['field_slug'] ) ) {
3239
			if ( 0 < $id && $fetch ) {
3240
				$slug = $this->field( $this->pod_data['field_slug'] );
3241
			} else {
3242
				$slug = pods( $this->pod, $id )->field( $this->pod_data['field_slug'] );
3243
			}
3244
3245
			if ( 0 < strlen( $slug ) ) {
3246
				pods_cache_clear( $slug, 'pods_items_' . $this->pod );
3247
			}
3248
		}
3249
3250
		return $id;
3251
	}
3252
3253
	/**
3254
	 * Delete an item
3255
	 *
3256
	 * @see   PodsAPI::delete_pod_item
3257
	 *
3258
	 * @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...
3259
	 *
3260
	 * @return bool Whether the item was successfully deleted
3261
	 *
3262
	 * @since 2.0
3263
	 * @link  https://pods.io/docs/delete/
3264
	 */
3265
	public function delete( $id = null ) {
3266
3267
		if ( null === $id ) {
3268
			$id = $this->id();
3269
		}
3270
3271
		$id = (int) $this->do_hook( 'delete', $id );
3272
3273
		if ( empty( $id ) ) {
3274
			return false;
3275
		}
3276
3277
		$params = array(
3278
			'pod' => $this->pod,
3279
			'id'  => $id,
3280
		);
3281
3282
		return $this->api->delete_pod_item( $params );
3283
	}
3284
3285
	/**
3286
	 * Reset Pod
3287
	 *
3288
	 * @see   PodsAPI::reset_pod
3289
	 *
3290
	 * @return bool Whether the Pod was successfully reset
3291
	 *
3292
	 * @since 2.1.1
3293
	 */
3294
	public function reset_pod() {
3295
3296
		$params = array( 'id' => $this->pod_id );
3297
3298
		$this->data->id   = null;
3299
		$this->data->row  = array();
3300
		$this->data->data = array();
3301
3302
		$this->data->total       = 0;
3303
		$this->data->total_found = 0;
3304
3305
		return $this->api->reset_pod( $params );
3306
	}
3307
3308
	/**
3309
	 * Duplicate an item
3310
	 *
3311
	 * @see   PodsAPI::duplicate_pod_item
3312
	 *
3313
	 * @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...
3314
	 *
3315
	 * @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...
3316
	 *
3317
	 * @since 2.0
3318
	 * @link  https://pods.io/docs/duplicate/
3319
	 */
3320
	public function duplicate( $id = null ) {
3321
3322
		if ( null === $id ) {
3323
			$id = $this->id();
3324
		}
3325
3326
		$id = (int) $this->do_hook( 'duplicate', $id );
3327
3328
		if ( empty( $id ) ) {
3329
			return false;
3330
		}
3331
3332
		$params = array(
3333
			'pod' => $this->pod,
3334
			'id'  => $id,
3335
		);
3336
3337
		return $this->api->duplicate_pod_item( $params );
3338
	}
3339
3340
	/**
3341
	 * Import data / Save multiple rows of data at once
3342
	 *
3343
	 * @see   PodsAPI::import
3344
	 *
3345
	 * @param mixed  $import_data  PHP associative array or CSV input.
3346
	 * @param bool   $numeric_mode Use IDs instead of the name field when matching.
3347
	 * @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...
3348
	 *
3349
	 * @return array IDs of imported items
3350
	 *
3351
	 * @since 2.3
3352
	 */
3353
	public function import( $import_data, $numeric_mode = false, $format = null ) {
3354
3355
		return $this->api->import( $import_data, $numeric_mode, $format );
3356
	}
3357
3358
	/**
3359
	 * Export an item's data
3360
	 *
3361
	 * @see   PodsAPI::export_pod_item
3362
	 *
3363
	 * @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...
3364
	 * @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...
3365
	 * @param null|string $format (optional) The format of the export (php | json).
3366
	 *
3367
	 * @return array|bool Data array of the exported pod item
3368
	 *
3369
	 * @since 2.0
3370
	 * @link  https://pods.io/docs/export/
3371
	 */
3372
	public function export( $fields = null, $id = null, $format = null ) {
3373
3374
		$params = array(
3375
			'pod'     => $this->pod,
3376
			'id'      => $id,
3377
			'fields'  => null,
3378
			'depth'   => 2,
3379
			'flatten' => false,
3380
			'context' => null,
3381
			'format'  => $format,
3382
		);
3383
3384
		if ( is_array( $fields ) && ( isset( $fields['fields'] ) || isset( $fields['depth'] ) ) ) {
3385
			$params = array_merge( $params, $fields );
3386
		} else {
3387
			$params['fields'] = $fields;
3388
		}
3389
3390
		if ( isset( $params['fields'] ) && is_array( $params['fields'] ) && ! in_array( $this->pod_data['field_id'], $params['fields'], true ) ) {
3391
			$params['fields'] = array_merge( array( $this->pod_data['field_id'] ), $params['fields'] );
3392
		}
3393
3394
		if ( null === $params['id'] ) {
3395
			$params['id'] = $this->id();
3396
		}
3397
3398
		$params = (array) $this->do_hook( 'export', $params );
3399
3400
		if ( empty( $params['id'] ) ) {
3401
			return false;
3402
		}
3403
3404
		$data = $this->api->export_pod_item( $params );
3405
3406
		if ( ! empty( $params['format'] ) && 'json' === $params['format'] ) {
3407
			$data = wp_json_encode( (array) $data );
3408
		}
3409
3410
		return $data;
3411
	}
3412
3413
	/**
3414
	 * Export data from all items
3415
	 *
3416
	 * @see   PodsAPI::export
3417
	 *
3418
	 * @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...
3419
	 *
3420
	 * @return array Data arrays of all exported pod items
3421
	 *
3422
	 * @since 2.3
3423
	 */
3424
	public function export_data( $params = null ) {
3425
3426
		$defaults = array(
3427
			'fields' => null,
3428
			'depth'  => 2,
3429
			'params' => null,
3430
		);
3431
3432
		if ( empty( $params ) ) {
3433
			$params = $defaults;
3434
		} else {
3435
			$params = array_merge( $defaults, (array) $params );
3436
		}
3437
3438
		return $this->api->export( $this, $params );
3439
	}
3440
3441
	/**
3442
	 * Display the pagination controls, types supported by default
3443
	 * are simple, paginate and advanced. The base and format parameters
3444
	 * are used only for the paginate view.
3445
	 *
3446
	 * @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...
3447
	 *
3448
	 * @return string Pagination HTML
3449
	 * @since 2.0
3450
	 * @link  https://pods.io/docs/pagination/
3451
	 */
3452
	public function pagination( $params = null ) {
3453
3454
		if ( empty( $params ) ) {
3455
			$params = array();
3456
		} elseif ( ! is_array( $params ) ) {
3457
			$params = array( 'label' => $params );
3458
		}
3459
3460
		$this->page_var = pods_v( 'page_var', $params, $this->page_var );
3461
3462
		$url = pods_query_arg( null, null, $this->page_var );
3463
3464
		$append = '?';
3465
3466
		if ( false !== strpos( $url, '?' ) ) {
3467
			$append = '&';
3468
		}
3469
3470
		$defaults = array(
3471
			'type'        => 'advanced',
3472
			'label'       => __( 'Go to page:', 'pods' ),
3473
			'show_label'  => true,
3474
			'first_text'  => __( '&laquo; First', 'pods' ),
3475
			'prev_text'   => __( '&lsaquo; Previous', 'pods' ),
3476
			'next_text'   => __( 'Next &rsaquo;', 'pods' ),
3477
			'last_text'   => __( 'Last &raquo;', 'pods' ),
3478
			'prev_next'   => true,
3479
			'first_last'  => true,
3480
			'limit'       => (int) $this->limit,
3481
			'offset'      => (int) $this->offset,
3482
			'page'        => max( 1, (int) $this->page ),
3483
			'mid_size'    => 2,
3484
			'end_size'    => 1,
3485
			'total_found' => $this->total_found(),
3486
			'page_var'    => $this->page_var,
3487
			'base'        => "{$url}{$append}%_%",
3488
			'format'      => "{$this->page_var}=%#%",
3489
			'class'       => '',
3490
			'link_class'  => '',
3491
		);
3492
3493
		if ( is_object( $params ) ) {
3494
			$params = get_object_vars( $params );
3495
		}
3496
3497
		$params = (object) array_merge( $defaults, (array) $params );
3498
3499
		$params->total = $this->total_pages( $params->limit, $params->offset, $params->total_found );
3500
3501
		if ( $params->limit < 1 || $params->total_found < 1 || 1 === $params->total || $params->total_found <= $params->offset ) {
3502
			return $this->do_hook( 'pagination', $this->do_hook( 'pagination_' . $params->type, '', $params ), $params );
3503
		}
3504
3505
		$pagination = $params->type;
3506
3507
		if ( ! in_array( $params->type, array( 'simple', 'advanced', 'paginate', 'list' ), true ) ) {
3508
			$pagination = 'advanced';
3509
		}
3510
3511
		ob_start();
3512
3513
		pods_view( PODS_DIR . 'ui/front/pagination/' . $pagination . '.php', compact( array_keys( get_defined_vars() ) ) );
3514
3515
		$output = ob_get_clean();
3516
3517
		return $this->do_hook( 'pagination', $this->do_hook( 'pagination_' . $params->type, $output, $params ), $params );
3518
3519
	}
3520
3521
	/**
3522
	 * Return a filter form for searching a Pod
3523
	 *
3524
	 * @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...
3525
	 *
3526
	 * @return string Filters HTML
3527
	 *
3528
	 * @since 2.0
3529
	 * @link  https://pods.io/docs/filters/
3530
	 */
3531
	public function filters( $params = null ) {
3532
3533
		$defaults = array(
3534
			'fields' => $params,
3535
			'label'  => '',
3536
			'action' => '',
3537
			'search' => '',
3538
		);
3539
3540
		if ( is_array( $params ) ) {
3541
			$params = array_merge( $defaults, $params );
3542
		} else {
3543
			$params = $defaults;
3544
		}
3545
3546
		$pod =& $this;
3547
3548
		/**
3549
		 * Filter the Pods::filters() parameters.
3550
		 *
3551
		 * @param array $params Parameters to filter with.
3552
		 * @param Pods  $pod    Pods object.
3553
		 */
3554
		$params = apply_filters( 'pods_filters_params', $params, $pod );
3555
3556
		$fields = $params['fields'];
3557
3558
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
3559
			$fields = explode( ',', $fields );
3560
		}
3561
3562
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
3563
3564
		// Force array.
3565
		if ( empty( $fields ) ) {
3566
			$fields = array();
3567
		} else {
3568
			// Temporary.
3569
			$filter_fields = $fields;
3570
3571
			$fields = array();
3572
3573
			foreach ( $filter_fields as $k => $field ) {
3574
				$name = $k;
3575
3576
				$defaults = array(
3577
					'name' => $name,
3578
				);
3579
3580
				if ( ! is_array( $field ) ) {
3581
					$name = $field;
3582
3583
					$field = array(
3584
						'name' => $name,
3585
					);
3586
				}
3587
3588
				// @codingStandardsIgnoreLine.
3589
				$field = array_merge( $defaults, $field );
3590
3591
				$field['name'] = trim( $field['name'] );
3592
3593
				if ( pods_v( 'hidden', $field, false, true ) ) {
3594
					$field['type'] = 'hidden';
3595
				}
3596
3597
				if ( isset( $object_fields[ $field['name'] ] ) ) {
3598
					// @codingStandardsIgnoreLine.
3599
					$fields[ $field['name'] ] = array_merge( $object_fields[ $field['name'] ], $field );
3600
				} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
3601
					// @codingStandardsIgnoreLine.
3602
					$fields[ $field['name'] ] = array_merge( $this->fields[ $field['name'] ], $field );
3603
				}
3604
			}//end foreach
3605
3606
			// Cleanup.
3607
			unset( $filter_fields );
3608
		}//end if
3609
3610
		$this->filters = array_keys( $fields );
3611
3612
		$label = $params['label'];
3613
3614
		if ( '' === $label ) {
3615
			$label = __( 'Search', 'pods' );
3616
		}
3617
3618
		$action = $params['action'];
3619
3620
		$search = trim( $params['search'] );
3621
3622
		if ( '' === $search ) {
3623
			$search = pods_v_sanitized( $pod->search_var, 'get', '' );
3624
		}
3625
3626
		ob_start();
3627
3628
		pods_view( PODS_DIR . 'ui/front/filters.php', compact( array_keys( get_defined_vars() ) ) );
3629
3630
		$output = ob_get_clean();
3631
3632
		/**
3633
		 * Filter the HTML output of filters()
3634
		 *
3635
		 * @since unknown
3636
		 *
3637
		 * @param string      $output Filter output.
3638
		 * @param array       $params Params array passed to filters().
3639
		 * @param object|Pods $this   Current Pods object.
3640
		 */
3641
		return apply_filters( 'pods_pods_filters', $output, $params, $this );
3642
	}
3643
3644
	/**
3645
	 * Run a helper within a Pod Page or WP Template
3646
	 *
3647
	 * @see   Pods_Helpers::helper
3648
	 *
3649
	 * @param string|array $helper Helper name.
3650
	 * @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...
3651
	 * @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...
3652
	 *
3653
	 * @return mixed Anything returned by the helper
3654
	 * @since 2.0
3655
	 */
3656
	public function helper( $helper, $value = null, $name = null ) {
3657
3658
		$params = array(
3659
			'helper'     => $helper,
3660
			'value'      => $value,
3661
			'name'       => $name,
3662
			'deprecated' => false,
3663
		);
3664
3665
		if ( class_exists( 'Pods_Templates' ) ) {
3666
			$params['deprecated'] = Pods_Templates::$deprecated;
3667
		}
3668
3669
		if ( is_array( $helper ) ) {
3670
			$params = array_merge( $params, $helper );
3671
		}
3672
3673
		if ( class_exists( 'Pods_Helpers' ) ) {
3674
			$value = Pods_Helpers::helper( $params, $this );
3675
		} elseif ( function_exists( $params['helper'] ) ) {
3676
			$disallowed = array(
3677
				'system',
3678
				'exec',
3679
				'popen',
3680
				'eval',
3681
				'preg_replace',
3682
				'create_function',
3683
				'include',
3684
				'include_once',
3685
				'require',
3686
				'require_once',
3687
			);
3688
3689
			$allowed = array();
3690
3691
			/**
3692
			 * Allows adjusting the disallowed callbacks as needed.
3693
			 *
3694
			 * @param array $disallowed List of callbacks not allowed.
3695
			 * @param array $params     Parameters used by Pods::helper() method.
3696
			 *
3697
			 * @since 2.7
3698
			 */
3699
			$disallowed = apply_filters( 'pods_helper_disallowed_callbacks', $disallowed, $params );
3700
3701
			/**
3702
			 * Allows adjusting the allowed allowed callbacks as needed.
3703
			 *
3704
			 * @param array $allowed List of callbacks explicitly allowed.
3705
			 * @param array $params  Parameters used by Pods::helper() method.
3706
			 *
3707
			 * @since 2.7
3708
			 */
3709
			$allowed = apply_filters( 'pods_helper_allowed_callbacks', $allowed, $params );
3710
3711
			// Clean up helper callback (if string).
3712
			if ( is_string( $params['helper'] ) ) {
3713
				$params['helper'] = strip_tags( str_replace( array( '`', chr( 96 ) ), "'", $params['helper'] ) );
3714
			}
3715
3716
			$is_allowed = false;
3717
3718
			if ( ! empty( $allowed ) ) {
3719
				if ( in_array( $params['helper'], $allowed, true ) ) {
3720
					$is_allowed = true;
3721
				}
3722
			} elseif ( ! in_array( $params['helper'], $disallowed, true ) ) {
3723
				$is_allowed = true;
3724
			}
3725
3726
			if ( $is_allowed ) {
3727
				$value = call_user_func( $params['helper'], $value );
3728
			}
3729
		} else {
3730
			$value = apply_filters( $params['helper'], $value );
3731
		}//end if
3732
3733
		return $value;
3734
	}
3735
3736
	/**
3737
	 * Display the page template
3738
	 *
3739
	 * @see   Pods_Templates::template
3740
	 *
3741
	 * @param string      $template_name The template name.
3742
	 * @param string|null $code          Custom template code to use instead.
3743
	 * @param bool        $deprecated    Whether to use deprecated functionality based on old function usage.
3744
	 *
3745
	 * @return mixed Template output
3746
	 *
3747
	 * @since 2.0
3748
	 * @link  https://pods.io/docs/template/
3749
	 */
3750
	public function template( $template_name, $code = null, $deprecated = false ) {
3751
3752
		$out = null;
3753
3754
		$obj =& $this;
3755
3756
		if ( ! empty( $code ) ) {
3757
			// backwards compatibility.
3758
			$code = str_replace( '$this->', '$obj->', $code );
3759
3760
			/**
3761
			 * Filter the template code before running it.
3762
			 *
3763
			 * @param string $code          Template code.
3764
			 * @param string $template_name Template name.
3765
			 * @param Pods  $pod            Pods object.
3766
			 */
3767
			$code = apply_filters( 'pods_templates_pre_template', $code, $template_name, $this );
3768
3769
			/**
3770
			 * Filter the template code before running it.
3771
			 *
3772
			 * @param string $code          Template code.
3773
			 * @param string $template_name Template name.
3774
			 * @param Pods  $pod            Pods object.
3775
			 */
3776
			$code = apply_filters( "pods_templates_pre_template_{$template_name}", $code, $template_name, $this );
3777
3778
			ob_start();
3779
3780
			if ( ! empty( $code ) ) {
3781
				// Only detail templates need $this->id.
3782
				if ( empty( $this->id ) ) {
3783
					while ( $this->fetch() ) {
3784
						// @codingStandardsIgnoreLine
3785
						echo $this->do_magic_tags( $code );
3786
					}
3787
				} else {
3788
					// @codingStandardsIgnoreLine
3789
					echo $this->do_magic_tags( $code );
3790
				}
3791
			}
3792
3793
			$out = ob_get_clean();
3794
3795
			/**
3796
			 * Filter the template output.
3797
			 *
3798
			 * @param string $out           Template output.
3799
			 * @param string $code          Template code.
3800
			 * @param string $template_name Template name.
3801
			 * @param Pods   $pod           Pods object.
3802
			 */
3803
			$out = apply_filters( 'pods_templates_post_template', $out, $code, $template_name, $this );
3804
3805
			/**
3806
			 * Filter the template output.
3807
			 *
3808
			 * @param string $out           Template output.
3809
			 * @param string $code          Template code.
3810
			 * @param string $template_name Template name.
3811
			 * @param Pods   $pod           Pods object.
3812
			 */
3813
			$out = apply_filters( "pods_templates_post_template_{$template_name}", $out, $code, $template_name, $this );
3814
		} elseif ( class_exists( 'Pods_Templates' ) ) {
3815
			$out = Pods_Templates::template( $template_name, $code, $this, $deprecated );
3816
		} elseif ( trim( preg_replace( '/[^a-zA-Z0-9_\-\/]/', '', $template_name ), ' /-' ) === $template_name ) {
3817
			ob_start();
3818
3819
			$default_templates = array(
3820
				'pods/' . $template_name,
3821
				'pods-' . $template_name,
3822
				$template_name,
3823
			);
3824
3825
			/**
3826
			 * Filter the default Pods Template files.
3827
			 *
3828
			 * @param array $default_templates Default Pods Template files.
3829
			 */
3830
			$default_templates = apply_filters( 'pods_template_default_templates', $default_templates );
3831
3832
			// Only detail templates need $this->id.
3833
			if ( empty( $this->id ) ) {
3834
				while ( $this->fetch() ) {
3835
					pods_template_part( $default_templates, compact( array_keys( get_defined_vars() ) ) );
3836
				}
3837
			} else {
3838
				pods_template_part( $default_templates, compact( array_keys( get_defined_vars() ) ) );
3839
			}
3840
3841
			$out = ob_get_clean();
3842
3843
			/**
3844
			 * Filter the template output.
3845
			 *
3846
			 * @param string $out           Template output.
3847
			 * @param string $code          Template code.
3848
			 * @param string $template_name Template name.
3849
			 * @param Pods   $pod           Pods object.
3850
			 */
3851
			$out = apply_filters( 'pods_templates_post_template', $out, $code, $template_name, $this );
3852
3853
			/**
3854
			 * Filter the template output.
3855
			 *
3856
			 * @param string $out           Template output.
3857
			 * @param string $code          Template code.
3858
			 * @param string $template_name Template name.
3859
			 * @param Pods   $pod           Pods object.
3860
			 */
3861
			$out = apply_filters( "pods_templates_post_template_{$template_name}", $out, $code, $template_name, $this );
3862
		}//end if
3863
3864
		return $out;
3865
	}
3866
3867
	/**
3868
	 * Embed a form to add / edit a pod item from within your theme. Provide an array of $fields to include
3869
	 * and override options where needed. For WP object based Pods, you can pass through the WP object
3870
	 * field names too, such as "post_title" or "post_content" for example.
3871
	 *
3872
	 * @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...
3873
	 * @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...
3874
	 * @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...
3875
	 *
3876
	 * @return bool|mixed
3877
	 * @since 2.0
3878
	 * @link  https://pods.io/docs/form/
3879
	 */
3880
	public function form( $params = null, $label = null, $thank_you = null ) {
3881
3882
		$defaults = array(
3883
			'fields'      => $params,
3884
			'label'       => $label,
3885
			'thank_you'   => $thank_you,
3886
			'fields_only' => false,
3887
		);
3888
3889
		if ( is_array( $params ) ) {
3890
			$params = array_merge( $defaults, $params );
3891
		} else {
3892
			$params = $defaults;
3893
		}
3894
3895
		$pod =& $this;
3896
3897
		$params = $this->do_hook( 'form_params', $params );
3898
3899
		$fields = $params['fields'];
3900
3901
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
3902
			$fields = explode( ',', $fields );
3903
		}
3904
3905
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
3906
3907
		if ( empty( $fields ) ) {
3908
			// Add core object fields if $fields is empty.
3909
			$fields = array_merge( $object_fields, $this->fields );
3910
		}
3911
3912
		// Temporary.
3913
		$form_fields = $fields;
3914
3915
		$fields = array();
3916
3917
		foreach ( $form_fields as $k => $field ) {
3918
			$name = $k;
3919
3920
			$defaults = array(
3921
				'name' => $name,
3922
			);
3923
3924
			if ( ! is_array( $field ) ) {
3925
				$name = $field;
3926
3927
				$field = array(
3928
					'name' => $name,
3929
				);
3930
			}
3931
3932
			// @codingStandardsIgnoreLine.
3933
			$field = array_merge( $defaults, $field );
3934
3935
			$field['name'] = trim( $field['name'] );
3936
3937
			$default_value = pods_v( 'default', $field );
3938
			$value         = pods_v( 'value', $field );
3939
3940
			if ( empty( $field['name'] ) ) {
3941
				$field['name'] = trim( $name );
3942
			}
3943
3944
			if ( isset( $object_fields[ $field['name'] ] ) ) {
3945
				// @codingStandardsIgnoreLine.
3946
				$field = array_merge( $object_fields[ $field['name'] ], $field );
3947
			} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
3948
				// @codingStandardsIgnoreLine.
3949
				$field = array_merge( $this->fields[ $field['name'] ], $field );
3950
			}
3951
3952
			if ( pods_v( 'hidden', $field, false, true ) ) {
3953
				$field['type'] = 'hidden';
3954
			}
3955
3956
			$fields[ $field['name'] ] = $field;
3957
3958
			if ( empty( $this->id ) && null !== $default_value ) {
3959
				$this->row_override[ $field['name'] ] = $default_value;
3960
			} elseif ( ! empty( $this->id ) && null !== $value ) {
3961
				$this->row[ $field['name'] ] = $value;
3962
			}
3963
		}//end foreach
3964
3965
		// Cleanup.
3966
		unset( $form_fields );
3967
3968
		$fields = $this->do_hook( 'form_fields', $fields, $params );
3969
3970
		$label = $params['label'];
3971
3972
		if ( empty( $label ) ) {
3973
			$label = __( 'Save Changes', 'pods' );
3974
		}
3975
3976
		$thank_you   = $params['thank_you'];
3977
		$fields_only = $params['fields_only'];
3978
3979
		PodsForm::$form_counter ++;
3980
3981
		ob_start();
3982
3983
		if ( empty( $thank_you ) ) {
3984
			$success = 'success';
3985
3986
			if ( 1 < PodsForm::$form_counter ) {
3987
				$success .= PodsForm::$form_counter;
3988
			}
3989
3990
			$thank_you = pods_query_arg( array(
3991
				'success*' => null,
3992
				$success   => 1,
3993
			) );
3994
3995
			if ( 1 === (int) pods_v( $success, 'get', 0 ) ) {
3996
				$message = __( 'Form submitted successfully', 'pods' );
3997
3998
				/**
3999
				 * Change the text of the message that appears on successful form submission.
4000
				 *
4001
				 * @param string $message Success message.
4002
				 *
4003
				 * @since 2.7
4004
				 */
4005
				$message = apply_filters( 'pods_pod_form_success_message', $message );
4006
4007
				echo '<div id="message" class="pods-form-front-success">' . wp_kses_post( $message ) . '</div>';
4008
			}
4009
		}//end if
4010
4011
		pods_view( PODS_DIR . 'ui/front/form.php', compact( array_keys( get_defined_vars() ) ) );
4012
4013
		$output = ob_get_clean();
4014
4015
		if ( empty( $this->id ) ) {
4016
			$this->row_override = array();
4017
		}
4018
4019
		return $this->do_hook( 'form', $output, $fields, $label, $thank_you, $this, $this->id() );
4020
	}
4021
4022
	/**
4023
	 * Render a singular view for the Pod item content.
4024
	 *
4025
	 * @param array|string|null $fields (optional) Fields to show in the view, defaults to all fields.
4026
	 *
4027
	 * @return mixed
4028
	 * @since 2.3.10
4029
	 */
4030
	public function view( $fields = null ) {
4031
4032
		$pod =& $this;
4033
4034
		// Convert comma separated list of fields to an array.
4035
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
4036
			$fields = explode( ',', $fields );
4037
		}
4038
4039
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
4040
4041
		if ( empty( $fields ) ) {
4042
			// Add core object fields if $fields is empty.
4043
			$fields = array_merge( $object_fields, $this->fields );
4044
		}
4045
4046
		// Temporary.
4047
		$view_fields = $fields;
4048
4049
		$fields = array();
4050
4051
		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...
4052
			$defaults = array(
4053
				'name' => $name,
4054
			);
4055
4056
			if ( ! is_array( $field ) ) {
4057
				$name = $field;
4058
4059
				$field = array(
4060
					'name' => $name,
4061
				);
4062
			}
4063
4064
			// @codingStandardsIgnoreLine.
4065
			$field = array_merge( $defaults, $field );
4066
4067
			$field['name'] = trim( $field['name'] );
4068
4069
			if ( empty( $field['name'] ) ) {
4070
				$field['name'] = trim( $name );
4071
			}
4072
4073
			if ( isset( $object_fields[ $field['name'] ] ) ) {
4074
				// @codingStandardsIgnoreLine.
4075
				$field = array_merge( $field, $object_fields[ $field['name'] ] );
4076
			} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
4077
				// @codingStandardsIgnoreLine.
4078
				$field = array_merge( $this->fields[ $field['name'] ], $field );
4079
			}
4080
4081
			if ( pods_v( 'hidden', $field, false, true ) || 'hidden' === $field['type'] ) {
4082
				continue;
4083
			} elseif ( ! PodsForm::permission( $field['type'], $field['name'], $field['options'], $fields, $pod, $pod->id() ) ) {
4084
				continue;
4085
			}
4086
4087
			$fields[ $field['name'] ] = $field;
4088
		}//end foreach
4089
4090
		// Cleanup.
4091
		unset( $view_fields );
4092
4093
		$output = pods_view( PODS_DIR . 'ui/front/view.php', compact( array_keys( get_defined_vars() ) ), false, 'cache', true );
4094
4095
		return $this->do_hook( 'view', $output, $fields, $this->id() );
4096
4097
	}
4098
4099
	/**
4100
	 * Replace magic tags with their values
4101
	 *
4102
	 * @param string $code The content to evaluate.
4103
	 *
4104
	 * @return string Code with Magic Tags evaluated
4105
	 *
4106
	 * @since 2.0
4107
	 */
4108
	public function do_magic_tags( $code ) {
4109
4110
		/**
4111
		 * Filters the Pods magic tags content before the default function.
4112
		 * Allows complete replacement of the Pods magic tag engine.
4113
		 *
4114
		 * @param null   $pre  Default is null which processes magic tags normally. Return any other value to override.
4115
		 * @param string $code The content to evaluate.
4116
		 * @param Pods   $pods The Pods Object.
4117
		 *
4118
		 * @since 2.7
4119
		 */
4120
		$pre = apply_filters( 'pods_pre_do_magic_tags', null, $code, $this );
4121
		if ( null !== $pre ) {
4122
			return $pre;
4123
		}
4124
4125
		return preg_replace_callback( '/({@(.*?)})/m', array( $this, 'process_magic_tags' ), $code );
4126
	}
4127
4128
	/**
4129
	 * Replace magic tags with their values
4130
	 *
4131
	 * @param string|array $tag The magic tag to process.
4132
	 *
4133
	 * @return string Code with Magic Tags evaluated
4134
	 *
4135
	 * @since 2.0.2
4136
	 */
4137
	private function process_magic_tags( $tag ) {
4138
4139
		if ( is_array( $tag ) ) {
4140
			if ( ! isset( $tag[2] ) && '' === trim( $tag[2] ) ) {
4141
				return '';
4142
			}
4143
4144
			$tag = $tag[2];
4145
		}
4146
4147
		$tag = trim( $tag, ' {@}' );
4148
		$tag = explode( ',', $tag );
4149
4150
		if ( empty( $tag ) || ! isset( $tag[0] ) || '' === trim( $tag[0] ) ) {
4151
			return '';
4152
		}
4153
4154
		foreach ( $tag as $k => $v ) {
4155
			$tag[ $k ] = trim( $v );
4156
		}
4157
4158
		$field_name = $tag[0];
4159
4160
		$helper_name = '';
4161
		$before      = '';
4162
		$after       = '';
4163
4164
		if ( isset( $tag[1] ) && ! empty( $tag[1] ) ) {
4165
			$value = $this->field( $field_name );
4166
4167
			$helper_name = $tag[1];
4168
4169
			$value = $this->helper( $helper_name, $value, $field_name );
4170
		} else {
4171
			$value = $this->display( $field_name );
4172
		}
4173
4174
		if ( isset( $tag[2] ) && ! empty( $tag[2] ) ) {
4175
			$before = $tag[2];
4176
		}
4177
4178
		if ( isset( $tag[3] ) && ! empty( $tag[3] ) ) {
4179
			$after = $tag[3];
4180
		}
4181
4182
		/**
4183
		 * Filter the magic tag output for a value.
4184
		 *
4185
		 * @param string $value      Magic tag output for value.
4186
		 * @param string $field_name Magic tag field name.
4187
		 * @param string $before     Before content.
4188
		 * @param string $after      After content.
4189
		 */
4190
		$value = apply_filters( 'pods_do_magic_tags', $value, $field_name, $helper_name, $before, $after );
4191
4192
		if ( is_array( $value ) ) {
4193
			$value = pods_serial_comma( $value, array(
4194
				'field'  => $field_name,
4195
				'fields' => $this->fields,
4196
			) );
4197
		}
4198
4199
		if ( null !== $value && false !== $value ) {
4200
			return $before . $value . $after;
4201
		}
4202
4203
		return '';
4204
	}
4205
4206
	/**
4207
	 *
4208
	 * Generate UI for Data Management
4209
	 *
4210
	 * @param mixed $options Array or String containing Pod or Options to be used.
4211
	 * @param bool  $amend   Whether to amend the default UI options or replace entirely.
4212
	 *
4213
	 * @return PodsUI|null UI object or null if custom UI used
4214
	 *
4215
	 * @since 2.3.10
4216
	 */
4217
	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...
4218
4219
		$num = '';
4220
4221
		if ( empty( $options ) ) {
4222
			$options = array();
4223
		} else {
4224
			$num = pods_v_sanitized( 'num', $options, '' );
4225
4226
			if ( empty( $num ) ) {
4227
				$num = '';
4228
			}
4229
		}
4230
4231
		$check_id = pods_v( 'id' . $num, 'get', null, true );
4232
4233
		// @codingStandardsIgnoreLine
4234
		if ( $this->id() != $check_id ) {
4235
			$this->fetch( $check_id );
4236
		}
4237
4238
		if ( ! empty( $options ) && ! $amend ) {
4239
			$this->ui = $options;
4240
4241
			return pods_ui( $this );
4242
		} elseif ( ! empty( $options ) || 'custom' !== pods_v( 'ui_style', $this->pod_data['options'], 'post_type', true ) ) {
4243
			$actions_enabled = pods_v( 'ui_actions_enabled', $this->pod_data['options'] );
4244
4245
			if ( ! empty( $actions_enabled ) ) {
4246
				$actions_enabled = (array) $actions_enabled;
4247
			} else {
4248
				$actions_enabled = array();
4249
			}
4250
4251
			$available_actions = array(
4252
				'add',
4253
				'edit',
4254
				'duplicate',
4255
				'delete',
4256
				'reorder',
4257
				'export',
4258
			);
4259
4260
			if ( ! empty( $actions_enabled ) ) {
4261
				$actions_disabled = array(
4262
					'view' => 'view',
4263
				);
4264
4265
				foreach ( $available_actions as $action ) {
4266
					if ( ! in_array( $action, $actions_enabled, true ) ) {
4267
						$actions_disabled[ $action ] = $action;
4268
					}
4269
				}
4270
			} else {
4271
				$actions_disabled = array(
4272
					'duplicate' => 'duplicate',
4273
					'view'      => 'view',
4274
					'export'    => 'export',
4275
				);
4276
4277
				if ( 1 === pods_v( 'ui_export', $this->pod_data['options'], 0 ) ) {
4278
					unset( $actions_disabled['export'] );
4279
				}
4280
			}//end if
4281
4282
			if ( empty( $options ) ) {
4283
				$author_restrict = false;
4284
4285
				if ( isset( $this->fields['author'] ) && 'pick' === $this->fields['author']['type'] && 'user' === $this->fields['author']['pick_object'] ) {
4286
					$author_restrict = 'author.ID';
4287
				}
4288
4289
				if ( ! pods_is_admin( array( 'pods', 'pods_content' ) ) ) {
4290
					if ( ! current_user_can( 'pods_add_' . $this->pod ) ) {
4291
						$actions_disabled['add'] = 'add';
4292
4293
						if ( 'add' === pods_v( 'action' . $num, 'get' ) ) {
4294
							$_GET[ 'action' . $num ] = 'manage';
4295
						}
4296
					}
4297
4298
					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...
4299
						$actions_disabled['edit'] = 'edit';
4300
					}
4301
4302
					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...
4303
						$actions_disabled['delete'] = 'delete';
4304
					}
4305
4306
					if ( ! current_user_can( 'pods_reorder_' . $this->pod ) ) {
4307
						$actions_disabled['reorder'] = 'reorder';
4308
					}
4309
4310
					if ( ! current_user_can( 'pods_export_' . $this->pod ) ) {
4311
						$actions_disabled['export'] = 'export';
4312
					}
4313
				}//end if
4314
			}//end if
4315
4316
			$_GET[ 'action' . $num ] = pods_v_sanitized( 'action' . $num, 'get', pods_v( 'action', $options, 'manage' ), true );
4317
4318
			$index = $this->pod_data['field_id'];
4319
			$label = __( 'ID', 'pods' );
4320
4321
			if ( isset( $this->pod_data['fields'][ $this->pod_data['field_index'] ] ) ) {
4322
				$index = $this->pod_data['field_index'];
4323
				$label = $this->pod_data['fields'][ $this->pod_data['field_index'] ];
4324
			}
4325
4326
			$manage = array(
4327
				$index => $label,
4328
			);
4329
4330
			if ( isset( $this->pod_data['fields']['modified'] ) ) {
4331
				$manage['modified'] = $this->pod_data['fields']['modified']['label'];
4332
			}
4333
4334
			$manage_fields = (array) pods_v( 'ui_fields_manage', $this->pod_data['options'] );
4335
4336
			if ( ! empty( $manage_fields ) ) {
4337
				$manage_new = array();
4338
4339
				foreach ( $manage_fields as $manage_field ) {
4340
					if ( isset( $this->pod_data['fields'][ $manage_field ] ) ) {
4341
						$manage_new[ $manage_field ] = $this->pod_data['fields'][ $manage_field ];
4342
					} elseif ( isset( $this->pod_data['object_fields'][ $manage_field ] ) ) {
4343
						$manage_new[ $manage_field ] = $this->pod_data['object_fields'][ $manage_field ];
4344
					} elseif ( $manage_field === $this->pod_data['field_id'] ) {
4345
						$field = array(
4346
							'name'  => $manage_field,
4347
							'label' => 'ID',
4348
							'type'  => 'number',
4349
							'width' => '8%',
4350
						);
4351
4352
						$manage_new[ $manage_field ] = PodsForm::field_setup( $field, null, $field['type'] );
4353
					}
4354
				}
4355
4356
				if ( ! empty( $manage_new ) ) {
4357
					$manage = $manage_new;
4358
				}
4359
			}//end if
4360
4361
			$manage = apply_filters( 'pods_admin_ui_fields_' . $this->pod, apply_filters( 'pods_admin_ui_fields', $manage, $this->pod, $this ), $this->pod, $this );
4362
4363
			$icon = pods_v( 'ui_icon', $this->pod_data['options'] );
4364
4365
			if ( ! empty( $icon ) ) {
4366
				$icon = pods_image_url( $icon, '32x32' );
4367
			}
4368
4369
			$filters = pods_v( 'ui_filters', $this->pod_data['options'] );
4370
4371
			if ( ! empty( $filters ) ) {
4372
				$filters_new = array();
4373
4374
				$filters = (array) $filters;
4375
4376
				foreach ( $filters as $filter_field ) {
4377
					if ( isset( $this->pod_data['fields'][ $filter_field ] ) ) {
4378
						$filters_new[ $filter_field ] = $this->pod_data['fields'][ $filter_field ];
4379
					} elseif ( isset( $this->pod_data['object_fields'][ $filter_field ] ) ) {
4380
						$filters_new[ $filter_field ] = $this->pod_data['object_fields'][ $filter_field ];
4381
					}
4382
				}
4383
4384
				$filters = $filters_new;
4385
			}
4386
4387
			$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...
4388
				'fields'           => array(
4389
					'manage'    => $manage,
4390
					'add'       => $this->pod_data['fields'],
4391
					'edit'      => $this->pod_data['fields'],
4392
					'duplicate' => $this->pod_data['fields'],
4393
				),
4394
				'icon'             => $icon,
4395
				'actions_disabled' => $actions_disabled,
4396
				'actions_bulk'     => array(),
4397
			);
4398
4399
			if ( ! empty( $filters ) ) {
4400
				$ui['fields']['search'] = $filters;
4401
				$ui['filters']          = array_keys( $filters );
4402
				$ui['filters_enhanced'] = true;
4403
			}
4404
4405
			$reorder_field = pods_v( 'ui_reorder_field', $this->pod_data['options'] );
4406
4407
			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 ] ) ) ) {
4408
				$ui['reorder']     = array( 'on' => $reorder_field );
4409
				$ui['orderby']     = $reorder_field;
4410
				$ui['orderby_dir'] = 'ASC';
4411
			}
4412
4413
			if ( ! empty( $author_restrict ) ) {
4414
				$ui['restrict'] = array( 'author_restrict' => $author_restrict );
4415
			}
4416
4417
			if ( ! in_array( 'export', $ui['actions_disabled'], true ) ) {
4418
				$ui['actions_bulk']['export'] = array(
4419
					'label' => __( 'Export', 'pods' ),
4420
					// callback not needed, Pods has this built-in for export.
4421
				);
4422
			}
4423
4424
			if ( ! in_array( 'delete', $ui['actions_disabled'], true ) ) {
4425
				$ui['actions_bulk']['delete'] = array(
4426
					'label' => __( 'Delete', 'pods' ),
4427
					// callback not needed, Pods has this built-in for delete.
4428
				);
4429
			}
4430
4431
			$detail_url = pods_v( 'detail_url', $this->pod_data['options'] );
4432
4433
			if ( 0 < strlen( $detail_url ) ) {
4434
				$ui['actions_custom'] = array(
4435
					'view_url' => array(
4436
						'label' => 'View',
4437
						'link'  => get_site_url() . '/' . $detail_url,
4438
					),
4439
				);
4440
			}
4441
4442
			// @todo Customize the Add New / Manage links to point to their correct menu items.
4443
			$ui = apply_filters( 'pods_admin_ui_' . $this->pod, apply_filters( 'pods_admin_ui', $ui, $this->pod, $this ), $this->pod, $this );
4444
4445
			// Override UI options.
4446
			foreach ( $options as $option => $value ) {
4447
				$ui[ $option ] = $value;
4448
			}
4449
4450
			$this->ui = $ui;
4451
4452
			return pods_ui( $this );
4453
		}//end if
4454
4455
		do_action( 'pods_admin_ui_custom', $this );
4456
		do_action( 'pods_admin_ui_custom_' . $this->pod, $this );
4457
4458
		return null;
4459
	}
4460
4461
	/**
4462
	 * Handle filters / actions for the class
4463
	 *
4464
	 * @see   pods_do_hook
4465
	 *
4466
	 * @param string $name Hook name.
4467
	 *
4468
	 * @return mixed Value filtered
4469
	 *
4470
	 * @since 2.0
4471
	 */
4472
	private function do_hook( $name ) {
4473
4474
		$args = func_get_args();
4475
4476
		if ( empty( $args ) ) {
4477
			return false;
4478
		}
4479
4480
		// Remove first argument.
4481
		array_shift( $args );
4482
4483
		return pods_do_hook( 'pods', $name, $args, $this );
4484
	}
4485
4486
	/**
4487
	 * Handle variables that have been deprecated and PodsData vars
4488
	 *
4489
	 * @param string $name Property name.
4490
	 *
4491
	 * @return mixed
4492
	 *
4493
	 * @since 2.0
4494
	 */
4495
	public function __get( $name ) {
4496
4497
		$name = (string) $name;
4498
4499
		// PodsData vars.
4500
		if ( isset( $this->data->{$name} ) && 0 === strpos( $name, 'field_' ) ) {
4501
			return $this->data->{$name};
4502
		}
4503
4504
		if ( ! $this->deprecated ) {
4505
			require_once PODS_DIR . 'deprecated/classes/Pods.php';
4506
4507
			$this->deprecated = new Pods_Deprecated( $this );
4508
		}
4509
4510
		$var = null;
4511
4512
		$pod_class_exists = class_exists( 'Pod' );
4513
4514
		if ( isset( $this->deprecated->{$name} ) ) {
4515
			if ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4516
				pods_deprecated( "Pods->{$name}", '2.0' );
4517
			}
4518
4519
			$var = $this->deprecated->{$name};
4520
		} elseif ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4521
			pods_deprecated( "Pods->{$name}", '2.0' );
4522
		}
4523
4524
		return $var;
4525
	}
4526
4527
	/**
4528
	 * Handle methods that have been deprecated and any aliasing.
4529
	 *
4530
	 * @param string $name Function name.
4531
	 * @param array  $args Arguments passed to function.
4532
	 *
4533
	 * @return mixed|null
4534
	 *
4535
	 * @since 2.0
4536
	 */
4537
	public function __call( $name, $args ) {
4538
4539
		$name = (string) $name;
4540
4541
		// select > find alias.
4542
		if ( 'select' === $name ) {
4543
			return call_user_func_array( array( $this, 'find' ), $args );
4544
		}
4545
4546
		if ( ! $this->deprecated ) {
4547
			require_once PODS_DIR . 'deprecated/classes/Pods.php';
4548
4549
			$this->deprecated = new Pods_Deprecated( $this );
4550
		}
4551
4552
		$pod_class_exists = class_exists( 'Pod' );
4553
4554
		if ( method_exists( $this->deprecated, $name ) ) {
4555
			return call_user_func_array( array( $this->deprecated, $name ), $args );
4556
			// @codingStandardsIgnoreLine
4557
		} elseif ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4558
			pods_deprecated( "Pods::{$name}", '2.0' );
4559
		}
4560
4561
		return null;
4562
	}
4563
4564
	/**
4565
	 * Handle casting a Pods() object to string
4566
	 *
4567
	 * @return string Pod type and name in CURIE notation
4568
	 */
4569
	public function __toString() {
4570
4571
		$string = '';
4572
4573
		if ( ! empty( $this->pod_data ) ) {
4574
			$string = sprintf( '%s:%s', $this->pod_data['type'], $this->pod_data['name'] );
4575
		}
4576
4577
		return $string;
4578
4579
	}
4580
}
4581