Completed
Push — master ( a2ba05...f6b388 )
by Stephanie
02:49 queued 11s
created

FrmEntryMeta::get_top_level_entry_ids()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	die( 'You are not allowed to call this page directly.' );
4
}
5
6
class FrmEntryMeta {
7
8
	/**
9
	 * @param string $meta_key
10
	 */
11
	public static function add_entry_meta( $entry_id, $field_id, $meta_key = null, $meta_value ) {
12
		global $wpdb;
13
14
		if ( FrmAppHelper::is_empty_value( $meta_value ) ) {
15
			// don't save blank fields
16
			return 0;
17
		}
18
19
		$new_values = array(
20
			'meta_value' => is_array( $meta_value ) ? serialize( array_filter( $meta_value, 'FrmAppHelper::is_not_empty_value' ) ) : trim( $meta_value ),
21
			'item_id'    => $entry_id,
22
			'field_id'   => $field_id,
23
			'created_at' => current_time( 'mysql', 1 ),
24
		);
25
26
		self::set_value_before_save( $new_values );
27
		$new_values = apply_filters( 'frm_add_entry_meta', $new_values );
28
29
		$query_results = $wpdb->insert( $wpdb->prefix . 'frm_item_metas', $new_values );
30
31
		if ( $query_results ) {
32
			self::clear_cache();
33
			wp_cache_delete( $entry_id, 'frm_entry' );
34
			$id = $wpdb->insert_id;
35
		} else {
36
			$id = 0;
37
		}
38
39
		return $id;
40
	}
41
42
	/**
43
	 * @param int $entry_id
44
	 * @param int $field_id
45
	 * @param string $meta_key deprecated
46
	 * @param array|string $meta_value
47
	 *
48
	 * @return bool|false|int
49
	 */
50
	public static function update_entry_meta( $entry_id, $field_id, $meta_key = null, $meta_value ) {
51
		if ( ! $field_id ) {
52
			return false;
53
		}
54
55
		global $wpdb;
56
57
		$values               = array(
58
			'item_id'  => $entry_id,
59
			'field_id' => $field_id,
60
		);
61
		$where_values         = $values;
62
		$values['meta_value'] = $meta_value;
63
		self::set_value_before_save( $values );
64
		$values = apply_filters( 'frm_update_entry_meta', $values );
65
66
		if ( is_array( $values['meta_value'] ) ) {
67
			$values['meta_value'] = array_filter( $values['meta_value'], 'FrmAppHelper::is_not_empty_value' );
68
		}
69
		$meta_value = maybe_serialize( $values['meta_value'] );
70
71
		wp_cache_delete( $entry_id, 'frm_entry' );
72
		self::clear_cache();
73
74
		return $wpdb->update( $wpdb->prefix . 'frm_item_metas', array( 'meta_value' => $meta_value ), $where_values );
75
	}
76
77
	/**
78
	 * @since 3.0
79
	 */
80
	private static function set_value_before_save( &$values ) {
81
		$field = FrmField::getOne( $values['field_id'] );
82
		if ( $field ) {
83
			$field_obj = FrmFieldFactory::get_field_object( $field );
84
85
			$values['meta_value'] = $field_obj->set_value_before_save( $values['meta_value'] );
86
		}
87
	}
88
89
	/**
90
	 * @since 3.0
91
	 */
92
	private static function get_value_to_save( $atts, &$value ) {
93
		if ( is_object( $atts['field'] ) ) {
94
			$field_obj = FrmFieldFactory::get_field_object( $atts['field'] );
95
			$value     = $field_obj->get_value_to_save(
96
				$value,
97
				array(
98
					'entry_id' => $atts['entry_id'],
99
					'field_id' => $atts['field_id'],
100
				)
101
			);
102
		}
103
104
		$value = apply_filters( 'frm_prepare_data_before_db', $value, $atts['field_id'], $atts['entry_id'], array( 'field' => $atts['field'] ) );
105
	}
106
107
	public static function update_entry_metas( $entry_id, $values ) {
108
		global $wpdb;
109
110
		$prev_values = FrmDb::get_col(
111
			$wpdb->prefix . 'frm_item_metas',
112
			array(
113
				'item_id'    => $entry_id,
114
				'field_id !' => 0,
115
			),
116
			'field_id'
117
		);
118
119
		foreach ( $values as $field_id => $meta_value ) {
120
			$field = false;
121
			if ( ! empty( $field_id ) ) {
122
				$field = FrmField::getOne( $field_id );
123
			}
124
125
			self::get_value_to_save( compact( 'field', 'field_id', 'entry_id' ), $meta_value );
126
127
			if ( $prev_values && in_array( $field_id, $prev_values ) ) {
128
129
				if ( ( is_array( $meta_value ) && empty( $meta_value ) ) || ( ! is_array( $meta_value ) && trim( $meta_value ) == '' ) ) {
130
					// remove blank fields
131
					unset( $values[ $field_id ] );
132
				} else {
133
					// if value exists, then update it
134
					self::update_entry_meta( $entry_id, $field_id, '', $meta_value );
135
				}
136
			} else {
137
				// if value does not exist, then create it
138
				self::add_entry_meta( $entry_id, $field_id, '', $meta_value );
139
			}
140
		}
141
142
		if ( empty( $prev_values ) ) {
143
			return;
144
		}
145
146
		$prev_values = array_diff( $prev_values, array_keys( $values ) );
147
148
		if ( empty( $prev_values ) ) {
149
			return;
150
		}
151
152
		// prepare the query
153
		$where = array(
154
			'item_id'  => $entry_id,
155
			'field_id' => $prev_values,
156
		);
157
		FrmDb::get_where_clause_and_values( $where );
158
159
		// Delete any leftovers
160
		$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas ' . $where['where'], $where['values'] ) ); // WPCS: unprepared SQL ok.
161
		self::clear_cache();
162
	}
163
164
	public static function duplicate_entry_metas( $old_id, $new_id ) {
165
		$metas = self::get_entry_meta_info( $old_id );
166
		foreach ( $metas as $meta ) {
0 ignored issues
show
Bug introduced by
The expression $metas of type array|null|string|object 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...
167
			self::add_entry_meta( $new_id, $meta->field_id, null, $meta->meta_value );
168
			unset( $meta );
169
		}
170
		self::clear_cache();
171
	}
172
173
	public static function delete_entry_meta( $entry_id, $field_id ) {
174
		global $wpdb;
175
		self::clear_cache();
176
177
		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}frm_item_metas WHERE field_id=%d AND item_id=%d", $field_id, $entry_id ) );
178
	}
