Issues (2873)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

classes/Pods.php (65 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
Should the type for parameter $pod not be string|null?

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

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

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

Loading history...
277
	 * @param mixed  $id  (optional) The ID or slug, to load a single record; Provide array of $params to run 'find'.
278
	 *
279
	 * @license http://www.gnu.org/licenses/gpl-2.0.html
280
	 * @since   1.0.0
281
	 * @link    https://pods.io/docs/pods/
282
	 */
283
	public function __construct( $pod = null, $id = null ) {
284
285
		if ( null === $pod ) {
286
			$queried_object = get_queried_object();
287
288
			if ( $queried_object ) {
289
				$id_lookup = true;
290
291
				if ( isset( $queried_object->post_type ) ) {
292
					// Post Type Singular.
293
					$pod = $queried_object->post_type;
294
				} elseif ( isset( $queried_object->taxonomy ) ) {
295
					// Term Archive.
296
					$pod = $queried_object->taxonomy;
297
				} elseif ( isset( $queried_object->user_login ) ) {
298
					// Author Archive.
299
					$pod = 'user';
300
				} elseif ( isset( $queried_object->public, $queried_object->name ) ) {
301
					// Post Type Archive.
302
					$pod = $queried_object->name;
303
304
					$id_lookup = false;
305
				}
306
307
				if ( null === $id && $id_lookup ) {
308
					$id = get_queried_object_id();
309
				}
310
			}//end if
311
		}//end if
312
313
		$this->api                 = pods_api( $pod );
314
		$this->api->display_errors =& $this->display_errors;
315
316
		$this->data               = pods_data( $this->api, $id, false );
317
		PodsData::$display_errors =& $this->display_errors;
318
319
		// Set up page variable.
320
		if ( pods_strict( false ) ) {
321
			$this->page       = 1;
322
			$this->pagination = false;
323
			$this->search     = false;
324
		} else {
325
			// Get the page variable.
326
			$this->page = pods_v( $this->page_var, 'get', 1, true );
327
328
			if ( ! empty( $this->page ) ) {
329
				$this->page = max( 1, pods_absint( $this->page ) );
330
			}
331
		}
332
333
		// Set default pagination handling to on/off.
334
		if ( defined( 'PODS_GLOBAL_POD_PAGINATION' ) ) {
335
			if ( ! PODS_GLOBAL_POD_PAGINATION ) {
336
				$this->page       = 1;
337
				$this->pagination = false;
338
			} else {
339
				$this->pagination = true;
340
			}
341
		}
342
343
		// Set default search to on/off.
344
		if ( defined( 'PODS_GLOBAL_POD_SEARCH' ) ) {
345
			if ( PODS_GLOBAL_POD_SEARCH ) {
346
				$this->search = true;
347
			} else {
348
				$this->search = false;
349
			}
350
		}
351
352
		// Set default search mode.
353
		$allowed_search_modes = array( 'int', 'text', 'text_like' );
354
355
		if ( defined( 'PODS_GLOBAL_POD_SEARCH_MODE' ) && in_array( PODS_GLOBAL_POD_SEARCH_MODE, $allowed_search_modes, true ) ) {
356
			$this->search_mode = PODS_GLOBAL_POD_SEARCH_MODE;
357
		}
358
359
		// Sync Settings.
360
		$this->data->page        =& $this->page;
361
		$this->data->limit       =& $this->limit;
362
		$this->data->pagination  =& $this->pagination;
363
		$this->data->search      =& $this->search;
364
		$this->data->search_mode =& $this->search_mode;
365
366
		// Sync Pod Data.
367
		$this->api->pod_data =& $this->data->pod_data;
368
		$this->pod_data      =& $this->api->pod_data;
369
		$this->api->pod_id   =& $this->data->pod_id;
0 ignored issues
show
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
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
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
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
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
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
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
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
		if ( is_object( $name ) ) {
796
			$name = get_object_vars( $name );
797
		}
798
799
		if ( is_object( $single ) ) {
800
			$single = get_object_vars( $single );
801
		}
802
803
		if ( is_object( $raw ) ) {
804
			$raw = get_object_vars( $raw );
805
		}
806
807
		if ( is_array( $name ) ) {
808
			$defaults['name'] = null;
809
810
			$params = (object) array_merge( $defaults, (array) $name );
811
		} elseif ( is_array( $single ) ) {
812
			$defaults['single'] = null;
813
814
			$params = (object) array_merge( $defaults, (array) $single );
815
		} elseif ( is_array( $raw ) ) {
816
			$defaults['raw'] = false;
817
818
			$params = (object) array_merge( $defaults, (array) $raw );
819
		} else {
820
			$params = (object) $defaults;
821
		}//end if
822
823
		if ( $params->in_form ) {
824
			$params->output = 'ids';
825
		} elseif ( null === $params->output ) {
826
			/**
827
			 * Override the way related fields are output
828
			 *
829
			 * @param string       $output How to output related fields. Default is 'arrays'. Options: ids|names|objects|arrays|pods|find
830
			 * @param array|object $row    Current row being outputted.
831
			 * @param array        $params Params array passed to field().
832
			 * @param Pods         $this   Current Pods object.
833
			 */
834
			$params->output = apply_filters( 'pods_pods_field_related_output_type', 'arrays', $this->row, $params, $this );
835
		}
836
837
		if ( in_array( $params->output, array( 'id', 'name', 'object', 'array', 'pod' ), true ) ) {
838
			$params->output .= 's';
839
		}
840
841
		// Support old $orderby variable.
842
		if ( null !== $params->single && is_string( $params->single ) && empty( $params->orderby ) ) {
843
			if ( ! class_exists( 'Pod' ) || Pod::$deprecated_notice ) {
844
				pods_deprecated( 'Pods::field', '2.0', 'Use $params[ \'orderby\' ] instead' );
845
			}
846
847
			$params->orderby = $params->single;
848
			$params->single  = false;
849
		}
850
851
		if ( null !== $params->single ) {
852
			$params->single = (boolean) $params->single;
853
		}
854
855
		$params->name = trim( $params->name );
856
		if ( is_array( $params->name ) || '' === $params->name ) {
857
			return null;
858
		}
859
860
		$params->full_name = $params->name;
861
862
		$value = null;
863
864
		if ( isset( $this->row_override[ $params->name ] ) ) {
865
			$value = $this->row_override[ $params->name ];
866
		}
867
868
		if ( false === $this->row() ) {
869
			if ( false !== $this->data() ) {
870
				$this->fetch();
871
			} else {
872
				return $value;
873
			}
874
		}
875
876
		if ( $this->data->field_id === $params->name ) {
877
			if ( isset( $this->row[ $params->name ] ) ) {
878
				return $this->row[ $params->name ];
879
				// @codingStandardsIgnoreLine.
880
			} elseif ( null !== $value ) {
881
				return $value;
882
			}
883
884
			return 0;
885
		}
886
887
		$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...
888
		$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...
889
890
		$params->traverse = array();
891
892
		if ( in_array( $params->name, array(
893
			'_link',
894
			'detail_url',
895
		), true ) || ( in_array( $params->name, array(
896
			'permalink',
897
			'the_permalink',
898
		), true ) && in_array( $this->pod_data['type'], array(
899
			'post_type',
900
			'taxonomy',
901
			'media',
902
			'user',
903
			'comment',
904
		), true ) ) ) {
905
			if ( 0 < strlen( $this->detail_page ) ) {
906
				$value = get_home_url() . '/' . $this->do_magic_tags( $this->detail_page );
907
			} elseif ( in_array( $this->pod_data['type'], array( 'post_type', 'media' ), true ) ) {
908
				$value = get_permalink( $this->id() );
909
			} elseif ( 'taxonomy' === $this->pod_data['type'] ) {
910
				$value = get_term_link( $this->id(), $this->pod_data['name'] );
911
			} elseif ( 'user' === $this->pod_data['type'] ) {
912
				$value = get_author_posts_url( $this->id() );
913
			} elseif ( 'comment' === $this->pod_data['type'] ) {
914
				$value = get_comment_link( $this->id() );
915
			}
916
		}
917
918
		$field_data      = false;
919
		$last_field_data = false;
920
		$field_type      = false;
921
922
		$first_field = explode( '.', $params->name );
923
		$first_field = $first_field[0];
924
925
		if ( isset( $this->fields[ $first_field ] ) ) {
926
			$field_data = $this->fields[ $first_field ];
927
			$field_type = 'field';
928
		} elseif ( ! empty( $this->pod_data['object_fields'] ) ) {
929
			if ( isset( $this->pod_data['object_fields'][ $first_field ] ) ) {
930
				$field_data = $this->pod_data['object_fields'][ $first_field ];
931
				$field_type = 'object_field';
932
			} else {
933
				$object_fields = (array) $this->pod_data['object_fields'];
934
935
				foreach ( $object_fields as $object_field => $object_field_opt ) {
936
					if ( in_array( $first_field, $object_field_opt['alias'], true ) ) {
937
						if ( $first_field === $params->name ) {
938
							$params->name = $object_field;
939
						}
940
941
						$first_field = $object_field;
942
						$field_data  = $object_field_opt;
943
						$field_type  = 'object_field';
944
945
						break;
946
					}
947
				}
948
			}
949
		}//end if
950
951
		// Simple fields have no other output options.
952
		if ( 'pick' === $field_data['type'] && in_array( $field_data['pick_object'], $simple_tableless_objects, true ) ) {
953
			$params->output = 'arrays';
954
		}
955
956
		if ( empty( $value ) && in_array( $field_data['type'], $tableless_field_types, true ) ) {
957
			$params->raw = true;
958
959
			$value = false;
960
961
			$row_key = sprintf( '_%s_%s', $params->output, $params->name );
962
963
			if ( 'arrays' !== $params->output && isset( $this->row[ $row_key ] ) ) {
964
				$value = $this->row[ '_' . $params->output . '_' . $params->name ];
965
			} elseif ( 'arrays' === $params->output && isset( $this->row[ $params->name ] ) ) {
966
				$value = $this->row[ $params->name ];
967
			}
968
969
			if ( false !== $value && ! is_array( $value ) && 'pick' === $field_data['type'] && in_array( $field_data['pick_object'], $simple_tableless_objects, true ) ) {
970
				$value = PodsForm::field_method( 'pick', 'simple_value', $params->name, $value, $field_data, $this->pod_data, $this->id(), true );
971
			}
972
		}
973
974
		if ( empty( $value ) && isset( $this->row[ $params->name ] ) && ( ! in_array( $field_data['type'], $tableless_field_types, true ) || 'arrays' === $params->output ) ) {
975
			if ( empty( $field_data ) || in_array( $field_data['type'], array(
976
				'boolean',
977
				'number',
978
				'currency',
979
			), true ) ) {
980
				$params->raw = true;
981
			}
982
983
			if ( null === $params->single ) {
984
				if ( isset( $this->fields[ $params->name ] ) && ! in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true ) ) {
985
					$params->single = true;
986
				} else {
987
					$params->single = false;
988
				}
989
			}
990
991
			$value = $this->row[ $params->name ];
992
		} elseif ( empty( $value ) ) {
993
			$object_field_found = false;
994
995
			if ( 'object_field' === $field_type ) {
996
				$object_field_found = true;
997
998
				if ( isset( $this->row[ $first_field ] ) ) {
999
					$value = $this->row[ $first_field ];
1000
				} elseif ( in_array( $field_data['type'], $tableless_field_types, true ) ) {
1001
					$this->fields[ $first_field ] = $field_data;
1002
1003
					$object_field_found = false;
1004
				} else {
1005
					return null;
1006
				}
1007
			}
1008
1009
			if ( 'post_type' === $this->pod_data['type'] && ! isset( $this->fields[ $params->name ] ) ) {
1010
				if ( ! isset( $this->fields['post_thumbnail'] ) && ( 'post_thumbnail' === $params->name || 0 === strpos( $params->name, 'post_thumbnail.' ) ) ) {
1011
					$size = 'thumbnail';
1012
1013
					if ( 0 === strpos( $params->name, 'post_thumbnail.' ) ) {
1014
						$field_names = explode( '.', $params->name );
1015
1016
						if ( isset( $field_names[1] ) ) {
1017
							$size = $field_names[1];
1018
						}
1019
					}
1020
1021
					// Pods will auto-get the thumbnail ID if this isn't an attachment.
1022
					$value = pods_image( $this->id(), $size, 0, null, true );
1023
1024
					$object_field_found = true;
1025
				} elseif ( ! isset( $this->fields['post_thumbnail_url'] ) && ( 'post_thumbnail_url' === $params->name || 0 === strpos( $params->name, 'post_thumbnail_url.' ) ) ) {
1026
					$size = 'thumbnail';
1027
1028
					if ( 0 === strpos( $params->name, 'post_thumbnail_url.' ) ) {
1029
						$field_names = explode( '.', $params->name );
1030
1031
						if ( isset( $field_names[1] ) ) {
1032
							$size = $field_names[1];
1033
						}
1034
					}
1035
1036
					// Pods will auto-get the thumbnail ID if this isn't an attachment.
1037
					$value = pods_image_url( $this->id(), $size, 0, true );
1038
1039
					$object_field_found = true;
1040
				} elseif ( 0 === strpos( $params->name, 'image_attachment.' ) ) {
1041
					$size = 'thumbnail';
1042
1043
					$image_id = 0;
1044
1045
					$field_names = explode( '.', $params->name );
1046
1047
					if ( isset( $field_names[1] ) ) {
1048
						$image_id = $field_names[1];
1049
					}
1050
1051
					if ( isset( $field_names[2] ) ) {
1052
						$size = $field_names[2];
1053
					}
1054
1055
					if ( ! empty( $image_id ) ) {
1056
						$value = pods_image( $image_id, $size, 0, null, true );
1057
1058
						if ( ! empty( $value ) ) {
1059
							$object_field_found = true;
1060
						}
1061
					}
1062
				} elseif ( 0 === strpos( $params->name, 'image_attachment_url.' ) ) {
1063
					$size = 'thumbnail';
1064
1065
					$image_id = 0;
1066
1067
					$field_names = explode( '.', $params->name );
1068
1069
					if ( isset( $field_names[1] ) ) {
1070
						$image_id = $field_names[1];
1071
					}
1072
1073
					if ( isset( $field_names[2] ) ) {
1074
						$size = $field_names[2];
1075
					}
1076
1077
					if ( ! empty( $image_id ) ) {
1078
						$value = pods_image_url( $image_id, $size, 0, true );
1079
1080
						if ( ! empty( $value ) ) {
1081
							$object_field_found = true;
1082
						}
1083
					}
1084
				}//end if
1085
			} elseif ( 'user' === $this->pod_data['type'] && ! isset( $this->fields[ $params->name ] ) ) {
1086
				if ( ! isset( $this->fields['avatar'] ) && ( 'avatar' === $params->name || 0 === strpos( $params->name, 'avatar.' ) ) ) {
1087
					$size = null;
1088
1089
					if ( 0 === strpos( $params->name, 'avatar.' ) ) {
1090
						$field_names = explode( '.', $params->name );
1091
1092
						if ( isset( $field_names[1] ) ) {
1093
							$size = (int) $field_names[1];
1094
						}
1095
					}
1096
1097
					if ( 0 < $size ) {
1098
						$value = get_avatar( $this->id(), $size );
1099
					} else {
1100
						$value = get_avatar( $this->id() );
1101
					}
1102
1103
					$object_field_found = true;
1104
				}
1105
			} elseif ( 0 === strpos( $params->name, 'image_attachment.' ) ) {
1106
				$size = 'thumbnail';
1107
1108
				$image_id = 0;
1109
1110
				$field_names = explode( '.', $params->name );
1111
1112
				if ( isset( $field_names[1] ) ) {
1113
					$image_id = $field_names[1];
1114
				}
1115
1116
				if ( isset( $field_names[2] ) ) {
1117
					$size = $field_names[2];
1118
				}
1119
1120
				if ( ! empty( $image_id ) ) {
1121
					$value = pods_image( $image_id, $size, 0, null, true );
1122
1123
					if ( ! empty( $value ) ) {
1124
						$object_field_found = true;
1125
					}
1126
				}
1127
			} elseif ( 0 === strpos( $params->name, 'image_attachment_url.' ) ) {
1128
				$size = 'thumbnail';
1129
1130
				$image_id = 0;
1131
1132
				$field_names = explode( '.', $params->name );
1133
1134
				if ( isset( $field_names[1] ) ) {
1135
					$image_id = $field_names[1];
1136
				}
1137
1138
				if ( isset( $field_names[2] ) ) {
1139
					$size = $field_names[2];
1140
				}
1141
1142
				if ( ! empty( $image_id ) ) {
1143
					$value = pods_image_url( $image_id, $size, 0, true );
1144
1145
					if ( ! empty( $value ) ) {
1146
						$object_field_found = true;
1147
					}
1148
				}
1149
			}//end if
1150
1151
			if ( false === $object_field_found ) {
1152
				$params->traverse = array( $params->name );
1153
1154
				if ( false !== strpos( $params->name, '.' ) ) {
1155
					$params->traverse = explode( '.', $params->name );
1156
1157
					$params->name = $params->traverse[0];
1158
				}
1159
1160
				if ( isset( $this->fields[ $params->name ], $this->fields[ $params->name ]['type'] ) ) {
1161
					/**
1162
					 * Modify value returned by field() after its retrieved, but before its validated or formatted
1163
					 *
1164
					 * Filter name is set dynamically with name of field: "pods_pods_field_{field_name}"
1165
					 *
1166
					 * @since unknown
1167
					 *
1168
					 * @param array|string|null $value  Value retrieved.
1169
					 * @param array|object      $row    Current row being outputted.
1170
					 * @param array             $params Params array passed to field().
1171
					 * @param object|Pods       $this   Current Pods object.
1172
					 */
1173
					$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...
1174
1175
					if ( null !== $v ) {
1176
						return $v;
1177
					}
1178
				}
1179
1180
				$simple = false;
1181
1182
				if ( isset( $this->fields[ $params->name ] ) ) {
1183
					if ( 'meta' === $this->pod_data['storage'] && ! in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true ) ) {
1184
						$simple = true;
1185
					}
1186
1187
					if ( in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true ) ) {
1188
						$params->raw = true;
1189
1190
						if ( 'pick' === $this->fields[ $params->name ]['type'] && in_array( $this->fields[ $params->name ]['pick_object'], $simple_tableless_objects, true ) ) {
1191
							$simple         = true;
1192
							$params->single = true;
1193
						}
1194
					} elseif ( in_array( $this->fields[ $params->name ]['type'], array(
1195
						'boolean',
1196
						'number',
1197
						'currency',
1198
					), true ) ) {
1199
						$params->raw = true;
1200
					}
1201
				}
1202
1203
				$is_field_set       = isset( $this->fields[ $params->name ] );
1204
				$is_tableless_field = false;
1205
1206
				if ( $is_field_set ) {
1207
					$is_tableless_field = in_array( $this->fields[ $params->name ]['type'], $tableless_field_types, true );
1208
				}
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 ( $is_field_set ) {
1256
							$single_multi = pods_v( $this->fields[ $params->name ]['type'] . '_format_type', $this->fields[ $params->name ]['options'], $single_multi );
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 ( ! empty( $related_obj ) && 0 === strpos( $full_field, 'post_thumbnail' ) ) {
1695
											// We want to catch post_thumbnail and post_thumbnail_url here
1696
											$value[] = $related_obj->field( $full_field );
1697
										} elseif ( ( ( false !== strpos( $full_field, '_src' ) || 'guid' === $field ) && ( in_array( $table['type'], array(
1698
											'attachment',
1699
											'media',
1700
										), true ) || in_array( $last_type, PodsForm::file_field_types(), true ) ) ) || ( in_array( $field, array(
1701
											'_link',
1702
											'detail_url',
1703
										), true ) || in_array( $field, array(
1704
											'permalink',
1705
											'the_permalink',
1706
										), true ) && in_array( $last_type, PodsForm::file_field_types(), true ) ) ) {
1707
											// @todo Refactor the above condition statement.
1708
											$size = 'full';
1709
1710
											if ( false === strpos( 'image', get_post_mime_type( $item_id ) ) ) {
1711
												// No default sizes for non-images.
1712
												// When a size is defined this will be overwritten.
1713
												$size = null;
1714
											}
1715
1716
											if ( false !== strpos( $full_field, '_src.' ) && 5 < strlen( $full_field ) ) {
1717
												$size = substr( $full_field, 5 );
1718
											} elseif ( false !== strpos( $full_field, '_src_relative.' ) && 14 < strlen( $full_field ) ) {
1719
												$size = substr( $full_field, 14 );
1720
											} elseif ( false !== strpos( $full_field, '_src_schemeless.' ) && 16 < strlen( $full_field ) ) {
1721
												$size = substr( $full_field, 16 );
1722
											}
1723
1724
											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...
1725
												$value_url = pods_image_url( $item_id, $size );
1726
											} else {
1727
												$value_url = wp_get_attachment_url( $item_id );
1728
											}
1729
1730
											if ( ! empty( $value_url ) ) {
1731
												if ( false !== strpos( $full_field, '_src_relative' ) ) {
1732
													$value_url_parsed = wp_parse_url( $value_url );
1733
													$value_url        = $value_url_parsed['path'];
1734
												} elseif ( false !== strpos( $full_field, '_src_schemeless' ) ) {
1735
													$value_url = str_replace( array(
1736
														'http://',
1737
														'https://',
1738
													), '//', $value_url );
1739
												}
1740
											}
1741
1742
											if ( ! empty( $value_url ) ) {
1743
												$value[] = $value_url;
1744
											}
1745
1746
											$params->raw_display = true;
1747
										} elseif ( false !== strpos( $full_field, '_img' ) && ( in_array( $table['type'], array(
1748
											'attachment',
1749
											'media',
1750
										), true ) || in_array( $last_type, PodsForm::file_field_types(), true ) ) ) {
1751
											$size = 'full';
1752
1753
											if ( false !== strpos( $full_field, '_img.' ) && 5 < strlen( $full_field ) ) {
1754
												$size = substr( $full_field, 5 );
1755
											}
1756
1757
											$value[] = pods_image( $item_id, $size );
1758
1759
											$params->raw_display = true;
1760
										} elseif ( in_array( $field, array(
1761
											'_link',
1762
											'detail_url',
1763
										), true ) || in_array( $field, array( 'permalink', 'the_permalink' ), true ) ) {
1764
											if ( 'pod' === $object_type ) {
1765
												if ( is_object( $related_obj ) ) {
1766
													$related_obj->fetch( $item_id );
1767
1768
													$value[] = $related_obj->field( 'detail_url' );
1769
												} else {
1770
													$value[] = '';
1771
												}
1772
											} elseif ( 'post' === $object_type ) {
1773
												$value[] = get_permalink( $item_id );
1774
											} elseif ( 'taxonomy' === $object_type ) {
1775
												$value[] = get_term_link( $item_id, $object );
1776
											} elseif ( 'user' === $object_type ) {
1777
												$value[] = get_author_posts_url( $item_id );
1778
											} elseif ( 'comment' === $object_type ) {
1779
												$value[] = get_comment_link( $item_id );
1780
											} else {
1781
												$value[] = '';
1782
											}
1783
1784
											$params->raw_display = true;
1785
										} elseif ( in_array( $object_type, array(
1786
											'post',
1787
											'taxonomy',
1788
											'user',
1789
											'comment',
1790
										), true ) ) {
1791
											$metadata_object_id = $item_id;
1792
1793
											$metadata_type = $object_type;
1794
1795
											if ( 'post' === $object_type ) {
1796
												// Support for WPML 'duplicated' translation handling.
1797
												if ( did_action( 'wpml_loaded' ) && apply_filters( 'wpml_is_translated_post_type', false, $object ) ) {
1798
													$master_post_id = (int) apply_filters( 'wpml_master_post_from_duplicate', $metadata_object_id );
1799
1800
													if ( 0 < $master_post_id ) {
1801
														$metadata_object_id = $master_post_id;
1802
													}
1803
												}
1804
											} elseif ( 'taxonomy' === $object_type ) {
1805
												$metadata_type = 'term';
1806
											}
1807
1808
											$value[] = get_metadata( $metadata_type, $metadata_object_id, $field, true );
1809
										} elseif ( 'settings' === $object_type ) {
1810
											$value[] = get_option( $object . '_' . $field );
1811
										}//end if
1812
									}//end foreach
