GetPaid_Data_Store_WP::update_meta()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Shared logic for WP based data stores.
4
 *
5
 * @version 1.0.19
6
 */
7
8
defined( 'ABSPATH' ) || exit;
9
10
/**
11
 * GetPaid_Data_Store_WP class.
12
 *
13
 * Datastores that extend this class use CPTs to store data.
14
 */
15
class GetPaid_Data_Store_WP {
16
17
	/**
18
	 * Meta type. This should match up with
19
	 * the types available at https://developer.wordpress.org/reference/functions/add_metadata/.
20
	 * WP defines 'post', 'user', 'comment', and 'term'.
21
	 *
22
	 * @var string
23
	 */
24
	protected $meta_type = 'post';
25
26
	/**
27
	 * This only needs set if you are using a custom metadata type.
28
	 *
29
	 * @var string
30
	 */
31
	protected $object_id_field_for_meta = '';
32
33
	/**
34
	 * Data stored in meta keys, but not considered "meta" for an object.
35
	 *
36
	 * @since 1.0.19
37
	 *
38
	 * @var array
39
	 */
40
	protected $internal_meta_keys = array();
41
42
	/**
43
	 * Meta data which should exist in the DB, even if empty.
44
	 *
45
	 * @since 1.0.19
46
	 *
47
	 * @var array
48
	 */
49
	protected $must_exist_meta_keys = array();
50
51
	/**
52
	 * A map of meta keys to data props.
53
	 *
54
	 * @since 1.0.19
55
	 *
56
	 * @var array
57
	 */
58
	protected $meta_key_to_props = array();
59
60
	/**
61
	 * Returns an array of meta for an object.
62
	 *
63
	 * @since  1.0.19
64
	 * @param  GetPaid_Data $object GetPaid_Data object.
65
	 * @return array
66
	 */
67
	public function read_meta( &$object ) {
68
		global $wpdb;
69
		$db_info       = $this->get_db_info();
70
		$raw_meta_data = $wpdb->get_results(
71
			$wpdb->prepare(
72
				"SELECT {$db_info['meta_id_field']} as meta_id, meta_key, meta_value
73
				FROM {$db_info['table']}
74
				WHERE {$db_info['object_id_field']} = %d
75
				ORDER BY {$db_info['meta_id_field']}",
76
				$object->get_id()
77
			)
78
		);
79
80
		$this->internal_meta_keys = array_merge( array_map( array( $this, 'prefix_key' ), $object->get_data_keys() ), $this->internal_meta_keys );
81
		$meta_data                = array_filter( $raw_meta_data, array( $this, 'exclude_internal_meta_keys' ) );
82
		return apply_filters( "getpaid_data_store_wp_{$this->meta_type}_read_meta", $meta_data, $object, $this );
83
	}
84
85
	/**
86
	 * Deletes meta based on meta ID.
87
	 *
88
	 * @since  1.0.19
89
	 * @param  GetPaid_Data  $object GetPaid_Data object.
90
	 * @param  stdClass $meta (containing at least ->id).
91
	 */
92
	public function delete_meta( &$object, $meta ) {
93
		delete_metadata_by_mid( $this->meta_type, $meta->id );
94
	}
95
96
	/**
97
	 * Add new piece of meta.
98
	 *
99
	 * @since  1.0.19
100
	 * @param  GetPaid_Data  $object GetPaid_Data object.
101
	 * @param  stdClass $meta (containing ->key and ->value).
102
	 * @return int meta ID
103
	 */
104
	public function add_meta( &$object, $meta ) {
105
		return add_metadata( $this->meta_type, $object->get_id(), $meta->key, is_string( $meta->value ) ? wp_slash( $meta->value ) : $meta->value, false );
0 ignored issues
show
Bug Best Practice introduced by
The expression return add_metadata($thi... : $meta->value, false) could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
106
	}
107
108
	/**
109
	 * Update meta.
110
	 *
111
	 * @since  1.0.19
112
	 * @param  GetPaid_Data  $object GetPaid_Data object.
113
	 * @param  stdClass $meta (containing ->id, ->key and ->value).
114
	 */
115
	public function update_meta( &$object, $meta ) {
116
		update_metadata_by_mid( $this->meta_type, $meta->id, $meta->value, $meta->key );
117
	}
118
119
	/**
120
	 * Table structure is slightly different between meta types, this function will return what we need to know.
121
	 *
122
	 * @since  1.0.19
123
	 * @return array Array elements: table, object_id_field, meta_id_field
124
	 */
125
	protected function get_db_info() {
126
		global $wpdb;
127
128
		$meta_id_field = 'meta_id'; // users table calls this umeta_id so we need to track this as well.
129
		$table         = $wpdb->prefix;
130
131
		// If we are dealing with a type of metadata that is not a core type, the table should be prefixed.
132
		if ( ! in_array( $this->meta_type, array( 'post', 'user', 'comment', 'term' ), true ) ) {
133
			$table .= 'getpaid_';
134
		}
135
136
		$table          .= $this->meta_type . 'meta';
137
		$object_id_field = $this->meta_type . '_id';
138
139
		// Figure out our field names.
140
		if ( 'user' === $this->meta_type ) {
141
			$meta_id_field = 'umeta_id';
142
			$table         = $wpdb->usermeta;
143
		}
144
145
		if ( ! empty( $this->object_id_field_for_meta ) ) {
146
			$object_id_field = $this->object_id_field_for_meta;
147
		}
148
149
		return array(
150
			'table'           => $table,
151
			'object_id_field' => $object_id_field,
152
			'meta_id_field'   => $meta_id_field,
153
		);
154
	}
155
156
	/**
157
	 * Internal meta keys we don't want exposed as part of meta_data. This is in
158
	 * addition to all data props with _ prefix.
159
	 *
160
	 * @since 1.0.19
161
	 *
162
	 * @param string $key Prefix to be added to meta keys.
163
	 * @return string
164
	 */
165
	protected function prefix_key( $key ) {
166
		return '_' === substr( $key, 0, 1 ) ? $key : '_' . $key;
167
	}
168
169
	/**
170
	 * Callback to remove unwanted meta data.
171
	 *
172
	 * @param object $meta Meta object to check if it should be excluded or not.
173
	 * @return bool
174
	 */
175
	protected function exclude_internal_meta_keys( $meta ) {
176
		return ! in_array( $meta->meta_key, $this->internal_meta_keys, true ) && 0 !== stripos( $meta->meta_key, 'wp_' );
177
	}
178
179
	/**
180
	 * Gets a list of props and meta keys that need updated based on change state
181
	 * or if they are present in the database or not.
182
	 *
183
	 * @param  GetPaid_Data $object         The GetPaid_Data object.
184
	 * @param  array   $meta_key_to_props   A mapping of meta keys => prop names.
185
	 * @param  string  $meta_type           The internal WP meta type (post, user, etc).
186
	 * @return array                        A mapping of meta keys => prop names, filtered by ones that should be updated.
187
	 */
188
	protected function get_props_to_update( $object, $meta_key_to_props, $meta_type = 'post' ) {
189
		$props_to_update = array();
190
		$changed_props   = $object->get_changes();
191
192
		// Props should be updated if they are a part of the $changed array or don't exist yet.
193
		foreach ( $meta_key_to_props as $meta_key => $prop ) {
194
			if ( array_key_exists( $prop, $changed_props ) || ! metadata_exists( $meta_type, $object->get_id(), $meta_key ) ) {
195
				$props_to_update[ $meta_key ] = $prop;
196
			}
197
		}
198
199
		return $props_to_update;
200
	}
201
202
	/**
203
	 * Read object data.
204
	 *
205
	 * @param GetPaid_Data $object GetPaid_Data object.
206
	 * @param WP_Post   $post_object Post object.
207
	 * @since 1.0.19
208
	 */
209
	protected function read_object_data( &$object, $post_object ) {
210
		$id    = $object->get_id();
211
		$props = array();
212
213
		foreach ( $this->meta_key_to_props as $meta_key => $prop ) {
214
			$props[ $prop ] = get_post_meta( $id, $meta_key, true );
215
		}
216
217
		// Set object properties.
218
		$object->set_props( $props );
219
220
		// Gets extra data associated with the object if needed.
221
		foreach ( $object->get_extra_data_keys() as $key ) {
222
			$function = 'set_' . $key;
223
			if ( is_callable( array( $object, $function ) ) ) {
224
				$object->{$function}( get_post_meta( $object->get_id(), $key, true ) );
225
			}
226
		}
227
	}
228
229
	/**
230
	 * Helper method that updates all the post meta for an object based on it's settings in the GetPaid_Data class.
231
	 *
232
	 * @param GetPaid_Data $object GetPaid_Data object.
233
	 * @since 1.0.19
234
	 */
235
	protected function update_post_meta( &$object ) {
236
237
		$updated_props   = array();
238
		$props_to_update = $this->get_props_to_update( $object, $this->meta_key_to_props );
239
		$object_type     = $object->get_object_type();
240
241
		foreach ( $props_to_update as $meta_key => $prop ) {
242
			$value = $object->{"get_$prop"}( 'edit' );
243
			$value = is_string( $value ) ? wp_slash( $value ) : $value;
244
245
			$updated = $this->update_or_delete_post_meta( $object, $meta_key, $value );
246
247
			if ( $updated ) {
248
				$updated_props[] = $prop;
249
			}
250
		}
251
252
		do_action( "getpaid_{$object_type}_object_updated_props", $object, $updated_props );
253
	}
254
255
	/**
256
	 * Update meta data in, or delete it from, the database.
257
	 *
258
	 * Avoids storing meta when it's either an empty string or empty array or null.
259
	 * Other empty values such as numeric 0 should still be stored.
260
	 * Data-stores can force meta to exist using `must_exist_meta_keys`.
261
	 *
262
	 * Note: WordPress `get_metadata` function returns an empty string when meta data does not exist.
263
	 *
264
	 * @param GetPaid_Data $object The GetPaid_Data object.
265
	 * @param string  $meta_key Meta key to update.
266
	 * @param mixed   $meta_value Value to save.
267
	 *
268
	 * @since 1.0.19 Added to prevent empty meta being stored unless required.
269
	 *
270
	 * @return bool True if updated/deleted.
271
	 */
272
	protected function update_or_delete_post_meta( $object, $meta_key, $meta_value ) {
273
		if ( in_array( $meta_value, array( array(), '', null ), true ) && ! in_array( $meta_key, $this->must_exist_meta_keys, true ) ) {
274
			$updated = delete_post_meta( $object->get_id(), $meta_key );
275
		} else {
276
			$updated = update_post_meta( $object->get_id(), $meta_key, $meta_value );
277
		}
278
279
		return (bool) $updated;
280
	}
281
282
	/**
283
	 * Return list of internal meta keys.
284
	 *
285
	 * @since 1.0.19
286
	 * @return array
287
	 */
288
	public function get_internal_meta_keys() {
289
		return $this->internal_meta_keys;
290
	}
291
292
	/**
293
	 * Clear any caches.
294
	 *
295
	 * @param GetPaid_Data $object GetPaid_Data object.
296
	 * @since 1.0.19
297
	 */
298
	protected function clear_caches( &$object ) {
299
		clean_post_cache( $object->get_id() );
300
	}
301
302
	/**
303
	 * Method to delete a data object from the database.
304
	 *
305
	 * @param GetPaid_Data $object GetPaid_Data object.
306
	 * @param array    $args Array of args to pass to the delete method.
307
	 *
308
	 * @return void
309
	 */
310
	public function delete( &$object, $args = array() ) {
311
		$id          = $object->get_id();
312
		$object_type = $object->get_object_type();
313
314
		if ( 'invoice' == $object_type ) {
315
			$object_type = $object->get_type();
0 ignored issues
show
Bug introduced by
The method get_type() does not exist on GetPaid_Data. It seems like you code against a sub-type of GetPaid_Data such as WPInv_Invoice or WPInv_Item or WPInv_Discount. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

315
			/** @scrutinizer ignore-call */ 
316
   $object_type = $object->get_type();
Loading history...
316
		}
317
318
		$args        = wp_parse_args(
319
			$args,
320
			array(
321
				'force_delete' => false,
322
			)
323
		);
324
325
		if ( ! $id ) {
326
			return;
327
		}
328
329
		if ( $args['force_delete'] ) {
330
			do_action( "getpaid_delete_$object_type", $object );
331
			wp_delete_post( $id, true );
332
			$object->set_id( 0 );
333
		} else {
334
			do_action( "getpaid_trash_$object_type", $object );
335
			wp_trash_post( $id );
336
			$object->set_status( 'trash' );
337
		}
338
	}
339
340
	/**
341
	 * Get the status to save to the post object.
342
	 *
343
	 *
344
	 * @since 1.0.19
345
	 * @param  GetPaid_Data $object GetPaid_Data object.
346
	 * @return string
347
	 */
348
	protected function get_post_status( $object ) {
349
		$object_status = $object->get_status( 'edit' );
350
		$object_type   = $object->get_object_type();
351
352
		if ( ! $object_status ) {
353
			$object_status = apply_filters( "getpaid_default_{$object_type}_status", 'draft' );
354
		}
355
356
		return $object_status;
357
	}
358
359
}
360