179
180
	/**
181
	 * Clear entry meta caching
182
	 * Called when a meta is added or changed
183
	 *
184
	 * @since 2.0.5
185
	 */
186
	public static function clear_cache() {
187
		FrmDb::cache_delete_group( 'frm_entry_meta' );
188
		FrmDb::cache_delete_group( 'frm_item_meta' );
189
	}
190
191
	/**
192
	 * @since 2.0.9
193
	 */
194
	public static function get_meta_value( $entry, $field_id ) {
195
		if ( isset( $entry->metas ) ) {
196
			return isset( $entry->metas[ $field_id ] ) ? $entry->metas[ $field_id ] : false;
197
		} else {
198
			return self::get_entry_meta_by_field( $entry->id, $field_id );
199
		}
200
	}
201
202
	public static function get_entry_meta_by_field( $entry_id, $field_id ) {
203
		global $wpdb;
204
205
		if ( is_object( $entry_id ) ) {
206
			$entry    = $entry_id;
207
			$entry_id = $entry->id;
208
			$cached   = $entry;
209
		} else {
210
			$entry_id = (int) $entry_id;
211
			$cached   = FrmDb::check_cache( $entry_id, 'frm_entry' );
212
		}
213
214
		if ( $cached && isset( $cached->metas ) && isset( $cached->metas[ $field_id ] ) ) {
215
			$result = $cached->metas[ $field_id ];
216
217
			return wp_unslash( $result );
218
		}
219
220
		$get_table = $wpdb->prefix . 'frm_item_metas';
221
		$query     = array( 'item_id' => $entry_id );
222 View Code Duplication
		if ( is_numeric( $field_id ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
223
			$query['field_id'] = $field_id;
224
		} else {
225
			$get_table             .= ' it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_fields fi ON it.field_id=fi.id';
226
			$query['fi.field_key'] = $field_id;
227
		}
228
229
		$result = FrmDb::get_var( $get_table, $query, 'meta_value' );
230
		FrmAppHelper::unserialize_or_decode( $result );
231
		$result = wp_unslash( $result );
232
233
		return $result;
234
	}
235
236
	public static function get_entry_metas_for_field( $field_id, $order = '', $limit = '', $args = array() ) {
237
		$defaults = array(
238
			'value'        => false,
239
			'unique'       => false,
240
			'stripslashes' => true,
241
			'is_draft'     => false,
242
		);
243
		$args     = wp_parse_args( $args, $defaults );
244
245
		$query = array();
246
		self::meta_field_query( $field_id, $order, $limit, $args, $query );
247
		$query = implode( ' ', $query );
248
249
		$cache_key = 'entry_metas_for_field_' . $field_id . $order . $limit . FrmAppHelper::maybe_json_encode( $args );
250
		$values    = FrmDb::check_cache( $cache_key, 'frm_entry', $query, 'get_col' );
251
252
		if ( ! $args['stripslashes'] ) {
253
			return $values;
254
		}
255
256
		foreach ( $values as $k => $v ) {
257
			FrmAppHelper::unserialize_or_decode( $v );
258
			$values[ $k ] = $v;
259
			unset( $k, $v );
260
		}
261
262
		return wp_unslash( $values );
263
	}
264
265
	/**
266
	 * @param string $order
267
	 * @param string $limit
268
	 */
269
	private static function meta_field_query( $field_id, $order, $limit, $args, array &$query ) {
270
		global $wpdb;
271
		$query[] = 'SELECT';
272
		$query[] = $args['unique'] ? 'DISTINCT(em.meta_value)' : 'em.meta_value';
273
		$query[] = 'FROM ' . $wpdb->prefix . 'frm_item_metas em ';
274
275
		if ( ! $args['is_draft'] ) {
276
			$query[] = 'INNER JOIN ' . $wpdb->prefix . 'frm_items e ON (e.id=em.item_id)';
277
		}
278
279 View Code Duplication
		if ( is_numeric( $field_id ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
280
			$query[] = $wpdb->prepare( 'WHERE em.field_id=%d', $field_id );
281
		} else {
282
			$query[] = $wpdb->prepare( 'LEFT JOIN ' . $wpdb->prefix . 'frm_fields fi ON (em.field_id = fi.id) WHERE fi.field_key=%s', $field_id );
283
		}
284
285
		if ( ! $args['is_draft'] ) {
286
			$query[] = 'AND e.is_draft=0';
287
		}
288
289
		if ( $args['value'] ) {
290
			$query[] = $wpdb->prepare( ' AND meta_value=%s', $args['value'] );
291
		}
292
		$query[] = $order . $limit;
293
	}
294
295
	public static function get_entry_meta_info( $entry_id ) {
296
		return FrmDb::get_results( 'frm_item_metas', array( 'item_id' => $entry_id ) );
297
	}
298
299
	public static function getAll( $where = array(), $order_by = '', $limit = '', $stripslashes = false ) {
300
		global $wpdb;
301
		$query = 'SELECT it.*, fi.type as field_type, fi.field_key as field_key,
302
            fi.required as required, fi.form_id as field_form_id, fi.name as field_name, fi.options as fi_options
303
			FROM ' . $wpdb->prefix . 'frm_item_metas it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_fields fi ON it.field_id=fi.id' .
304
			FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
305
306
		$cache_key = 'all_' . FrmAppHelper::maybe_json_encode( $where ) . $order_by . $limit;
307
		$results   = FrmDb::check_cache( $cache_key, 'frm_entry', $query, ( $limit == ' LIMIT 1' ? 'get_row' : 'get_results' ) );
308
309
		if ( ! $results || ! $stripslashes ) {
310
			return $results;
311
		}
312
313
		foreach ( $results as $k => $result ) {
314
			FrmAppHelper::unserialize_or_decode( $result->meta_value );
315
			$results[ $k ]->meta_value = wp_unslash( $result->meta_value );
316
			unset( $k, $result );
317
		}
318
319
		return $results;
320
	}
321
322
	public static function getEntryIds( $where = array(), $order_by = '', $limit = '', $unique = true, $args = array() ) {
323
		$defaults = array(
324
			'is_draft' => false,
325
			'user_id'  => '',
326
			'group_by' => '',
327
		);
328
		$args     = wp_parse_args( $args, $defaults );
329
330
		$query = array();
331
		self::get_ids_query( $where, $order_by, $limit, $unique, $args, $query );
332
		$query = implode( ' ', $query );
333
334
		$cache_key = 'ids_' . FrmAppHelper::maybe_json_encode( $where ) . $order_by . 'l' . $limit . 'u' . $unique . FrmAppHelper::maybe_json_encode( $args );
335
		$type      = 'get_' . ( ' LIMIT 1' === $limit ? 'var' : 'col' );
336
		return FrmDb::check_cache( $cache_key, 'frm_entry', $query, $type );
337
	}
338
339
	/**
340
	 * Given a query including a form id and its child form ids, output an array of matching entry ids
341
	 * If a child entry id is matched, its parent will be returned in its place
342
	 *
343
	 * @param array $query
344
	 * @param array $args
345
	 * @return array
346
	 */
347
	public static function get_top_level_entry_ids( $query, $args ) {
348
		$args['return_parent_id_if_0_return_id'] = true;
349
		return self::getEntryIds( $query, '', '', true, $args );
350
	}
351
352
	/**
353
	 * @param string|array $where
354
	 * @param string $order_by
355
	 * @param string $limit
356
	 */
357
	private static function get_ids_query( $where, $order_by, $limit, $unique, $args, array &$query ) {
358
		global $wpdb;
359
		$query[]  = 'SELECT';
360
		$defaults = array(
361
			'return_parent_id'                => false,
362
			'return_parent_id_if_0_return_id' => false,
363
		);
364
		$args     = array_merge( $defaults, $args );
365
366
		if ( $unique ) {
367
			$query[] = 'DISTINCT';
368
		}
369
370
		if ( $args['return_parent_id_if_0_return_id'] ) {
371
			$query[] = 'IF ( e.parent_item_id = 0, it.item_id, e.parent_item_id )';
372
		} elseif ( $args['return_parent_id'] ) {
373
			$query[] = 'e.parent_item_id';
374
		} else {
375
			$query[] = 'it.item_id';
376
		}
377
378
		$query[] = 'FROM ' . $wpdb->prefix . 'frm_item_metas it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_fields fi ON it.field_id=fi.id';
379
380
		$query[] = 'INNER JOIN ' . $wpdb->prefix . 'frm_items e ON (e.id=it.item_id)';
381
		if ( is_array( $where ) ) {
382
			if ( ! $args['is_draft'] ) {
383
				$where['e.is_draft'] = 0;
384
			} elseif ( $args['is_draft'] == 1 ) {
385
				$where['e.is_draft'] = 1;
386
			}
387
388
			if ( ! empty( $args['user_id'] ) ) {
389
				$where['e.user_id'] = $args['user_id'];
390
			}
391
			$query[] = FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
392
393
			if ( $args['group_by'] ) {
394
				$query[] = ' GROUP BY ' . sanitize_text_field( $args['group_by'] );
395
			}
396
397
			return;
398
		}
399
400
		$draft_where = '';
401
		$user_where  = '';
402
		if ( ! $args['is_draft'] ) {
403
			$draft_where = $wpdb->prepare( ' AND e.is_draft=%d', 0 );
404
		} elseif ( $args['is_draft'] == 1 ) {
405
			$draft_where = $wpdb->prepare( ' AND e.is_draft=%d', 1 );
406
		}
407
408
		if ( ! empty( $args['user_id'] ) ) {
409
			$user_where = $wpdb->prepare( ' AND e.user_id=%d', $args['user_id'] );
410
		}
411
412
		if ( strpos( $where, ' GROUP BY ' ) ) {
413
			// don't inject WHERE filtering after GROUP BY
414
			$parts  = explode( ' GROUP BY ', $where );
415
			$where  = $parts[0];
416
			$where .= $draft_where . $user_where;
417
			$where .= ' GROUP BY ' . $parts[1];
418
		} else {
419
			$where .= $draft_where . $user_where;
420
		}
421
422
		// The query has already been prepared
423
		$query[] = FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
424
	}
425
426
	public static function search_entry_metas( $search, $field_id = '', $operator ) {
427
		$cache_key = 'search_' . FrmAppHelper::maybe_json_encode( $search ) . $field_id . $operator;
428
		$results   = wp_cache_get( $cache_key, 'frm_entry' );
429
		if ( false !== $results ) {
430
			return $results;
431
		}
432
433
		global $wpdb;
434
		if ( is_array( $search ) ) {
435
			$where = '';
436
			foreach ( $search as $field => $value ) {
437
				if ( $value <= 0 || ! in_array( $field, array( 'year', 'month', 'day' ) ) ) {
438
					continue;
439
				}
440
441
				switch ( $field ) {
442
					case 'year':
443
						$value = '%' . $value;
444
						break;
445
					case 'month':
446
						$value .= '%';
447
						break;
448
					case 'day':
449
						$value = '%' . $value . '%';
450
				}
451
				$where .= $wpdb->prepare( ' meta_value ' . $operator . ' %s and', $value ); // WPCS: unprepared SQL ok.
452
			}
453
			$where .= $wpdb->prepare( ' field_id=%d', $field_id );
454
			$query = 'SELECT DISTINCT item_id FROM ' . $wpdb->prefix . 'frm_item_metas' . FrmDb::prepend_and_or_where( ' WHERE ', $where );
455
		} else {
456
			if ( $operator == 'LIKE' ) {
457
				$search = '%' . $search . '%';
458
			}
459
			$query = $wpdb->prepare( "SELECT DISTINCT item_id FROM {$wpdb->prefix}frm_item_metas WHERE meta_value {$operator} %s and field_id = %d", $search, $field_id ); // WPCS: unprepared SQL ok.
460
		}
461
462
		$results = $wpdb->get_col( $query, 0 ); // WPCS: unprepared SQL ok.
463
		FrmDb::set_cache( $cache_key, $results, 'frm_entry' );
464
465
		return $results;
466
	}
467
}
468