1813
								}//end if
1814
1815
								if ( ! $no_conflict && in_array( $object_type, array(
1816
									'post',
1817
									'taxonomy',
1818
									'user',
1819
									'comment',
1820
									'settings',
1821
								), true ) ) {
1822
									pods_no_conflict_off( $object_type );
1823
								}
1824
1825
								// Handle Simple Relationships.
1826
								if ( $simple ) {
1827
									if ( null === $params->single ) {
1828
										$params->single = false;
1829
									}
1830
1831
									$value = PodsForm::field_method( 'pick', 'simple_value', $field, $value, $last_options, $all_fields[ $pod ], 0, true );
1832
								} elseif ( false === $params->in_form && ! empty( $value ) && is_array( $value ) ) {
1833
									$value = array_values( $value );
1834
								}
1835
1836
								// Return a single column value.
1837
								if ( false === $params->in_form && 1 === $limit && ! empty( $value ) && is_array( $value ) && 1 === count( $value ) ) {
1838
									$value = current( $value );
1839
								}
1840
							}//end if
1841
1842
							if ( $last_options ) {
1843
								$last_field_data = $last_options;
1844
							}
1845
1846
							break;
1847
						}//end if
1848
					}//end foreach
1849
				}//end if
1850
			}//end if
1851
		}//end if
1852
1853
		if ( ! empty( $params->traverse ) && 1 < count( $params->traverse ) ) {
1854
			$field_names = implode( '.', $params->traverse );
1855
1856
			$this->row[ $field_names ] = $value;
1857
		} elseif ( 'arrays' !== $params->output && in_array( $field_data['type'], $tableless_field_types, true ) ) {
1858
			$this->row[ '_' . $params->output . '_' . $params->full_name ] = $value;
1859
		} elseif ( 'arrays' === $params->output || ! in_array( $field_data['type'], $tableless_field_types, true ) ) {
1860
			$this->row[ $params->full_name ] = $value;
1861
		}
1862
1863
		if ( true === $params->single && is_array( $value ) && 1 === count( $value ) ) {
1864
			$value = current( $value );
1865
		}
1866
1867
		if ( ! empty( $last_field_data ) ) {
1868
			$field_data = $last_field_data;
1869
		}
1870
1871
		// @todo Expand this into traversed fields too.
1872
		if ( ! empty( $field_data ) && ( $params->display || ! $params->raw ) && ! $params->in_form && ! $params->raw_display ) {
1873
			if ( $params->display || ( ( $params->get_meta || $params->deprecated ) && ! in_array( $field_data['type'], $tableless_field_types, true ) ) ) {
1874
				$field_data['options'] = pods_v( 'options', $field_data, array(), true );
1875
1876
				$post_temp   = false;
1877
				$old_post    = null;
1878
				$old_post_id = null;
1879
1880
				if ( empty( $GLOBALS['post'] ) && 'post_type' === pods_v( 'type', $this->pod_data ) && 0 < $this->id() ) {
1881
					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...
1882
1883
					$post_temp = true;
1884
1885
					$old_post    = $post;
1886
					$old_post_id = $post_ID;
1887
1888
					// @codingStandardsIgnoreLine.
1889
					$post    = get_post( $this->id() );
1890
					// @codingStandardsIgnoreLine.
1891
					$post_ID = $this->id();
1892
				}
1893
1894
				$filter = pods_v( 'display_filter', $field_data['options'] );
1895
1896
				if ( 0 < strlen( $filter ) ) {
1897
					$args = array(
1898
						$filter,
1899
						$value,
1900
					);
1901
1902
					$filter_args = pods_v( 'display_filter_args', $field_data['options'] );
1903
1904
					if ( ! empty( $filter_args ) ) {
1905
						$args = array_merge( $args, compact( $filter_args ) );
1906
					}
1907
1908
					$value = call_user_func_array( 'apply_filters', $args );
1909
				} elseif ( 1 === (int) pods_v( 'display_process', $field_data['options'], 1 ) ) {
1910
					$value = PodsForm::display( $field_data['type'], $value, $params->name, array_merge( $field_data, $field_data['options'] ), $this->pod_data, $this->id() );
1911
				}
1912
1913
				if ( $post_temp ) {
1914
					// @codingStandardsIgnoreLine.
1915
					$post    = $old_post;
1916
					// @codingStandardsIgnoreLine.
1917
					$post_ID = $old_post_id;
1918
				}
1919
			} else {
1920
				$value = PodsForm::value( $field_data['type'], $value, $params->name, array_merge( $field_data, $field_data['options'] ), $this->pod_data, $this->id() );
1921
			}//end if
1922
		}//end if
1923
1924
		/**
1925
		 * Modify value returned by field() directly before output.
1926
		 *
1927
		 * Will not run if value was null
1928
		 *
1929
		 * @since unknown
1930
		 *
1931
		 * @param array|string|null $value  Value to be returned.
1932
		 * @param array|object      $row    Current row being outputted.
1933
		 * @param array             $params Params array passed to field().
1934
		 * @param object|Pods       $this   Current Pods object.
1935
		 */
1936
		$value = apply_filters( 'pods_pods_field', $value, $this->row, $params, $this );
1937
1938
		return $value;
1939
	}
1940
1941
	/**
1942
	 * Check if an item field has a specific value in it
1943
	 *
1944
	 * @param string $field Field name.
1945
	 * @param mixed  $value Value to check.
1946
	 * @param int    $id    (optional) ID of the pod item to check.
0 ignored issues
show
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...
1947
	 *
1948
	 * @return bool Whether the value was found
0 ignored issues
show
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...
1949
	 *
1950
	 * @since 2.3.3
1951
	 */
1952
	public function has( $field, $value, $id = null ) {
1953
1954
		$pod =& $this;
1955
1956
		if ( null === $id ) {
1957
			$id = $this->id();
1958
			// @codingStandardsIgnoreLine.
1959
		} elseif ( $id != $this->id() ) {
1960
			$pod = pods( $this->pod, $id );
1961
		}
1962
1963
		$this->do_hook( 'has', $field, $value, $id );
1964
1965
		if ( ! isset( $this->fields[ $field ] ) ) {
1966
			return false;
1967
		}
1968
1969
		// Tableless fields.
1970
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
1971
			if ( ! is_array( $value ) ) {
1972
				$value = explode( ',', $value );
1973
			}
1974
1975
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
1976
				$current_value = $pod->raw( $field );
1977
1978
				if ( ! empty( $current_value ) ) {
1979
					$current_value = (array) $current_value;
1980
				}
1981
1982
				foreach ( $current_value as $v ) {
1983
					// @codingStandardsIgnoreLine.
1984
					if ( in_array( $v, $value ) ) {
1985
						return true;
1986
					}
1987
				}
1988
			} else {
1989
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
1990
1991
				foreach ( $value as $k => $v ) {
1992
					if ( ! preg_match( '/[^\D]/', $v ) ) {
1993
						$value[ $k ] = (int) $v;
1994
					}
1995
1996
					// @todo Convert slugs into IDs.
1997
				}
1998
1999
				foreach ( $related_ids as $v ) {
2000
					// @codingStandardsIgnoreLine.
2001
					if ( in_array( $v, $value ) ) {
2002
						return true;
2003
					}
2004
				}
2005
			}//end if
2006
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
2007
			// Text fields.
2008
			$current_value = $pod->raw( $field );
2009
2010
			if ( 0 < strlen( $current_value ) ) {
2011
				return stripos( $current_value, $value );
2012
			}
2013
		} else {
2014
			// All other fields.
2015
			return $this->is( $field, $value, $id );
2016
		}//end if
2017
2018
		return false;
2019
	}
2020
2021
	/**
2022
	 * Check if an item field is a specific value
2023
	 *
2024
	 * @param string $field Field name.
2025
	 * @param mixed  $value Value to check.
2026
	 * @param int    $id    (optional) ID of the pod item to check.
0 ignored issues
show
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...
2027
	 *
2028
	 * @return bool Whether the value was found
2029
	 *
2030
	 * @since 2.3.3
2031
	 */
2032
	public function is( $field, $value, $id = null ) {
0 ignored issues
show
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...
2033
2034
		$pod =& $this;
2035
2036
		if ( null === $id ) {
2037
			$id = $this->id();
2038
			// @codingStandardsIgnoreLine.
2039
		} elseif ( $id != $this->id() ) {
2040
			$pod = pods( $this->pod, $id );
2041
		}
2042
2043
		$this->do_hook( 'is', $field, $value, $id );
2044
2045
		if ( ! isset( $this->fields[ $field ] ) ) {
2046
			return false;
2047
		}
2048
2049
		// Tableless fields.
2050
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
2051
			if ( ! is_array( $value ) ) {
2052
				$value = explode( ',', $value );
2053
			}
2054
2055
			$current_value = array();
2056
2057
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
2058
				$current_value = $pod->raw( $field );
2059
2060
				if ( ! empty( $current_value ) ) {
2061
					$current_value = (array) $current_value;
2062
				}
2063
2064
				foreach ( $current_value as $v ) {
2065
					// @codingStandardsIgnoreLine.
2066
					if ( in_array( $v, $value ) ) {
2067
						return true;
2068
					}
2069
				}
2070
			} else {
2071
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
2072
2073
				foreach ( $value as $k => $v ) {
2074
					if ( ! preg_match( '/[^\D]/', $v ) ) {
2075
						$value[ $k ] = (int) $v;
2076
					}
2077
2078
					// @todo Convert slugs into IDs.
2079
				}
2080
2081
				foreach ( $related_ids as $v ) {
2082
					// @codingStandardsIgnoreLine.
2083
					if ( in_array( $v, $value ) ) {
2084
						return true;
2085
					}
2086
				}
2087
			}//end if
2088
2089
			if ( ! empty( $current_value ) ) {
2090
				$current_value = array_filter( array_unique( $current_value ) );
2091
			} else {
2092
				$current_value = array();
2093
			}
2094
2095
			if ( ! empty( $value ) ) {
2096
				$value = array_filter( array_unique( $value ) );
2097
			} else {
2098
				$value = array();
2099
			}
2100
2101
			sort( $current_value );
2102
			sort( $value );
2103
2104
			if ( $value === $current_value ) {
2105
				return true;
2106
			}
2107
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
2108
			// Number fields.
2109
			$current_value = $pod->raw( $field );
2110
2111
			if ( (float) $current_value === (float) $value ) {
2112
				return true;
2113
			}
2114
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
2115
			// Date fields.
2116
			$current_value = $pod->raw( $field );
2117
2118
			if ( 0 < strlen( $current_value ) ) {
2119
				if ( strtotime( $current_value ) === strtotime( $value ) ) {
2120
					return true;
2121
				}
2122
			} elseif ( empty( $value ) ) {
2123
				return true;
2124
			}
2125
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
2126
			// Text fields.
2127
			$current_value = $pod->raw( $field );
2128
2129
			if ( (string) $current_value === (string) $value ) {
2130
				return true;
2131
			}
2132
		} else {
2133
			// All other fields.
2134
			$current_value = $pod->raw( $field );
2135
2136
			if ( $current_value === $value ) {
2137
				return true;
2138
			}
2139
		}//end if
2140
2141
		return false;
2142
	}
2143
2144
	/**
2145
	 * Return the item ID
2146
	 *
2147
	 * @return int
2148
	 * @since 2.0
2149
	 */
2150
	public function id() {
0 ignored issues
show
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...
2151
2152
		if ( isset( $this->data->row, $this->data->row['id'] ) ) {
2153
			// If we already have data loaded return that ID.
2154
			return $this->data->row['id'];
2155
		}
2156
2157
		return $this->field( $this->data->field_id );
2158
	}
2159
2160
	/**
2161
	 * Return the previous item ID, loops at the last id to return the first
2162
	 *
2163
	 * @param int|null          $id              ID to start from.
2164
	 * @param array|object|null $params_override Override the find() parameters.
2165
	 *
2166
	 * @return int
2167
	 * @since 2.0
2168
	 */
2169
	public function prev_id( $id = null, $params_override = null ) {
2170
2171
		if ( null === $id ) {
2172
			$id = $this->id();
2173
		}
2174
2175
		$id = (int) $id;
2176
2177
		$params = array(
2178
			'select'  => "`t`.`{$this->data->field_id}`",
2179
			'where'   => "`t`.`{$this->data->field_id}` < {$id}",
2180
			'orderby' => "`t`.`{$this->data->field_id}` DESC",
2181
			'limit'   => 1,
2182
		);
2183
2184
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2185
			if ( ! empty( $params_override ) ) {
2186
				$params = $params_override;
2187
			} elseif ( ! empty( $this->params ) ) {
2188
				$params = $this->params;
2189
			}
2190
2191
			if ( is_object( $params ) ) {
2192
				$params = get_object_vars( $params );
2193
			}
2194
2195
			if ( 0 < $id ) {
2196
				if ( isset( $params['where'] ) && ! empty( $params['where'] ) ) {
2197
					$params['where']   = (array) $params['where'];
2198
					$params['where'][] = "`t`.`{$this->data->field_id}` < {$id}";
2199
				} else {
2200
					$params['where'] = "`t`.`{$this->data->field_id}` < {$id}";
2201
				}
2202
			} elseif ( isset( $params['offset'] ) && 0 < $params['offset'] ) {
2203
				$params['offset'] --;
2204
			} elseif ( 0 < $this->row_number && ! isset( $params['offset'] ) && ! empty( $this->params ) ) {
2205
				$params['offset'] = $this->row_number - 1;
2206
			} else {
2207
				return 0;
2208
			}
2209
2210
			if ( isset( $params['orderby'] ) && ! empty( $params['orderby'] ) ) {
2211
				if ( is_array( $params['orderby'] ) ) {
2212
					foreach ( $params['orderby'] as $orderby => $dir ) {
2213
						$dir = strtoupper( $dir );
2214
2215
						if ( ! in_array( $dir, array( 'ASC', 'DESC' ), true ) ) {
2216
							continue;
2217
						}
2218
2219
						if ( 'ASC' === $dir ) {
2220
							$params['orderby'][ $orderby ] = 'DESC';
2221
						} else {
2222
							$params['orderby'][ $orderby ] = 'ASC';
2223
						}
2224
					}
2225
2226
					$params['orderby'][ $this->data->field_id ] = 'DESC';
2227
				} elseif ( "`t`.`{$this->data->field_id}` DESC" !== $params['orderby'] ) {
2228
					$params['orderby'] .= ", `t`.`{$this->data->field_id}` DESC";
2229
				}
2230
			}//end if
2231
2232
			$params['select'] = "`t`.`{$this->data->field_id}`";
2233
			$params['limit']  = 1;
2234
		}//end if
2235
2236
		$pod = pods( $this->pod, $params );
2237
2238
		$new_id = 0;
2239
2240
		if ( $pod->fetch() ) {
2241
			$new_id = $pod->id();
2242
		}
2243
2244
		$new_id = $this->do_hook( 'prev_id', $new_id, $id, $pod, $params_override );
2245
2246
		return $new_id;
2247
	}
2248
2249
	/**
2250
	 * Return the next item ID, loops at the first id to return the last
2251
	 *
2252
	 * @param int|null          $id              ID to start from.
2253
	 * @param array|object|null $params_override Override the find() parameters.
2254
	 *
2255
	 * @return int
2256
	 * @since 2.0
2257
	 */
2258
	public function next_id( $id = null, $params_override = null ) {
2259
2260
		if ( null === $id ) {
2261
			$id = $this->id();
2262
		}
2263
2264
		$id = (int) $id;
2265
2266
		$params = array(
2267
			'select'  => "`t`.`{$this->data->field_id}`",
2268
			'where'   => "{$id} < `t`.`{$this->data->field_id}`",
2269
			'orderby' => "`t`.`{$this->data->field_id}` ASC",
2270
			'limit'   => 1,
2271
		);
2272
2273
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2274
			if ( ! empty( $params_override ) ) {
2275
				$params = $params_override;
2276
			} elseif ( ! empty( $this->params ) ) {
2277
				$params = $this->params;
2278
			}
2279
2280
			if ( is_object( $params ) ) {
2281
				$params = get_object_vars( $params );
2282
			}
2283
2284
			if ( 0 < $id ) {
2285
				if ( isset( $params['where'] ) && ! empty( $params['where'] ) ) {
2286
					$params['where']   = (array) $params['where'];
2287
					$params['where'][] = "{$id} < `t`.`{$this->data->field_id}`";
2288
				} else {
2289
					$params['where'] = "{$id} < `t`.`{$this->data->field_id}`";
2290
				}
2291
			} elseif ( ! isset( $params['offset'] ) ) {
2292
				if ( ! empty( $this->params ) && - 1 < $this->row_number ) {
2293
					$params['offset'] = $this->row_number + 1;
2294
				} else {
2295
					$params['offset'] = 1;
2296
				}
2297
			} else {
2298
				$params['offset'] ++;
2299
			}
2300
2301
			$params['select'] = "`t`.`{$this->data->field_id}`";
2302
			$params['limit']  = 1;
2303
		}//end if
2304
2305
		$pod = pods( $this->pod, $params );
2306
2307
		$new_id = 0;
2308
2309
		if ( $pod->fetch() ) {
2310
			$new_id = $pod->id();
2311
		}
2312
2313
		$new_id = $this->do_hook( 'next_id', $new_id, $id, $pod, $params_override );
2314
2315
		return $new_id;
2316
	}
2317
2318
	/**
2319
	 * Return the first item ID
2320
	 *
2321
	 * @param array|object|null $params_override Override the find() parameters.
2322
	 *
2323
	 * @return int
2324
	 * @since 2.3
2325
	 */
2326
	public function first_id( $params_override = null ) {
2327
2328
		$params = array(
2329
			'select'  => "`t`.`{$this->data->field_id}`",
2330
			'orderby' => "`t`.`{$this->data->field_id}` ASC",
2331
			'limit'   => 1,
2332
		);
2333
2334
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2335
			if ( ! empty( $params_override ) ) {
2336
				$params = $params_override;
2337
			} elseif ( ! empty( $this->params ) ) {
2338
				$params = $this->params;
2339
			}
2340
2341
			if ( is_object( $params ) ) {
2342
				$params = get_object_vars( $params );
2343
			}
2344
2345
			$params['select'] = "`t`.`{$this->data->field_id}`";
2346
			$params['offset'] = 0;
2347
			$params['limit']  = 1;
2348
		}
2349
2350
		$pod = pods( $this->pod, $params );
2351
2352
		$new_id = 0;
2353
2354
		if ( $pod->fetch() ) {
2355
			$new_id = $pod->id();
2356
		}
2357
2358
		$new_id = $this->do_hook( 'first_id', $new_id, $pod, $params_override );
2359
2360
		return $new_id;
2361
	}
2362
2363
	/**
2364
	 * Return the last item ID
2365
	 *
2366
	 * @param array|object|null $params_override Override the find() parameters.
2367
	 *
2368
	 * @return int
2369
	 * @since 2.3
2370
	 */
2371
	public function last_id( $params_override = null ) {
2372
2373
		$params = array(
2374
			'select'  => "`t`.`{$this->data->field_id}`",
2375
			'orderby' => "`t`.`{$this->data->field_id}` DESC",
2376
			'limit'   => 1,
2377
		);
2378
2379
		if ( ! empty( $params_override ) || ! empty( $this->params ) ) {
2380
			if ( ! empty( $params_override ) ) {
2381
				$params = $params_override;
2382
			} elseif ( ! empty( $this->params ) ) {
2383
				$params = $this->params;
2384
			}
2385
2386
			if ( is_object( $params ) ) {
2387
				$params = get_object_vars( $params );
2388
			}
2389
2390
			if ( isset( $params['total_found'] ) ) {
2391
				$params['offset'] = $params['total_found'] - 1;
2392
			} else {
2393
				$params['offset'] = $this->total_found() - 1;
2394
			}
2395
2396
			if ( isset( $params['orderby'] ) && ! empty( $params['orderby'] ) ) {
2397
				if ( is_array( $params['orderby'] ) ) {
2398
					foreach ( $params['orderby'] as $orderby => $dir ) {
2399
						$dir = strtoupper( $dir );
2400
2401
						if ( ! in_array( $dir, array( 'ASC', 'DESC' ), true ) ) {
2402
							continue;
2403
						}
2404
2405
						if ( 'ASC' === $dir ) {
2406
							$params['orderby'][ $orderby ] = 'DESC';
2407
						} else {
2408
							$params['orderby'][ $orderby ] = 'ASC';
2409
						}
2410
					}
2411
2412
					$params['orderby'][ $this->data->field_id ] = 'DESC';
2413
				} elseif ( "`t`.`{$this->data->field_id}` DESC" !== $params['orderby'] ) {
2414
					$params['orderby'] .= ", `t`.`{$this->data->field_id}` DESC";
2415
				}
2416
			}//end if
2417
2418
			$params['select'] = "`t`.`{$this->data->field_id}`";
2419
			$params['limit']  = 1;
2420
		}//end if
2421
2422
		$pod = pods( $this->pod, $params );
2423
2424
		$new_id = 0;
2425
2426
		if ( $pod->fetch() ) {
2427
			$new_id = $pod->id();
2428
		}
2429
2430
		$new_id = $this->do_hook( 'last_id', $new_id, $pod, $params_override );
2431
2432
		return $new_id;
2433
	}
2434
2435
	/**
2436
	 * Return the item name
2437
	 *
2438
	 * @return string
2439
	 * @since 2.0
2440
	 */
2441
	public function index() {
2442
2443
		return $this->field( $this->data->field_index );
2444
	}
2445
2446
	/**
2447
	 * Find items of a pod, much like WP_Query, but with advanced table handling.
2448
	 *
2449
	 * @param array|object $params An associative array of parameters.
0 ignored issues
show
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...
2450
	 * @param int          $limit  (deprecated) Limit the number of items to find, -1 to return all items with no limit.
2451
	 * @param string       $where  (deprecated) SQL WHERE declaration to use.
0 ignored issues
show
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...
2452
	 * @param string       $sql    (deprecated) For advanced use, a custom SQL query to run.
0 ignored issues
show
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...
2453
	 *
2454
	 * @return \Pods The pod object
2455
	 * @since 2.0
2456
	 * @link  https://pods.io/docs/find/
2457
	 */
2458
	public function find( $params = null, $limit = 15, $where = null, $sql = null ) {
2459
2460
		$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...
2461
		$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...
2462
2463
		$this->params = $params;
2464
2465
		$select = '`t`.*';
2466
2467
		if ( 'table' === $this->pod_data['storage'] && ! in_array( $this->pod_data['type'], array(
2468
			'pod',
2469
			'table',
2470
		), true ) ) {
2471
			$select .= ', `d`.*';
2472
		}
2473
2474
		if ( empty( $this->data->table ) ) {
2475
			return $this;
2476
		}
2477
2478
		$defaults = array(
2479
			// Optimization parameters.
2480
			'table'               => $this->data->table,
2481
			'select'              => $select,
2482
			'join'                => null,
2483
			// Main query parameters.
2484
			'where'               => $where,
2485
			'groupby'             => null,
2486
			'having'              => null,
2487
			'orderby'             => null,
2488
			// Pagination parameters.
2489
			'limit'               => (int) $limit,
2490
			'offset'              => null,
2491
			'page'                => (int) $this->page,
2492
			'page_var'            => $this->page_var,
2493
			'pagination'          => (boolean) $this->pagination,
2494
			// Search parameters.
2495
			'search'              => (boolean) $this->search,
2496
			'search_var'          => $this->search_var,
2497
			'search_query'        => null,
2498
			'search_mode'         => $this->search_mode,
2499
			'search_across'       => false,
2500
			'search_across_picks' => false,
2501
			'search_across_files' => false,
2502
			// Advanced parameters.
2503
			'filters'             => $this->filters,
2504
			'sql'                 => $sql,
2505
			// Caching parameters.
2506
			'expires'             => null,
2507
			'cache_mode'          => 'cache',
2508
		);
2509
2510
		if ( is_array( $params ) ) {
2511
			$params = (object) array_merge( $defaults, $params );
2512
		} elseif ( is_object( $params ) ) {
2513
			$params = (object) array_merge( $defaults, get_object_vars( $params ) );
2514
		} else {
2515
			$defaults['orderby'] = $params;
2516
2517
			$params = (object) $defaults;
2518
		}
2519
2520
		/**
2521
		 * Filter the Pods::find() parameters.
2522
		 *
2523
		 * @param object $params Parameters to make lookup with.
2524
		 */
2525
		$params = apply_filters( 'pods_pods_find', $params );
2526
2527
		$params->limit = (int) $params->limit;
2528
2529
		if ( 0 === $params->limit ) {
2530
			$params->limit = - 1;
2531
		}
2532
2533
		$this->limit      = (int) $params->limit;
2534
		$this->offset     = (int) $params->offset;
2535
		$this->page       = (int) $params->page;
2536
		$this->page_var   = $params->page_var;
2537
		$this->pagination = (boolean) $params->pagination;
2538
		$this->search     = (boolean) $params->search;
2539
		$this->search_var = $params->search_var;
2540
		$params->join     = (array) $params->join;
2541
2542
		if ( empty( $params->search_query ) ) {
2543
			$params->search_query = pods_v_sanitized( $this->search_var, 'get', '' );
2544
		}
2545
2546
		// 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...
2547
		if ( ! empty( $params->orderby ) && is_array( $params->orderby ) ) {
2548
			foreach ( $params->orderby as $k => $orderby ) {
2549
				if ( ! is_numeric( $k ) ) {
2550
					$key = '';
2551
2552
					$order = 'ASC';
2553
2554
					if ( 'DESC' === strtoupper( $orderby ) ) {
2555
						$order = 'DESC';
2556
					}
2557
2558
					if ( isset( $this->fields[ $k ] ) && in_array( $this->fields[ $k ]['type'], $tableless_field_types, true ) ) {
2559
						if ( in_array( $this->fields[ $k ]['pick_object'], $simple_tableless_objects, true ) ) {
2560
							if ( 'table' === $this->pod_data['storage'] ) {
2561
								if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2562
									$key = "`d`.`{$k}`";
2563
								} else {
2564
									$key = "`t`.`{$k}`";
2565
								}
2566
							} else {
2567
								$key = "`{$k}`.`meta_value`";
2568
							}
2569
						} else {
2570
							$pick_val = $this->fields[ $k ]['pick_val'];
2571
2572
							if ( '__current__' === $pick_val ) {
2573
								$pick_val = $this->pod;
2574
							}
2575
2576
							$table = $this->api->get_table_info( $this->fields[ $k ]['pick_object'], $pick_val );
2577
2578
							if ( ! empty( $table ) ) {
2579
								$key = "`{$k}`.`" . $table['field_index'] . '`';
2580
							}
2581
						}//end if
2582
					}//end if
2583
2584
					if ( empty( $key ) ) {
2585
						if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2586
							if ( isset( $this->pod_data['object_fields'][ $k ] ) ) {
2587
								$key = "`t`.`{$k}`";
2588
							} elseif ( isset( $this->fields[ $k ] ) ) {
2589
								if ( 'table' === $this->pod_data['storage'] ) {
2590
									$key = "`d`.`{$k}`";
2591
								} else {
2592
									$key = "`{$k}`.`meta_value`";
2593
								}
2594
							} else {
2595
								$object_fields = (array) $this->pod_data['object_fields'];
2596
2597
								foreach ( $object_fields as $object_field => $object_field_opt ) {
2598
									if ( $object_field === $k || in_array( $k, $object_field_opt['alias'], true ) ) {
2599
										$key = "`t`.`{$object_field}`";
2600
									}
2601
								}
2602
							}
2603
						} elseif ( isset( $this->fields[ $k ] ) ) {
2604
							if ( 'table' === $this->pod_data['storage'] ) {
2605
								$key = "`t`.`{$k}`";
2606
							} else {
2607
								$key = "`{$k}`.`meta_value`";
2608
							}
2609
						}//end if
2610
2611
						if ( empty( $key ) ) {
2612
							$key = $k;
2613
2614
							if ( false === strpos( $key, ' ' ) && false === strpos( $key, '`' ) ) {
2615
								$key = '`' . str_replace( '.', '`.`', $key ) . '`';
2616
							}
2617
						}
2618
					}//end if
2619
2620
					$orderby = $key;
2621
2622
					if ( false === strpos( $orderby, ' ' ) ) {
2623
						$orderby .= ' ' . $order;
2624
					}
2625
2626
					$params->orderby[ $k ] = $orderby;
2627
				}//end if
2628
			}//end foreach
2629
		}//end if
2630
2631
		// Add prefix to $params->orderby if needed.
2632
		if ( ! empty( $params->orderby ) ) {
2633
			if ( ! is_array( $params->orderby ) ) {
2634
				$params->orderby = array( $params->orderby );
2635
			}
2636
2637
			foreach ( $params->orderby as $ok => $prefix_orderby ) {
2638
				if ( false === strpos( $prefix_orderby, ',' ) && false === strpos( $prefix_orderby, '(' ) && false === stripos( $prefix_orderby, ' AS ' ) && false === strpos( $prefix_orderby, '`' ) && false === strpos( $prefix_orderby, '.' ) ) {
2639
					if ( false !== stripos( $prefix_orderby, ' DESC' ) ) {
2640
						$k   = trim( str_ireplace( array( '`', ' DESC' ), '', $prefix_orderby ) );
2641
						$dir = 'DESC';
2642
					} else {
2643
						$k   = trim( str_ireplace( array( '`', ' ASC' ), '', $prefix_orderby ) );
2644
						$dir = 'ASC';
2645
					}
2646
2647
					$key = $k;
2648
2649
					if ( ! in_array( $this->pod_data['type'], array( 'pod', 'table' ), true ) ) {
2650
						if ( isset( $this->pod_data['object_fields'][ $k ] ) ) {
2651
							$key = "`t`.`{$k}`";
2652
						} elseif ( isset( $this->fields[ $k ] ) ) {
2653
							if ( 'table' === $this->pod_data['storage'] ) {
2654
								$key = "`d`.`{$k}`";
2655
							} else {
2656
								$key = "`{$k}`.`meta_value`";
2657
							}
2658
						} else {
2659
							$object_fields = (array) $this->pod_data['object_fields'];
2660
2661
							foreach ( $object_fields as $object_field => $object_field_opt ) {
2662
								if ( $object_field === $k || in_array( $k, $object_field_opt['alias'], true ) ) {
2663
									$key = "`t`.`{$object_field}`";
2664
								}
2665
							}
2666
						}
2667
					} elseif ( isset( $this->fields[ $k ] ) ) {
2668
						if ( 'table' === $this->pod_data['storage'] ) {
2669
							$key = "`t`.`{$k}`";
2670
						} else {
2671
							$key = "`{$k}`.`meta_value`";
2672
						}
2673
					}//end if
2674
2675
					$prefix_orderby = "{$key} {$dir}";
2676
2677
					$params->orderby[ $ok ] = $prefix_orderby;
2678
				}//end if
2679
			}//end foreach
2680
		}//end if
2681
2682
		$this->data->select( $params );
2683
2684
		return $this;
2685
	}
2686
2687
	/**
2688
	 * Fetch an item from a Pod. If $id is null, it will return the next item in the list after running find().
2689
	 * You can rewind the list back to the start by using reset().
2690
	 *
2691
	 * Providing an $id will fetch a specific item from a Pod, much like a call to pods(), and can handle either an id
2692
	 * or slug.
2693
	 *
2694
	 * @see   PodsData::fetch
2695
	 *
2696
	 * @param int  $id           ID or slug of the item to fetch.
0 ignored issues
show
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...
2697
	 * @param bool $explicit_set Whether to set explicitly (use false when in loop).
2698
	 *
2699
	 * @return array An array of fields from the row
2700
	 *
2701
	 * @since 2.0
2702
	 * @link  https://pods.io/docs/fetch/
2703
	 */
2704
	public function fetch( $id = null, $explicit_set = true ) {
2705
2706
		/**
2707
		 * Runs directly before an item is fetched by fetch().
2708
		 *
2709
		 * @since unknown
2710
		 *
2711
		 * @param int|string|null $id   Item ID being fetched or null.
2712
		 * @param object|Pods     $this Current Pods object.
2713
		 */
2714
		do_action( 'pods_pods_fetch', $id, $this );
2715
2716
		if ( ! empty( $id ) ) {
2717
			$this->params = array();
2718
		}
2719
2720
		$this->data->fetch( $id, $explicit_set );
2721
2722
		return $this->row;
2723
	}
2724
2725
	/**
2726
	 * (Re)set the MySQL result pointer
2727
	 *
2728
	 * @see   PodsData::reset
2729
	 *
2730
	 * @param int $row ID of the row to reset to.
0 ignored issues
show
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...
2731
	 *
2732
	 * @return \Pods The pod object
2733
	 *
2734
	 * @since 2.0
2735
	 * @link  https://pods.io/docs/reset/
2736
	 */
2737
	public function reset( $row = null ) {
2738
2739
		/**
2740
		 * Runs directly before the Pods object is reset by reset()
2741
		 *
2742
		 * @since unknown
2743
		 *
2744
		 * @param int|string|null The ID of the row being reset to or null if being reset to the beginning.
2745
		 * @param object|Pods $this Current Pods object.
2746
		 */
2747
		do_action( 'pods_pods_reset', $row, $this );
2748
2749
		$this->data->reset( $row );
2750
2751
		return $this;
2752
	}
2753
2754
	/**
2755
	 * Fetch the total row count returned by the last call to find(), based on the 'limit' parameter set.
2756
	 *
2757
	 * This is different than the total number of rows found in the database, which you can get with total_found().
2758
	 *
2759
	 * @see   PodsData::total
2760
	 *
2761
	 * @return int Number of rows returned by find(), based on the 'limit' parameter set
2762
	 * @since 2.0
2763
	 * @link  https://pods.io/docs/total/
2764
	 */
2765
	public function total() {
2766
2767
		do_action( 'pods_pods_total', $this );
2768
2769
		$this->data->total();
0 ignored issues
show
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...
2770
2771
		$this->total =& $this->data->total;
2772
2773
		return $this->total;
2774
	}
2775
2776
	/**
2777
	 * Fetch the total amount of rows found by the last call to find(), regardless of the 'limit' parameter set.
2778
	 *
2779
	 * This is different than the total number of rows limited by the current call, which you can get with total().
2780
	 *
2781
	 * @see   PodsData::total_found
2782
	 *
2783
	 * @return int Number of rows returned by find(), regardless of the 'limit' parameter
0 ignored issues
show
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...
2784
	 * @since 2.0
2785
	 * @link  https://pods.io/docs/total-found/
2786
	 */
2787
	public function total_found() {
2788
2789
		/**
2790
		 * Runs directly before the value of total_found() is determined and returned.
2791
		 *
2792
		 * @since unknown
2793
		 *
2794
		 * @param object|Pods $this Current Pods object.
2795
		 */
2796
		do_action( 'pods_pods_total_found', $this );
2797
2798
		$this->data->total_found();
2799
2800
		$this->total_found =& $this->data->total_found;
2801
2802
		return $this->total_found;
2803
	}
2804
2805
	/**
2806
	 * Fetch the total number of pages, based on total rows found and the last find() limit
2807
	 *
2808
	 * @param null|int $limit  Rows per page.
2809
	 * @param null|int $offset Offset of rows.
2810
	 * @param null|int $total  Total rows.
2811
	 *
2812
	 * @return int Number of pages
0 ignored issues
show
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...
2813
	 * @since 2.3.10
2814
	 */
2815
	public function total_pages( $limit = null, $offset = null, $total = null ) {
2816
2817
		$this->do_hook( 'total_pages' );
2818
2819
		if ( null === $limit ) {
2820
			$limit = $this->limit;
2821
		}
2822
2823
		if ( null === $offset ) {
2824
			$offset = $this->offset;
2825
		}
2826
2827
		if ( null === $total ) {
2828
			$total = $this->total_found();
2829
		}
2830
2831
		return ceil( ( $total - $offset ) / $limit );
2832
2833
	}
2834
2835
	/**
2836
	 * Fetch the zebra switch
2837
	 *
2838
	 * @see   PodsData::zebra
2839
	 *
2840
	 * @return bool Zebra state
2841
	 * @since 1.12
2842
	 */
2843
	public function zebra() {
2844
2845
		$this->do_hook( 'zebra' );
2846
2847
		return $this->data->zebra();
2848
	}
2849
2850
	/**
2851
	 * Fetch the nth state
2852
	 *
2853
	 * @see   PodsData::nth
2854
	 *
2855
	 * @param int|string $nth The $nth to match on the PodsData::row_number.
0 ignored issues
show
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...
2856
	 *
2857
	 * @return bool Whether $nth matches
2858
	 * @since 2.3
2859
	 */
2860
	public function nth( $nth = null ) {
2861
2862
		$this->do_hook( 'nth', $nth );
2863
2864
		return $this->data->nth( $nth );
2865
	}
2866
2867
	/**
2868
	 * Fetch the current position in the loop (starting at 1)
2869
	 *
2870
	 * @see   PodsData::position
2871
	 *
2872
	 * @return int Current row number (+1)
2873
	 * @since 2.3
2874
	 */
2875
	public function position() {
2876
2877
		$this->do_hook( 'position' );
2878
2879
		return $this->data->position();
2880
	}
2881
2882
	/**
2883
	 * Add an item to a Pod by giving an array of field data or set a specific field to
2884
	 * a specific value if you're just wanting to add a new item but only set one field.
2885
	 *
2886
	 * You may be looking for save() in most cases where you're setting a specific field.
2887
	 *
2888
	 * @see   PodsAPI::save_pod_item
2889
	 *
2890
	 * @param array|string $data  Either an associative array of field information or a field name.
0 ignored issues
show
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...
2891
	 * @param mixed        $value (optional) Value of the field, if $data is a field name.
2892
	 *
2893
	 * @return int The item ID
0 ignored issues
show
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...
2894
	 *
2895
	 * @since 2.0
2896
	 * @link  https://pods.io/docs/add/
2897
	 */
2898
	public function add( $data = null, $value = null ) {
2899
2900
		if ( null !== $value ) {
2901
			$data = array( $data => $value );
2902
		}
2903
2904
		$data = (array) $this->do_hook( 'add', $data );
2905
2906
		if ( empty( $data ) ) {
2907
			return 0;
2908
		}
2909
2910
		$params = array(
2911
			'pod'                 => $this->pod,
2912
			'data'                => $data,
2913
			'allow_custom_fields' => true,
2914
		);
2915
2916
		return $this->api->save_pod_item( $params );
2917
	}
2918
2919
	/**
2920
	 * Add an item to the values of a relationship field, add a value to a number field (field+1), add time to a date
2921
	 * field, or add text to a text field
2922
	 *
2923
	 * @see   PodsAPI::save_pod_item
2924
	 *
2925
	 * @param string $field Field name.
2926
	 * @param mixed  $value IDs to add, int|float to add to number field, string for dates (+1 day), or string for text.
2927
	 * @param int    $id    (optional) ID of the pod item to update.
0 ignored issues
show
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...
2928
	 *
2929
	 * @return int The item ID
0 ignored issues
show
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...
2930
	 *
2931
	 * @since 2.3
2932
	 */
2933
	public function add_to( $field, $value, $id = null ) {
2934
2935
		$pod =& $this;
2936
2937
		$fetch = false;
2938
2939
		if ( null === $id ) {
2940
			$fetch = true;
2941
2942
			$id = $pod->id();
2943
			// @codingStandardsIgnoreLine
2944
		} elseif ( $id != $this->id() ) {
2945
			$pod = pods( $this->pod, $id );
2946
		}
2947
2948
		$this->do_hook( 'add_to', $field, $value, $id );
2949
2950
		if ( ! isset( $this->fields[ $field ] ) ) {
2951
			return $id;
2952
		}
2953
2954
		// Tableless fields.
2955
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
2956
			if ( ! is_array( $value ) ) {
2957
				$value = explode( ',', $value );
2958
			}
2959
2960
			if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
2961
				$current_value = $pod->raw( $field );
2962
2963
				if ( ! empty( $current_value ) || ( ! is_array( $current_value ) && 0 < strlen( $current_value ) ) ) {
2964
					$current_value = (array) $current_value;
2965
				} else {
2966
					$current_value = array();
2967
				}
2968
2969
				$value = array_merge( $current_value, $value );
2970
			} else {
2971
				$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
2972
2973
				foreach ( $value as $k => $v ) {
2974
					if ( ! preg_match( '/[^\D]/', $v ) ) {
2975
						$value[ $k ] = (int) $v;
2976
					}
2977
				}
2978
2979
				$value = array_merge( $related_ids, $value );
2980
			}//end if
2981
2982
			if ( ! empty( $value ) ) {
2983
				$value = array_filter( array_unique( $value ) );
2984
			} else {
2985
				$value = array();
2986
			}
2987
2988
			if ( empty( $value ) ) {
2989
				return $id;
2990
			}
2991
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
2992
			// Number fields.
2993
			$current_value = (float) $pod->raw( $field );
2994
2995
			$value = ( $current_value + (float) $value );
2996
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
2997
			// Date fields.
2998
			$current_value = $pod->raw( $field );
2999
3000
			if ( 0 < strlen( $current_value ) ) {
3001
				$value = strtotime( $value, strtotime( $current_value ) );
3002
			} else {
3003
				$value = strtotime( $value );
3004
			}
3005
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::text_field_types(), true ) ) {
3006
			// Text fields.
3007
			$current_value = $pod->raw( $field );
3008
3009
			if ( 0 < strlen( $current_value ) ) {
3010
				$value = $current_value . $value;
3011
			}
3012
		}//end if
3013
3014
		// @todo handle object fields and taxonomies.
3015
		$params = array(
3016
			'pod'  => $this->pod,
3017
			'id'   => $id,
3018
			'data' => array(
3019
				$field => $value,
3020
			),
3021
		);
3022
3023
		$id = $this->api->save_pod_item( $params );
3024
3025
		if ( 0 < $id && $fetch ) {
3026
			// Clear local var cache of field values.
3027
			$pod->data->row = array();
3028
3029
			$pod->fetch( $id, false );
3030
		}
3031
3032
		return $id;
3033
	}
3034
3035
	/**
3036
	 * Remove an item from the values of a relationship field, remove a value from a number field (field-1), remove
3037
	 * time to a date field
3038
	 *
3039
	 * @see   PodsAPI::save_pod_item
3040
	 *
3041
	 * @param string $field Field name.
3042
	 * @param mixed  $value IDs to add, int|float to add to number field, string for dates (-1 day), or string for text.
3043
	 * @param int    $id    (optional) ID of the pod item to update.
0 ignored issues
show
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...
3044
	 *
3045
	 * @return int The item ID
0 ignored issues
show
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...
3046
	 *
3047
	 * @since 2.3.3
3048
	 */
3049
	public function remove_from( $field, $value = null, $id = null ) {
3050
3051
		$pod =& $this;
3052
3053
		$fetch = false;
3054
3055
		if ( null === $id ) {
3056
			$fetch = true;
3057
3058
			$id = $this->id();
3059
			// @codingStandardsIgnoreLine
3060
		} elseif ( $id != $this->id() ) {
3061
			$pod = pods( $this->pod, $id );
3062
		}
3063
3064
		$this->do_hook( 'remove_from', $field, $value, $id );
3065
3066
		if ( ! isset( $this->fields[ $field ] ) ) {
3067
			return $id;
3068
		}
3069
3070
		// Tableless fields.
3071
		if ( in_array( $this->fields[ $field ]['type'], PodsForm::tableless_field_types(), true ) ) {
3072
			if ( empty( $value ) ) {
3073
				$value = array();
3074
			}
3075
3076
			if ( ! empty( $value ) ) {
3077
				if ( ! is_array( $value ) ) {
3078
					$value = explode( ',', $value );
3079
				}
3080
3081
				if ( 'pick' === $this->fields[ $field ]['type'] && in_array( $this->fields[ $field ]['pick_object'], PodsForm::simple_tableless_objects(), true ) ) {
3082
					$current_value = $pod->raw( $field );
3083
3084
					if ( ! empty( $current_value ) ) {
3085
						$current_value = (array) $current_value;
3086
					}
3087
3088
					foreach ( $current_value as $k => $v ) {
3089
						// @codingStandardsIgnoreLine.
3090
						if ( in_array( $v, $value ) ) {
3091
							unset( $current_value[ $k ] );
3092
						}
3093
					}
3094
3095
					$value = $current_value;
3096
				} else {
3097
					$related_ids = $this->api->lookup_related_items( $this->fields[ $field ]['id'], $this->pod_data['id'], $id, $this->fields[ $field ], $this->pod_data );
3098
3099
					foreach ( $value as $k => $v ) {
3100
						if ( ! preg_match( '/[^\D]/', $v ) ) {
3101
							$value[ $k ] = (int) $v;
3102
						}
3103
3104
						// @todo Convert slugs into IDs.
3105
					}
3106
3107
					foreach ( $related_ids as $k => $v ) {
3108
						// @codingStandardsIgnoreLine.
3109
						if ( in_array( $v, $value ) ) {
3110
							unset( $related_ids[ $k ] );
3111
						}
3112
					}
3113
3114
					$value = $related_ids;
3115
				}//end if
3116
3117
				if ( ! empty( $value ) ) {
3118
					$value = array_filter( array_unique( $value ) );
3119
				} else {
3120
					$value = array();
3121
				}
3122
			}//end if
3123
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::number_field_types(), true ) ) {
3124
			// Number fields.
3125
			// Date fields don't support empty for removing.
3126
			if ( empty( $value ) ) {
3127
				return $id;
3128
			}
3129
3130
			$current_value = (float) $pod->raw( $field );
3131
3132
			$value = ( $current_value - (float) $value );
3133
		} elseif ( in_array( $this->fields[ $field ]['type'], PodsForm::date_field_types(), true ) ) {
3134
			// Date fields.
3135
			// Date fields don't support empty for removing.
3136
			if ( empty( $value ) ) {
3137
				return $id;
3138
			}
3139
3140
			$current_value = $pod->raw( $field );
3141
3142
			if ( 0 < strlen( $current_value ) ) {
3143
				$value = strtotime( $value, strtotime( $current_value ) );
3144
			} else {
3145
				$value = strtotime( $value );
3146
			}
3147
3148
			$value = date_i18n( 'Y-m-d h:i:s', $value );
3149
		}//end if
3150
3151
		// @todo handle object fields and taxonomies.
3152
		$params = array(
3153
			'pod'  => $this->pod,
3154
			'id'   => $id,
3155
			'data' => array(
3156
				$field => $value,
3157
			),
3158
		);
3159
3160
		$id = $this->api->save_pod_item( $params );
3161
3162
		if ( 0 < $id && $fetch ) {
3163
			// Clear local var cache of field values.
3164
			$pod->data->row = array();
3165
3166
			$pod->fetch( $id, false );
3167
		}
3168
3169
		return $id;
3170
3171
	}
3172
3173
	/**
3174
	 * Save an item by giving an array of field data or set a specific field to a specific value.
3175
	 *
3176
	 * Though this function has the capacity to add new items, best practice should direct you
3177
	 * to use add() for that instead.
3178
	 *
3179
	 * @see   PodsAPI::save_pod_item
3180
	 *
3181
	 * @param array|string $data   Either an associative array of field information or a field name.
0 ignored issues
show
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...
3182
	 * @param mixed        $value  (optional) Value of the field, if $data is a field name.
3183
	 * @param int          $id     (optional) ID of the pod item to update.
0 ignored issues
show
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...
3184
	 * @param array        $params (optional) Additional params to send to save_pod_item.
0 ignored issues
show
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...
3185
	 *
3186
	 * @return int The item ID
0 ignored issues
show
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...
3187
	 *
3188
	 * @since 2.0
3189
	 * @link  https://pods.io/docs/save/
3190
	 */
3191
	public function save( $data = null, $value = null, $id = null, $params = null ) {
3192
3193
		if ( null !== $value ) {
3194
			$data = array( $data => $value );
3195
		}
3196
3197
		$fetch = false;
3198
3199
		// @codingStandardsIgnoreLine
3200
		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...
3201
			$fetch = true;
3202
3203
			if ( null === $id ) {
3204
				$id = $this->id();
3205
			}
3206
		}
3207
3208
		$data = (array) $this->do_hook( 'save', $data, $id );
3209
3210
		if ( empty( $data ) && empty( $params['is_new_item'] ) ) {
3211
			return $id;
3212
		}
3213
3214
		$default = array();
3215
3216
		if ( ! empty( $params ) && is_array( $params ) ) {
3217
			$default = $params;
3218
		}
3219
3220
		$params = array(
3221
			'pod'                 => $this->pod,
3222
			'id'                  => $id,
3223
			'data'                => $data,
3224
			'allow_custom_fields' => true,
3225
			'clear_slug_cache'    => false,
3226
		);
3227
3228
		if ( ! empty( $default ) ) {
3229
			$params = array_merge( $params, $default );
3230
		}
3231
3232
		$id = $this->api->save_pod_item( $params );
3233
3234
		if ( 0 < $id && $fetch ) {
3235
			// Clear local var cache of field values.
3236
			$this->data->row = array();
3237
3238
			$this->fetch( $id, false );
3239
		}
3240
3241
		if ( ! empty( $this->pod_data['field_slug'] ) ) {
3242
			if ( 0 < $id && $fetch ) {
3243
				$slug = $this->field( $this->pod_data['field_slug'] );
3244
			} else {
3245
				$slug = pods( $this->pod, $id )->field( $this->pod_data['field_slug'] );
3246
			}
3247
3248
			if ( 0 < strlen( $slug ) ) {
3249
				pods_cache_clear( $slug, 'pods_items_' . $this->pod );
3250
			}
3251
		}
3252
3253
		return $id;
3254
	}
3255
3256
	/**
3257
	 * Delete an item
3258
	 *
3259
	 * @see   PodsAPI::delete_pod_item
3260
	 *
3261
	 * @param int $id ID of the Pod item to delete.
0 ignored issues
show
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...
3262
	 *
3263
	 * @return bool Whether the item was successfully deleted
3264
	 *
3265
	 * @since 2.0
3266
	 * @link  https://pods.io/docs/delete/
3267
	 */
3268
	public function delete( $id = null ) {
3269
3270
		if ( null === $id ) {
3271
			$id = $this->id();
3272
		}
3273
3274
		$id = (int) $this->do_hook( 'delete', $id );
3275
3276
		if ( empty( $id ) ) {
3277
			return false;
3278
		}
3279
3280
		$params = array(
3281
			'pod' => $this->pod,
3282
			'id'  => $id,
3283
		);
3284
3285
		return $this->api->delete_pod_item( $params );
3286
	}
3287
3288
	/**
3289
	 * Reset Pod
3290
	 *
3291
	 * @see   PodsAPI::reset_pod
3292
	 *
3293
	 * @return bool Whether the Pod was successfully reset
3294
	 *
3295
	 * @since 2.1.1
3296
	 */
3297
	public function reset_pod() {
3298
3299
		$params = array( 'id' => $this->pod_id );
3300
3301
		$this->data->id   = null;
3302
		$this->data->row  = array();
3303
		$this->data->data = array();
3304
3305
		$this->data->total       = 0;
3306
		$this->data->total_found = 0;
3307
3308
		return $this->api->reset_pod( $params );
3309
	}
3310
3311
	/**
3312
	 * Duplicate an item
3313
	 *
3314
	 * @see   PodsAPI::duplicate_pod_item
3315
	 *
3316
	 * @param int $id ID of the pod item to duplicate.
0 ignored issues
show
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...
3317
	 *
3318
	 * @return int|bool ID of the new pod item
0 ignored issues
show
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...
3319
	 *
3320
	 * @since 2.0
3321
	 * @link  https://pods.io/docs/duplicate/
3322
	 */
3323
	public function duplicate( $id = null ) {
3324
3325
		if ( null === $id ) {
3326
			$id = $this->id();
3327
		}
3328
3329
		$id = (int) $this->do_hook( 'duplicate', $id );
3330
3331
		if ( empty( $id ) ) {
3332
			return false;
3333
		}
3334
3335
		$params = array(
3336
			'pod' => $this->pod,
3337
			'id'  => $id,
3338
		);
3339
3340
		return $this->api->duplicate_pod_item( $params );
3341
	}
3342
3343
	/**
3344
	 * Import data / Save multiple rows of data at once
3345
	 *
3346
	 * @see   PodsAPI::import
3347
	 *
3348
	 * @param mixed  $import_data  PHP associative array or CSV input.
3349
	 * @param bool   $numeric_mode Use IDs instead of the name field when matching.
3350
	 * @param string $format       Format of import data, options are php or csv.
0 ignored issues
show
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...
3351
	 *
3352
	 * @return array IDs of imported items
3353
	 *
3354
	 * @since 2.3
3355
	 */
3356
	public function import( $import_data, $numeric_mode = false, $format = null ) {
3357
3358
		return $this->api->import( $import_data, $numeric_mode, $format );
3359
	}
3360
3361
	/**
3362
	 * Export an item's data
3363
	 *
3364
	 * @see   PodsAPI::export_pod_item
3365
	 *
3366
	 * @param array       $fields (optional) Fields to export.
0 ignored issues
show
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...
3367
	 * @param int         $id     (optional) ID of the pod item to export.
0 ignored issues
show
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...
3368
	 * @param null|string $format (optional) The format of the export (php | json).
3369
	 *
3370
	 * @return array|bool Data array of the exported pod item
3371
	 *
3372
	 * @since 2.0
3373
	 * @link  https://pods.io/docs/export/
3374
	 */
3375
	public function export( $fields = null, $id = null, $format = null ) {
3376
3377
		$params = array(
3378
			'pod'     => $this->pod,
3379
			'id'      => $id,
3380
			'fields'  => null,
3381
			'depth'   => 2,
3382
			'flatten' => false,
3383
			'context' => null,
3384
			'format'  => $format,
3385
		);
3386
3387
		if ( is_array( $fields ) && ( isset( $fields['fields'] ) || isset( $fields['depth'] ) ) ) {
3388
			$params = array_merge( $params, $fields );
3389
		} else {
3390
			$params['fields'] = $fields;
3391
		}
3392
3393
		if ( isset( $params['fields'] ) && is_array( $params['fields'] ) && ! in_array( $this->pod_data['field_id'], $params['fields'], true ) ) {
3394
			$params['fields'] = array_merge( array( $this->pod_data['field_id'] ), $params['fields'] );
3395
		}
3396
3397
		if ( null === $params['id'] ) {
3398
			$params['id'] = $this->id();
3399
		}
3400
3401
		$params = (array) $this->do_hook( 'export', $params );
3402
3403
		if ( empty( $params['id'] ) ) {
3404
			return false;
3405
		}
3406
3407
		$data = $this->api->export_pod_item( $params );
3408
3409
		if ( ! empty( $params['format'] ) && 'json' === $params['format'] ) {
3410
			$data = wp_json_encode( (array) $data );
3411
		}
3412
3413
		return $data;
3414
	}
3415
3416
	/**
3417
	 * Export data from all items
3418
	 *
3419
	 * @see   PodsAPI::export
3420
	 *
3421
	 * @param array $params An associative array of parameters.
0 ignored issues
show
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...
3422
	 *
3423
	 * @return array Data arrays of all exported pod items
3424
	 *
3425
	 * @since 2.3
3426
	 */
3427
	public function export_data( $params = null ) {
3428
3429
		$defaults = array(
3430
			'fields' => null,
3431
			'depth'  => 2,
3432
			'params' => null,
3433
		);
3434
3435
		if ( empty( $params ) ) {
3436
			$params = $defaults;
3437
		} else {
3438
			$params = array_merge( $defaults, (array) $params );
3439
		}
3440
3441
		return $this->api->export( $this, $params );
3442
	}
3443
3444
	/**
3445
	 * Display the pagination controls, types supported by default
3446
	 * are simple, paginate and advanced. The base and format parameters
3447
	 * are used only for the paginate view.
3448
	 *
3449
	 * @param array|object $params Associative array of parameters.
0 ignored issues
show
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...
3450
	 *
3451
	 * @return string Pagination HTML
3452
	 * @since 2.0
3453
	 * @link  https://pods.io/docs/pagination/
3454
	 */
3455
	public function pagination( $params = null ) {
3456
3457
		if ( empty( $params ) ) {
3458
			$params = array();
3459
		} elseif ( ! is_array( $params ) ) {
3460
			$params = array( 'label' => $params );
3461
		}
3462
3463
		$this->page_var = pods_v( 'page_var', $params, $this->page_var );
3464
3465
		$url = pods_query_arg( null, null, $this->page_var );
3466
3467
		$append = '?';
3468
3469
		if ( false !== strpos( $url, '?' ) ) {
3470
			$append = '&';
3471
		}
3472
3473
		$defaults = array(
3474
			'type'        => 'advanced',
3475
			'label'       => __( 'Go to page:', 'pods' ),
3476
			'show_label'  => true,
3477
			'first_text'  => __( '&laquo; First', 'pods' ),
3478
			'prev_text'   => __( '&lsaquo; Previous', 'pods' ),
3479
			'next_text'   => __( 'Next &rsaquo;', 'pods' ),
3480
			'last_text'   => __( 'Last &raquo;', 'pods' ),
3481
			'prev_next'   => true,
3482
			'first_last'  => true,
3483
			'limit'       => (int) $this->limit,
3484
			'offset'      => (int) $this->offset,
3485
			'page'        => max( 1, (int) $this->page ),
3486
			'mid_size'    => 2,
3487
			'end_size'    => 1,
3488
			'total_found' => $this->total_found(),
3489
			'page_var'    => $this->page_var,
3490
			'base'        => "{$url}{$append}%_%",
3491
			'format'      => "{$this->page_var}=%#%",
3492
			'class'       => '',
3493
			'link_class'  => '',
3494
		);
3495
3496
		if ( is_object( $params ) ) {
3497
			$params = get_object_vars( $params );
3498
		}
3499
3500
		$params = (object) array_merge( $defaults, (array) $params );
3501
3502
		$params->total = $this->total_pages( $params->limit, $params->offset, $params->total_found );
3503
3504
		if ( $params->limit < 1 || $params->total_found < 1 || 1 === $params->total || $params->total_found <= $params->offset ) {
3505
			return $this->do_hook( 'pagination', $this->do_hook( 'pagination_' . $params->type, '', $params ), $params );
3506
		}
3507
3508
		$pagination = $params->type;
3509
3510
		if ( ! in_array( $params->type, array( 'simple', 'advanced', 'paginate', 'list' ), true ) ) {
3511
			$pagination = 'advanced';
3512
		}
3513
3514
		ob_start();
3515
3516
		pods_view( PODS_DIR . 'ui/front/pagination/' . $pagination . '.php', compact( array_keys( get_defined_vars() ) ) );
3517
3518
		$output = ob_get_clean();
3519
3520
		return $this->do_hook( 'pagination', $this->do_hook( 'pagination_' . $params->type, $output, $params ), $params );
3521
3522
	}
3523
3524
	/**
3525
	 * Return a filter form for searching a Pod
3526
	 *
3527
	 * @param array|string $params Comma-separated list of fields or array of parameters.
0 ignored issues
show
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...
3528
	 *
3529
	 * @return string Filters HTML
3530
	 *
3531
	 * @since 2.0
3532
	 * @link  https://pods.io/docs/filters/
3533
	 */
3534
	public function filters( $params = null ) {
3535
3536
		$defaults = array(
3537
			'fields' => $params,
3538
			'label'  => '',
3539
			'action' => '',
3540
			'search' => '',
3541
		);
3542
3543
		if ( is_array( $params ) ) {
3544
			$params = array_merge( $defaults, $params );
3545
		} else {
3546
			$params = $defaults;
3547
		}
3548
3549
		$pod =& $this;
3550
3551
		/**
3552
		 * Filter the Pods::filters() parameters.
3553
		 *
3554
		 * @param array $params Parameters to filter with.
3555
		 * @param Pods  $pod    Pods object.
3556
		 */
3557
		$params = apply_filters( 'pods_filters_params', $params, $pod );
3558
3559
		$fields = $params['fields'];
3560
3561
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
3562
			$fields = explode( ',', $fields );
3563
		}
3564
3565
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
3566
3567
		// Force array.
3568
		if ( empty( $fields ) ) {
3569
			$fields = array();
3570
		} else {
3571
			// Temporary.
3572
			$filter_fields = $fields;
3573
3574
			$fields = array();
3575
3576
			foreach ( $filter_fields as $k => $field ) {
3577
				$name = $k;
3578
3579
				$defaults = array(
3580
					'name' => $name,
3581
				);
3582
3583
				if ( ! is_array( $field ) ) {
3584
					$name = $field;
3585
3586
					$field = array(
3587
						'name' => $name,
3588
					);
3589
				}
3590
3591
				// @codingStandardsIgnoreLine.
3592
				$field = array_merge( $defaults, $field );
3593
3594
				$field['name'] = trim( $field['name'] );
3595
3596
				if ( pods_v( 'hidden', $field, false, true ) ) {
3597
					$field['type'] = 'hidden';
3598
				}
3599
3600
				if ( isset( $object_fields[ $field['name'] ] ) ) {
3601
					// @codingStandardsIgnoreLine.
3602
					$fields[ $field['name'] ] = array_merge( $object_fields[ $field['name'] ], $field );
3603
				} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
3604
					// @codingStandardsIgnoreLine.
3605
					$fields[ $field['name'] ] = array_merge( $this->fields[ $field['name'] ], $field );
3606
				}
3607
			}//end foreach
3608
3609
			// Cleanup.
3610
			unset( $filter_fields );
3611
		}//end if
3612
3613
		$this->filters = array_keys( $fields );
3614
3615
		$label = $params['label'];
3616
3617
		if ( '' === $label ) {
3618
			$label = __( 'Search', 'pods' );
3619
		}
3620
3621
		$action = $params['action'];
3622
3623
		$search = trim( $params['search'] );
3624
3625
		if ( '' === $search ) {
3626
			$search = pods_v_sanitized( $pod->search_var, 'get', '' );
3627
		}
3628
3629
		ob_start();
3630
3631
		pods_view( PODS_DIR . 'ui/front/filters.php', compact( array_keys( get_defined_vars() ) ) );
3632
3633
		$output = ob_get_clean();
3634
3635
		/**
3636
		 * Filter the HTML output of filters()
3637
		 *
3638
		 * @since unknown
3639
		 *
3640
		 * @param string      $output Filter output.
3641
		 * @param array       $params Params array passed to filters().
3642
		 * @param object|Pods $this   Current Pods object.
3643
		 */
3644
		return apply_filters( 'pods_pods_filters', $output, $params, $this );
3645
	}
3646
3647
	/**
3648
	 * Run a helper within a Pod Page or WP Template
3649
	 *
3650
	 * @see   Pods_Helpers::helper
3651
	 *
3652
	 * @param string|array $helper Helper name.
3653
	 * @param string       $value  Value to run the helper on.
0 ignored issues
show
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...
3654
	 * @param string       $name   Field name.
0 ignored issues
show
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...
3655
	 *
3656
	 * @return mixed Anything returned by the helper
3657
	 * @since 2.0
3658
	 */
3659
	public function helper( $helper, $value = null, $name = null ) {
3660
3661
		$params = array(
3662
			'helper'     => $helper,
3663
			'value'      => $value,
3664
			'name'       => $name,
3665
			'deprecated' => false,
3666
		);
3667
3668
		if ( class_exists( 'Pods_Templates' ) ) {
3669
			$params['deprecated'] = Pods_Templates::$deprecated;
3670
		}
3671
3672
		if ( is_array( $helper ) ) {
3673
			$params = array_merge( $params, $helper );
3674
		}
3675
3676
		if ( class_exists( 'Pods_Helpers' ) ) {
3677
			$value = Pods_Helpers::helper( $params, $this );
3678
		} elseif ( function_exists( $params['helper'] ) ) {
3679
			$disallowed = array(
3680
				'system',
3681
				'exec',
3682
				'popen',
3683
				'eval',
3684
				'preg_replace',
3685
				'create_function',
3686
				'include',
3687
				'include_once',
3688
				'require',
3689
				'require_once',
3690
			);
3691
3692
			$allowed = array();
3693
3694
			/**
3695
			 * Allows adjusting the disallowed callbacks as needed.
3696
			 *
3697
			 * @param array $disallowed List of callbacks not allowed.
3698
			 * @param array $params     Parameters used by Pods::helper() method.
3699
			 *
3700
			 * @since 2.7
3701
			 */
3702
			$disallowed = apply_filters( 'pods_helper_disallowed_callbacks', $disallowed, $params );
3703
3704
			/**
3705
			 * Allows adjusting the allowed allowed callbacks as needed.
3706
			 *
3707
			 * @param array $allowed List of callbacks explicitly allowed.
3708
			 * @param array $params  Parameters used by Pods::helper() method.
3709
			 *
3710
			 * @since 2.7
3711
			 */
3712
			$allowed = apply_filters( 'pods_helper_allowed_callbacks', $allowed, $params );
3713
3714
			// Clean up helper callback (if string).
3715
			if ( is_string( $params['helper'] ) ) {
3716
				$params['helper'] = strip_tags( str_replace( array( '`', chr( 96 ) ), "'", $params['helper'] ) );
3717
			}
3718
3719
			$is_allowed = false;
3720
3721
			if ( ! empty( $allowed ) ) {
3722
				if ( in_array( $params['helper'], $allowed, true ) ) {
3723
					$is_allowed = true;
3724
				}
3725
			} elseif ( ! in_array( $params['helper'], $disallowed, true ) ) {
3726
				$is_allowed = true;
3727
			}
3728
3729
			if ( $is_allowed ) {
3730
				$value = call_user_func( $params['helper'], $value );
3731
			}
3732
		} else {
3733
			$value = apply_filters( $params['helper'], $value );
3734
		}//end if
3735
3736
		return $value;
3737
	}
3738
3739
	/**
3740
	 * Display the page template
3741
	 *
3742
	 * @see   Pods_Templates::template
3743
	 *
3744
	 * @param string      $template_name The template name.
3745
	 * @param string|null $code          Custom template code to use instead.
3746
	 * @param bool        $deprecated    Whether to use deprecated functionality based on old function usage.
3747
	 *
3748
	 * @return mixed Template output
3749
	 *
3750
	 * @since 2.0
3751
	 * @link  https://pods.io/docs/template/
3752
	 */
3753
	public function template( $template_name, $code = null, $deprecated = false ) {
3754
3755
		$out = null;
3756
3757
		$obj =& $this;
3758
3759
		if ( ! empty( $code ) ) {
3760
			// backwards compatibility.
3761
			$code = str_replace( '$this->', '$obj->', $code );
3762
3763
			/**
3764
			 * Filter the template code before running it.
3765
			 *
3766
			 * @param string $code          Template code.
3767
			 * @param string $template_name Template name.
3768
			 * @param Pods  $pod            Pods object.
3769
			 */
3770
			$code = apply_filters( 'pods_templates_pre_template', $code, $template_name, $this );
3771
3772
			/**
3773
			 * Filter the template code before running it.
3774
			 *
3775
			 * @param string $code          Template code.
3776
			 * @param string $template_name Template name.
3777
			 * @param Pods  $pod            Pods object.
3778
			 */
3779
			$code = apply_filters( "pods_templates_pre_template_{$template_name}", $code, $template_name, $this );
3780
3781
			ob_start();
3782
3783
			if ( ! empty( $code ) ) {
3784
				// Only detail templates need $this->id.
3785
				if ( empty( $this->id ) ) {
3786
					while ( $this->fetch() ) {
3787
						// @codingStandardsIgnoreLine
3788
						echo $this->do_magic_tags( $code );
3789
					}
3790
				} else {
3791
					// @codingStandardsIgnoreLine
3792
					echo $this->do_magic_tags( $code );
3793
				}
3794
			}
3795
3796
			$out = ob_get_clean();
3797
3798
			/**
3799
			 * Filter the template output.
3800
			 *
3801
			 * @param string $out           Template output.
3802
			 * @param string $code          Template code.
3803
			 * @param string $template_name Template name.
3804
			 * @param Pods   $pod           Pods object.
3805
			 */
3806
			$out = apply_filters( 'pods_templates_post_template', $out, $code, $template_name, $this );
3807
3808
			/**
3809
			 * Filter the template output.
3810
			 *
3811
			 * @param string $out           Template output.
3812
			 * @param string $code          Template code.
3813
			 * @param string $template_name Template name.
3814
			 * @param Pods   $pod           Pods object.
3815
			 */
3816
			$out = apply_filters( "pods_templates_post_template_{$template_name}", $out, $code, $template_name, $this );
3817
		} elseif ( class_exists( 'Pods_Templates' ) ) {
3818
			$out = Pods_Templates::template( $template_name, $code, $this, $deprecated );
3819
		} elseif ( trim( preg_replace( '/[^a-zA-Z0-9_\-\/]/', '', $template_name ), ' /-' ) === $template_name ) {
3820
			ob_start();
3821
3822
			$default_templates = array(
3823
				'pods/' . $template_name,
3824
				'pods-' . $template_name,
3825
				$template_name,
3826
			);
3827
3828
			/**
3829
			 * Filter the default Pods Template files.
3830
			 *
3831
			 * @param array $default_templates Default Pods Template files.
3832
			 */
3833
			$default_templates = apply_filters( 'pods_template_default_templates', $default_templates );
3834
3835
			// Only detail templates need $this->id.
3836
			if ( empty( $this->id ) ) {
3837
				while ( $this->fetch() ) {
3838
					pods_template_part( $default_templates, compact( array_keys( get_defined_vars() ) ) );
3839
				}
3840
			} else {
3841
				pods_template_part( $default_templates, compact( array_keys( get_defined_vars() ) ) );
3842
			}
3843
3844
			$out = ob_get_clean();
3845
3846
			/**
3847
			 * Filter the template output.
3848
			 *
3849
			 * @param string $out           Template output.
3850
			 * @param string $code          Template code.
3851
			 * @param string $template_name Template name.
3852
			 * @param Pods   $pod           Pods object.
3853
			 */
3854
			$out = apply_filters( 'pods_templates_post_template', $out, $code, $template_name, $this );
3855
3856
			/**
3857
			 * Filter the template output.
3858
			 *
3859
			 * @param string $out           Template output.
3860
			 * @param string $code          Template code.
3861
			 * @param string $template_name Template name.
3862
			 * @param Pods   $pod           Pods object.
3863
			 */
3864
			$out = apply_filters( "pods_templates_post_template_{$template_name}", $out, $code, $template_name, $this );
3865
		}//end if
3866
3867
		return $out;
3868
	}
3869
3870
	/**
3871
	 * Embed a form to add / edit a pod item from within your theme. Provide an array of $fields to include
3872
	 * and override options where needed. For WP object based Pods, you can pass through the WP object
3873
	 * field names too, such as "post_title" or "post_content" for example.
3874
	 *
3875
	 * @param array  $params    (optional) Fields to show on the form, defaults to all fields.
0 ignored issues
show
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...
3876
	 * @param string $label     (optional) Save button label, defaults to "Save Changes".
0 ignored issues
show
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...
3877
	 * @param string $thank_you (optional) Thank you URL to send to upon success.
0 ignored issues
show
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...
3878
	 *
3879
	 * @return bool|mixed
3880
	 * @since 2.0
3881
	 * @link  https://pods.io/docs/form/
3882
	 */
3883
	public function form( $params = null, $label = null, $thank_you = null ) {
3884
3885
		$defaults = array(
3886
			'fields'      => $params,
3887
			'label'       => $label,
3888
			'thank_you'   => $thank_you,
3889
			'fields_only' => false,
3890
		);
3891
3892
		if ( is_array( $params ) ) {
3893
			$params = array_merge( $defaults, $params );
3894
		} else {
3895
			$params = $defaults;
3896
		}
3897
3898
		$pod =& $this;
3899
3900
		$params = $this->do_hook( 'form_params', $params );
3901
3902
		$fields = $params['fields'];
3903
3904
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
3905
			$fields = explode( ',', $fields );
3906
		}
3907
3908
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
3909
3910
		if ( empty( $fields ) ) {
3911
			// Add core object fields if $fields is empty.
3912
			$fields = array_merge( $object_fields, $this->fields );
3913
		}
3914
3915
		// Temporary.
3916
		$form_fields = $fields;
3917
3918
		$fields = array();
3919
3920
		foreach ( $form_fields as $k => $field ) {
3921
			$name = $k;
3922
3923
			$defaults = array(
3924
				'name' => $name,
3925
			);
3926
3927
			if ( ! is_array( $field ) ) {
3928
				$name = $field;
3929
3930
				$field = array(
3931
					'name' => $name,
3932
				);
3933
			}
3934
3935
			// @codingStandardsIgnoreLine.
3936
			$field = array_merge( $defaults, $field );
3937
3938
			$field['name'] = trim( $field['name'] );
3939
3940
			$default_value = pods_v( 'default', $field );
3941
			$value         = pods_v( 'value', $field );
3942
3943
			if ( empty( $field['name'] ) ) {
3944
				$field['name'] = trim( $name );
3945
			}
3946
3947
			if ( isset( $object_fields[ $field['name'] ] ) ) {
3948
				// @codingStandardsIgnoreLine.
3949
				$field = array_merge( $object_fields[ $field['name'] ], $field );
3950
			} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
3951
				// @codingStandardsIgnoreLine.
3952
				$field = array_merge( $this->fields[ $field['name'] ], $field );
3953
			}
3954
3955
			if ( pods_v( 'hidden', $field, false, true ) ) {
3956
				$field['type'] = 'hidden';
3957
			}
3958
3959
			$fields[ $field['name'] ] = $field;
3960
3961
			if ( empty( $this->id ) && null !== $default_value ) {
3962
				$this->row_override[ $field['name'] ] = $default_value;
3963
			} elseif ( ! empty( $this->id ) && null !== $value ) {
3964
				$this->row[ $field['name'] ] = $value;
3965
			}
3966
		}//end foreach
3967
3968
		// Cleanup.
3969
		unset( $form_fields );
3970
3971
		$fields = $this->do_hook( 'form_fields', $fields, $params );
3972
3973
		$label = $params['label'];
3974
3975
		if ( empty( $label ) ) {
3976
			$label = __( 'Save Changes', 'pods' );
3977
		}
3978
3979
		$thank_you   = $params['thank_you'];
3980
		$fields_only = $params['fields_only'];
3981
3982
		PodsForm::$form_counter ++;
3983
3984
		ob_start();
3985
3986
		if ( empty( $thank_you ) ) {
3987
			$success = 'success';
3988
3989
			if ( 1 < PodsForm::$form_counter ) {
3990
				$success .= PodsForm::$form_counter;
3991
			}
3992
3993
			$thank_you = pods_query_arg( array(
3994
				'success*' => null,
3995
				$success   => 1,
3996
			) );
3997
3998
			if ( 1 === (int) pods_v( $success, 'get', 0 ) ) {
3999
				$message = __( 'Form submitted successfully', 'pods' );
4000
4001
				/**
4002
				 * Change the text of the message that appears on successful form submission.
4003
				 *
4004
				 * @param string $message Success message.
4005
				 *
4006
				 * @since 2.7
4007
				 */
4008
				$message = apply_filters( 'pods_pod_form_success_message', $message );
4009
4010
				echo '<div id="message" class="pods-form-front-success">' . wp_kses_post( $message ) . '</div>';
4011
			}
4012
		}//end if
4013
4014
		pods_view( PODS_DIR . 'ui/front/form.php', compact( array_keys( get_defined_vars() ) ) );
4015
4016
		$output = ob_get_clean();
4017
4018
		if ( empty( $this->id ) ) {
4019
			$this->row_override = array();
4020
		}
4021
4022
		return $this->do_hook( 'form', $output, $fields, $label, $thank_you, $this, $this->id() );
4023
	}
4024
4025
	/**
4026
	 * Render a singular view for the Pod item content.
4027
	 *
4028
	 * @param array|string|null $fields (optional) Fields to show in the view, defaults to all fields.
4029
	 *
4030
	 * @return mixed
4031
	 * @since 2.3.10
4032
	 */
4033
	public function view( $fields = null ) {
4034
4035
		$pod =& $this;
4036
4037
		// Convert comma separated list of fields to an array.
4038
		if ( null !== $fields && ! is_array( $fields ) && 0 < strlen( $fields ) ) {
4039
			$fields = explode( ',', $fields );
4040
		}
4041
4042
		$object_fields = (array) pods_v( 'object_fields', $this->pod_data, array(), true );
4043
4044
		if ( empty( $fields ) ) {
4045
			// Add core object fields if $fields is empty.
4046
			$fields = array_merge( $object_fields, $this->fields );
4047
		}
4048
4049
		// Temporary.
4050
		$view_fields = $fields;
4051
4052
		$fields = array();
4053
4054
		foreach ( $view_fields as $name => $field ) {
0 ignored issues
show
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...
4055
			$defaults = array(
4056
				'name' => $name,
4057
			);
4058
4059
			if ( ! is_array( $field ) ) {
4060
				$name = $field;
4061
4062
				$field = array(
4063
					'name' => $name,
4064
				);
4065
			}
4066
4067
			// @codingStandardsIgnoreLine.
4068
			$field = array_merge( $defaults, $field );
4069
4070
			$field['name'] = trim( $field['name'] );
4071
4072
			if ( empty( $field['name'] ) ) {
4073
				$field['name'] = trim( $name );
4074
			}
4075
4076
			if ( isset( $object_fields[ $field['name'] ] ) ) {
4077
				// @codingStandardsIgnoreLine.
4078
				$field = array_merge( $field, $object_fields[ $field['name'] ] );
4079
			} elseif ( isset( $this->fields[ $field['name'] ] ) ) {
4080
				// @codingStandardsIgnoreLine.
4081
				$field = array_merge( $this->fields[ $field['name'] ], $field );
4082
			}
4083
4084
			if ( pods_v( 'hidden', $field, false, true ) || 'hidden' === $field['type'] ) {
4085
				continue;
4086
			} elseif ( ! PodsForm::permission( $field['type'], $field['name'], $field['options'], $fields, $pod, $pod->id() ) ) {
4087
				continue;
4088
			}
4089
4090
			$fields[ $field['name'] ] = $field;
4091
		}//end foreach
4092
4093
		// Cleanup.
4094
		unset( $view_fields );
4095
4096
		$output = pods_view( PODS_DIR . 'ui/front/view.php', compact( array_keys( get_defined_vars() ) ), false, 'cache', true );
4097
4098
		return $this->do_hook( 'view', $output, $fields, $this->id() );
4099
4100
	}
4101
4102
	/**
4103
	 * Replace magic tags with their values
4104
	 *
4105
	 * @param string $code The content to evaluate.
4106
	 *
4107
	 * @return string Code with Magic Tags evaluated
4108
	 *
4109
	 * @since 2.0
4110
	 */
4111
	public function do_magic_tags( $code ) {
4112
4113
		/**
4114
		 * Filters the Pods magic tags content before the default function.
4115
		 * Allows complete replacement of the Pods magic tag engine.
4116
		 *
4117
		 * @param null   $pre  Default is null which processes magic tags normally. Return any other value to override.
4118
		 * @param string $code The content to evaluate.
4119
		 * @param Pods   $pods The Pods Object.
4120
		 *
4121
		 * @since 2.7
4122
		 */
4123
		$pre = apply_filters( 'pods_pre_do_magic_tags', null, $code, $this );
4124
		if ( null !== $pre ) {
4125
			return $pre;
4126
		}
4127
4128
		return preg_replace_callback( '/({@(.*?)})/m', array( $this, 'process_magic_tags' ), $code );
4129
	}
4130
4131
	/**
4132
	 * Replace magic tags with their values
4133
	 *
4134
	 * @param string|array $tag The magic tag to process.
4135
	 *
4136
	 * @return string Code with Magic Tags evaluated
4137
	 *
4138
	 * @since 2.0.2
4139
	 */
4140
	private function process_magic_tags( $tag ) {
4141
4142
		if ( is_array( $tag ) ) {
4143
			if ( ! isset( $tag[2] ) && '' === trim( $tag[2] ) ) {
4144
				return '';
4145
			}
4146
4147
			$tag = $tag[2];
4148
		}
4149
4150
		$tag = trim( $tag, ' {@}' );
4151
		$tag = explode( ',', $tag );
4152
4153
		if ( empty( $tag ) || ! isset( $tag[0] ) || '' === trim( $tag[0] ) ) {
4154
			return '';
4155
		}
4156
4157
		foreach ( $tag as $k => $v ) {
4158
			$tag[ $k ] = trim( $v );
4159
		}
4160
4161
		$field_name = $tag[0];
4162
4163
		$helper_name = '';
4164
		$before      = '';
4165
		$after       = '';
4166
4167
		if ( isset( $tag[1] ) && ! empty( $tag[1] ) ) {
4168
			$value = $this->field( $field_name );
4169
4170
			$helper_name = $tag[1];
4171
4172
			$value = $this->helper( $helper_name, $value, $field_name );
4173
		} else {
4174
			$value = $this->display( $field_name );
4175
		}
4176
4177
		if ( isset( $tag[2] ) && ! empty( $tag[2] ) ) {
4178
			$before = $tag[2];
4179
		}
4180
4181
		if ( isset( $tag[3] ) && ! empty( $tag[3] ) ) {
4182
			$after = $tag[3];
4183
		}
4184
4185
		/**
4186
		 * Filter the magic tag output for a value.
4187
		 *
4188
		 * @param string $value      Magic tag output for value.
4189
		 * @param string $field_name Magic tag field name.
4190
		 * @param string $before     Before content.
4191
		 * @param string $after      After content.
4192
		 */
4193
		$value = apply_filters( 'pods_do_magic_tags', $value, $field_name, $helper_name, $before, $after );
4194
4195
		if ( is_array( $value ) ) {
4196
			$value = pods_serial_comma( $value, array(
4197
				'field'  => $field_name,
4198
				'fields' => $this->fields,
4199
			) );
4200
		}
4201
4202
		if ( null !== $value && false !== $value ) {
4203
			return $before . $value . $after;
4204
		}
4205
4206
		return '';
4207
	}
4208
4209
	/**
4210
	 *
4211
	 * Generate UI for Data Management
4212
	 *
4213
	 * @param mixed $options Array or String containing Pod or Options to be used.
4214
	 * @param bool  $amend   Whether to amend the default UI options or replace entirely.
4215
	 *
4216
	 * @return PodsUI|null UI object or null if custom UI used
4217
	 *
4218
	 * @since 2.3.10
4219
	 */
4220
	public function ui( $options = null, $amend = false ) {
0 ignored issues
show
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...
4221
4222
		$num = '';
4223
4224
		if ( empty( $options ) ) {
4225
			$options = array();
4226
		} else {
4227
			$num = pods_v_sanitized( 'num', $options, '' );
4228
4229
			if ( empty( $num ) ) {
4230
				$num = '';
4231
			}
4232
		}
4233
4234
		$check_id = pods_v( 'id' . $num, 'get', null, true );
4235
4236
		// @codingStandardsIgnoreLine
4237
		if ( $this->id() != $check_id ) {
4238
			$this->fetch( $check_id );
4239
		}
4240
4241
		if ( ! empty( $options ) && ! $amend ) {
4242
			$this->ui = $options;
4243
4244
			return pods_ui( $this );
4245
		} elseif ( ! empty( $options ) || 'custom' !== pods_v( 'ui_style', $this->pod_data['options'], 'post_type', true ) ) {
4246
			$actions_enabled = pods_v( 'ui_actions_enabled', $this->pod_data['options'] );
4247
4248
			if ( ! empty( $actions_enabled ) ) {
4249
				$actions_enabled = (array) $actions_enabled;
4250
			} else {
4251
				$actions_enabled = array();
4252
			}
4253
4254
			$available_actions = array(
4255
				'add',
4256
				'edit',
4257
				'duplicate',
4258
				'delete',
4259
				'reorder',
4260
				'export',
4261
			);
4262
4263
			if ( ! empty( $actions_enabled ) ) {
4264
				$actions_disabled = array(
4265
					'view' => 'view',
4266
				);
4267
4268
				foreach ( $available_actions as $action ) {
4269
					if ( ! in_array( $action, $actions_enabled, true ) ) {
4270
						$actions_disabled[ $action ] = $action;
4271
					}
4272
				}
4273
			} else {
4274
				$actions_disabled = array(
4275
					'duplicate' => 'duplicate',
4276
					'view'      => 'view',
4277
					'export'    => 'export',
4278
				);
4279
4280
				if ( 1 === pods_v( 'ui_export', $this->pod_data['options'], 0 ) ) {
4281
					unset( $actions_disabled['export'] );
4282
				}
4283
			}//end if
4284
4285
			if ( empty( $options ) ) {
4286
				$author_restrict = false;
4287
4288
				if ( isset( $this->fields['author'] ) && 'pick' === $this->fields['author']['type'] && 'user' === $this->fields['author']['pick_object'] ) {
4289
					$author_restrict = 'author.ID';
4290
				}
4291
4292
				if ( ! pods_is_admin( array( 'pods', 'pods_content' ) ) ) {
4293
					if ( ! current_user_can( 'pods_add_' . $this->pod ) ) {
4294
						$actions_disabled['add'] = 'add';
4295
4296
						if ( 'add' === pods_v( 'action' . $num ) ) {
4297
							$_GET[ 'action' . $num ] = 'manage';
4298
						}
4299
					}
4300
4301
					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...
4302
						$actions_disabled['edit'] = 'edit';
4303
					}
4304
4305
					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...
4306
						$actions_disabled['delete'] = 'delete';
4307
					}
4308
4309
					if ( ! current_user_can( 'pods_reorder_' . $this->pod ) ) {
4310
						$actions_disabled['reorder'] = 'reorder';
4311
					}
4312
4313
					if ( ! current_user_can( 'pods_export_' . $this->pod ) ) {
4314
						$actions_disabled['export'] = 'export';
4315
					}
4316
				}//end if
4317
			}//end if
4318
4319
			$_GET[ 'action' . $num ] = pods_v_sanitized( 'action' . $num, 'get', pods_v( 'action', $options, 'manage' ), true );
4320
4321
			$index = $this->pod_data['field_id'];
4322
			$label = __( 'ID', 'pods' );
4323
4324
			if ( isset( $this->pod_data['fields'][ $this->pod_data['field_index'] ] ) ) {
4325
				$index = $this->pod_data['field_index'];
4326
				$label = $this->pod_data['fields'][ $this->pod_data['field_index'] ];
4327
			}
4328
4329
			$manage = array(
4330
				$index => $label,
4331
			);
4332
4333
			if ( isset( $this->pod_data['fields']['modified'] ) ) {
4334
				$manage['modified'] = $this->pod_data['fields']['modified']['label'];
4335
			}
4336
4337
			$manage_fields = (array) pods_v( 'ui_fields_manage', $this->pod_data['options'] );
4338
4339
			if ( ! empty( $manage_fields ) ) {
4340
				$manage_new = array();
4341
4342
				foreach ( $manage_fields as $manage_field ) {
4343
					if ( isset( $this->pod_data['fields'][ $manage_field ] ) ) {
4344
						$manage_new[ $manage_field ] = $this->pod_data['fields'][ $manage_field ];
4345
					} elseif ( isset( $this->pod_data['object_fields'][ $manage_field ] ) ) {
4346
						$manage_new[ $manage_field ] = $this->pod_data['object_fields'][ $manage_field ];
4347
					} elseif ( $manage_field === $this->pod_data['field_id'] ) {
4348
						$field = array(
4349
							'name'  => $manage_field,
4350
							'label' => 'ID',
4351
							'type'  => 'number',
4352
							'width' => '8%',
4353
						);
4354
4355
						$manage_new[ $manage_field ] = PodsForm::field_setup( $field, null, $field['type'] );
4356
					}
4357
				}
4358
4359
				if ( ! empty( $manage_new ) ) {
4360
					$manage = $manage_new;
4361
				}
4362
			}//end if
4363
4364
			$manage = apply_filters( 'pods_admin_ui_fields_' . $this->pod, apply_filters( 'pods_admin_ui_fields', $manage, $this->pod, $this ), $this->pod, $this );
4365
4366
			$icon = pods_v( 'ui_icon', $this->pod_data['options'] );
4367
4368
			if ( ! empty( $icon ) ) {
4369
				$icon = pods_image_url( $icon, '32x32' );
4370
			}
4371
4372
			$filters = pods_v( 'ui_filters', $this->pod_data['options'] );
4373
4374
			if ( ! empty( $filters ) ) {
4375
				$filters_new = array();
4376
4377
				$filters = (array) $filters;
4378
4379
				foreach ( $filters as $filter_field ) {
4380
					if ( isset( $this->pod_data['fields'][ $filter_field ] ) ) {
4381
						$filters_new[ $filter_field ] = $this->pod_data['fields'][ $filter_field ];
4382
					} elseif ( isset( $this->pod_data['object_fields'][ $filter_field ] ) ) {
4383
						$filters_new[ $filter_field ] = $this->pod_data['object_fields'][ $filter_field ];
4384
					}
4385
				}
4386
4387
				$filters = $filters_new;
4388
			}
4389
4390
			$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...
4391
				'fields'           => array(
4392
					'manage'    => $manage,
4393
					'add'       => $this->pod_data['fields'],
4394
					'edit'      => $this->pod_data['fields'],
4395
					'duplicate' => $this->pod_data['fields'],
4396
				),
4397
				'icon'             => $icon,
4398
				'actions_disabled' => $actions_disabled,
4399
				'actions_bulk'     => array(),
4400
			);
4401
4402
			if ( ! empty( $filters ) ) {
4403
				$ui['fields']['search'] = $filters;
4404
				$ui['filters']          = array_keys( $filters );
4405
				$ui['filters_enhanced'] = true;
4406
			}
4407
4408
			$reorder_field = pods_v( 'ui_reorder_field', $this->pod_data['options'] );
4409
4410
			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 ] ) ) ) {
4411
				$ui['reorder']     = array( 'on' => $reorder_field );
4412
				$ui['orderby']     = $reorder_field;
4413
				$ui['orderby_dir'] = 'ASC';
4414
			}
4415
4416
			if ( ! empty( $author_restrict ) ) {
4417
				$ui['restrict'] = array( 'author_restrict' => $author_restrict );
4418
			}
4419
4420
			if ( ! in_array( 'export', $ui['actions_disabled'], true ) ) {
4421
				$ui['actions_bulk']['export'] = array(
4422
					'label' => __( 'Export', 'pods' ),
4423
					// callback not needed, Pods has this built-in for export.
4424
				);
4425
			}
4426
4427
			if ( ! in_array( 'delete', $ui['actions_disabled'], true ) ) {
4428
				$ui['actions_bulk']['delete'] = array(
4429
					'label' => __( 'Delete', 'pods' ),
4430
					// callback not needed, Pods has this built-in for delete.
4431
				);
4432
			}
4433
4434
			$detail_url = pods_v( 'detail_url', $this->pod_data['options'] );
4435
4436
			if ( 0 < strlen( $detail_url ) ) {
4437
				$ui['actions_custom'] = array(
4438
					'view_url' => array(
4439
						'label' => 'View',
4440
						'link'  => get_site_url() . '/' . $detail_url,
4441
					),
4442
				);
4443
			}
4444
4445
			// @todo Customize the Add New / Manage links to point to their correct menu items.
4446
			$ui = apply_filters( 'pods_admin_ui_' . $this->pod, apply_filters( 'pods_admin_ui', $ui, $this->pod, $this ), $this->pod, $this );
4447
4448
			// Override UI options.
4449
			foreach ( $options as $option => $value ) {
4450
				$ui[ $option ] = $value;
4451
			}
4452
4453
			$this->ui = $ui;
4454
4455
			return pods_ui( $this );
4456
		}//end if
4457
4458
		do_action( 'pods_admin_ui_custom', $this );
4459
		do_action( 'pods_admin_ui_custom_' . $this->pod, $this );
4460
4461
		return null;
4462
	}
4463
4464
	/**
4465
	 * Handle filters / actions for the class
4466
	 *
4467
	 * @see   pods_do_hook
4468
	 *
4469
	 * @param string $name Hook name.
4470
	 *
4471
	 * @return mixed Value filtered
4472
	 *
4473
	 * @since 2.0
4474
	 */
4475
	private function do_hook( $name ) {
4476
4477
		$args = func_get_args();
4478
4479
		if ( empty( $args ) ) {
4480
			return false;
4481
		}
4482
4483
		// Remove first argument.
4484
		array_shift( $args );
4485
4486
		return pods_do_hook( 'pods', $name, $args, $this );
4487
	}
4488
4489
	/**
4490
	 * Handle variables that have been deprecated and PodsData vars
4491
	 *
4492
	 * @param string $name Property name.
4493
	 *
4494
	 * @return mixed
4495
	 *
4496
	 * @since 2.0
4497
	 */
4498
	public function __get( $name ) {
4499
4500
		$name = (string) $name;
4501
4502
		// PodsData vars.
4503
		if ( isset( $this->data->{$name} ) && 0 === strpos( $name, 'field_' ) ) {
4504
			return $this->data->{$name};
4505
		}
4506
4507
		if ( ! $this->deprecated ) {
4508
			require_once PODS_DIR . 'deprecated/classes/Pods.php';
4509
4510
			$this->deprecated = new Pods_Deprecated( $this );
4511
		}
4512
4513
		$var = null;
4514
4515
		$pod_class_exists = class_exists( 'Pod' );
4516
4517
		if ( isset( $this->deprecated->{$name} ) ) {
4518
			if ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4519
				pods_deprecated( "Pods->{$name}", '2.0' );
4520
			}
4521
4522
			$var = $this->deprecated->{$name};
4523
		} elseif ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4524
			pods_deprecated( "Pods->{$name}", '2.0' );
4525
		}
4526
4527
		return $var;
4528
	}
4529
4530
	/**
4531
	 * Handle methods that have been deprecated and any aliasing.
4532
	 *
4533
	 * @param string $name Function name.
4534
	 * @param array  $args Arguments passed to function.
4535
	 *
4536
	 * @return mixed|null
4537
	 *
4538
	 * @since 2.0
4539
	 */
4540
	public function __call( $name, $args ) {
4541
4542
		$name = (string) $name;
4543
4544
		// select > find alias.
4545
		if ( 'select' === $name ) {
4546
			return call_user_func_array( array( $this, 'find' ), $args );
4547
		}
4548
4549
		if ( ! $this->deprecated ) {
4550
			require_once PODS_DIR . 'deprecated/classes/Pods.php';
4551
4552
			$this->deprecated = new Pods_Deprecated( $this );
4553
		}
4554
4555
		$pod_class_exists = class_exists( 'Pod' );
4556
4557
		if ( method_exists( $this->deprecated, $name ) ) {
4558
			return call_user_func_array( array( $this->deprecated, $name ), $args );
4559
			// @codingStandardsIgnoreLine
4560
		} elseif ( ! $pod_class_exists || Pod::$deprecated_notice ) {
4561
			pods_deprecated( "Pods::{$name}", '2.0' );
4562
		}
4563
4564
		return null;
4565
	}
4566
4567
	/**
4568
	 * Handle casting a Pods() object to string
4569
	 *
4570
	 * @return string Pod type and name in CURIE notation
4571
	 */
4572
	public function __toString() {
4573
4574
		$string = '';
4575
4576
		if ( ! empty( $this->pod_data ) ) {
4577
			$string = sprintf( '%s:%s', $this->pod_data['type'], $this->pod_data['name'] );
4578
		}
4579
4580
		return $string;
4581
4582
	}
4583
}
4584