WC_Data   B
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 334
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 334
rs 8.6
c 0
b 0
f 0
wmc 37
lcom 1
cbo 1

19 Methods

Rating   Name   Duplication   Size   Complexity  
get_id() 0 1 ?
create() 0 1 ?
read() 0 1 ?
update() 0 1 ?
delete() 0 1 ?
save() 0 1 ?
A __toString() 0 3 1
A get_data() 0 3 1
A get_meta_data() 0 3 1
A prefix_key() 0 3 2
A get_internal_meta_keys() 0 3 1
A get_meta() 0 14 3
B set_meta_data() 0 14 5
A add_meta_data() 0 10 2
A update_meta_data() 0 15 3
A delete_meta_data() 0 4 1
C read_meta_data() 0 43 8
B save_meta_data() 0 33 5
B _get_db_info() 0 29 4
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
* Abstract WC Data Class
8
*
9
* Implemented by classes using the same CRUD(s) pattern.
10
*
11
* @version  2.6.0
12
* @package  WooCommerce/Abstracts
13
* @category Abstract Class
14
* @author   WooThemes
15
*/
16
abstract class WC_Data {
17
18
	/**
19
	 * Core data for this object, name value pairs (name + default value).
20
	 * @var array
21
	 */
22
	protected $_data = array();
23
24
	/**
25
	 * Stores meta in cache for future reads.
26
	 * A group must be set to to enable caching.
27
	 * @var string
28
	 */
29
	protected $_cache_group = '';
30
31
	/**
32
	 * Meta type. This should match up with
33
	 * the types avaiable at https://codex.wordpress.org/Function_Reference/add_metadata.
34
	 * WP defines 'post', 'user', 'comment', and 'term'.
35
	 */
36
	protected $_meta_type = 'post';
37
38
	/**
39
	 * This only needs set if you are using a custom metadata type (for example payment tokens.
40
	 * This should be the name of the field your table uses for associating meta with objects.
41
	 * For example, in payment_tokenmeta, this would be payment_token_id.
42
	 * @var string
43
	 */
44
	protected $object_id_field_for_meta = '';
45
46
	/**
47
	 * Stores additonal meta data.
48
	 * @var array
49
	 */
50
	protected $_meta_data = array();
51
52
	/**
53
	 * Internal meta keys we don't want exposed for the object.
54
	 * @var array
55
	 */
56
	protected $_internal_meta_keys = array();
57
58
	/**
59
	 * Returns the unique ID for this object.
60
	 * @return int
61
	 */
62
	abstract public function get_id();
63
64
	/**
65
	 * Creates new object in the database.
66
	 */
67
	abstract public function create();
68
69
	/**
70
	 * Read object from the database.
71
	 * @param int ID of the object to load.
72
	 */
73
	abstract public function read( $id );
74
75
	/**
76
	 * Updates object data in the database.
77
	 */
78
	abstract public function update();
79
80
	/**
81
	 * Updates object data in the database.
82
	 */
83
	abstract public function delete();
84
85
	/**
86
	 * Save should create or update based on object existance.
87
	 */
88
	abstract public function save();
89
90
	/**
91
	 * Change data to JSON format.
92
	 * @return string Data in JSON format.
93
	 */
94
	public function __toString() {
95
		return json_encode( $this->get_data() );
96
	}
97
98
	/**
99
	 * Returns all data for this object.
100
	 * @return array
101
	 */
102
	public function get_data() {
103
		return array_merge( $this->_data, array( 'meta_data' => $this->get_meta_data() ) );
104
	}
105
106
	/**
107
	 * Get All Meta Data
108
	 * @since 2.6.0
109
	 * @return array
110
	 */
111
	public function get_meta_data() {
112
		return $this->_meta_data;
113
	}
114
115
	/**
116
	 * Internal meta keys we don't want exposed as part of meta_data. This is in
117
	 * addition to all data props with _ prefix.
118
	 * @since 2.6.0
119
	 * @return array()
0 ignored issues
show
Documentation introduced by
The doc-type array() could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
120
	 */
121
	protected function prefix_key( $key ) {
122
		return '_' === substr( $key, 0, 1 ) ? $key : '_' . $key;
123
	}
124
125
	/**
126
	 * Internal meta keys we don't want exposed as part of meta_data. This is in
127
	 * addition to all data props with _ prefix.
128
	 * @since 2.6.0
129
	 * @return array()
0 ignored issues
show
Documentation introduced by
The doc-type array() could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
130
	 */
131
	protected function get_internal_meta_keys() {
132
		return array_merge( array_map( array( $this, 'prefix_key' ), array_keys( $this->_data ) ), $this->_internal_meta_keys );
133
	}
134
135
	/**
136
	 * Get Meta Data by Key.
137
	 * @since 2.6.0
138
	 * @param  string $key
139
	 * @param  bool $single return first found meta with key, or all with $key
140
	 * @return mixed
141
	 */
142
	public function get_meta( $key = '', $single = true ) {
143
		$array_keys = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
144
		$value    = '';
145
146
		if ( ! empty( $array_keys ) ) {
147
			if ( $single ) {
148
				$value = $this->_meta_data[ current( $array_keys ) ]->value;
149
			} else {
150
				$value = array_intersect_key( $this->_meta_data, array_flip( $array_keys ) );
151
			}
152
		}
153
154
		return $value;
155
	}
156
157
	/**
158
	 * Set all meta data from array.
159
	 * @since 2.6.0
160
	 * @param array $data Key/Value pairs
161
	 */
162
	public function set_meta_data( $data ) {
163
		if ( ! empty( $data ) && is_array( $data ) ) {
164
			foreach ( $data as $meta ) {
165
				$meta = (array) $meta;
166
				if ( isset( $meta['key'], $meta['value'], $meta['meta_id'] ) ) {
167
					$this->_meta_data[] = (object) array(
168
						'key'     => $meta['key'],
169
						'value'   => $meta['value'],
170
						'meta_id' => $meta['meta_id'],
171
					);
172
				}
173
			}
174
		}
175
	}
176
177
	/**
178
	 * Add meta data.
179
	 * @since 2.6.0
180
	 * @param array $key Meta key
181
	 * @param array $value Meta value
182
	 * @param array $unique Should this be a unique key?
183
	 */
184
	public function add_meta_data( $key, $value, $unique = false ) {
185
		if ( $unique ) {
186
			$array_keys       = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
187
			$this->_meta_data = array_diff_key( $this->_meta_data, array_fill_keys( $array_keys, '' ) );
188
		}
189
		$this->_meta_data[] = (object) array(
190
			'key'   => $key,
191
			'value' => $value,
192
		);
193
	}
194
195
	/**
196
	 * Update meta data by key or ID, if provided.
197
	 * @since 2.6.0
198
	 * @param  string $key
199
	 * @param  string $value
200
	 * @param  int $meta_id
201
	 */
202
	public function update_meta_data( $key, $value, $meta_id = '' ) {
203
		$array_key = '';
204
		if ( $meta_id ) {
205
			$array_key = array_keys( wp_list_pluck( $this->_meta_data, 'meta_id' ), $meta_id );
206
		}
207
		if ( $array_key ) {
208
			$this->_meta_data[ current( $array_key ) ] = (object) array(
209
				'key'     => $key,
210
				'value'   => $value,
211
				'meta_id' => $meta_id,
212
			);
213
		} else {
214
			$this->add_meta_data( $key, $value, true );
0 ignored issues
show
Documentation introduced by
$key is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$value is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
true is of type boolean, but the function expects a false|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
215
		}
216
	}
217
218
	/**
219
	 * Delete meta data.
220
	 * @since 2.6.0
221
	 * @param array $key Meta key
222
	 */
223
	public function delete_meta_data( $key ) {
224
		$array_keys         = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
225
		$this->_meta_data   = array_diff_key( $this->_meta_data, array_fill_keys( $array_keys, '' ) );
226
	}
227
228
	/**
229
	 * Read Meta Data from the database. Ignore any internal properties.
230
	 * @since 2.6.0
231
	 */
232
	protected function read_meta_data() {
233
		$this->_meta_data = array();
234
		$cache_loaded     = false;
235
236
		if ( ! $this->get_id() ) {
237
			return;
238
		}
239
240
		if ( ! empty ( $this->_cache_group ) ) {
241
			$cache_key   = WC_Cache_Helper::get_cache_prefix( $this->_cache_group ) . $this->get_id();
242
			$cached_meta = wp_cache_get( $cache_key, $this->_cache_group );
243
244
			if ( false !== $cached_meta ) {
245
				$this->_meta_data = $cached_meta;
246
				$cache_loaded = true;
247
			}
248
		}
249
250
		if ( ! $cache_loaded ) {
251
			global $wpdb;
252
			$db_info = $this->_get_db_info();
253
			$raw_meta_data = $wpdb->get_results( $wpdb->prepare( "
254
				SELECT " . $db_info['meta_id_field'] . ", meta_key, meta_value
255
				FROM " . $db_info['table'] . "
256
				WHERE " . $db_info['object_id_field'] . " = %d ORDER BY " . $db_info['meta_id_field'] . "
257
			", $this->get_id() ) );
258
259
			foreach ( $raw_meta_data as $meta ) {
260
				if ( in_array( $meta->meta_key, $this->get_internal_meta_keys() ) ) {
261
					continue;
262
				}
263
				$this->_meta_data[] = (object) array(
264
					'key'     => $meta->meta_key,
265
					'value'   => $meta->meta_value,
266
					'meta_id' => $meta->{ $db_info['meta_id_field'] },
267
				);
268
			}
269
270
			if ( ! empty ( $this->_cache_group ) ) {
271
				wp_cache_set( $cache_key, $this->_meta_data, $this->_cache_group );
272
			}
273
		}
274
	}
275
276
	/**
277
	 * Update Meta Data in the database.
278
	 * @since 2.6.0
279
	 */
280
	protected function save_meta_data() {
281
		global $wpdb;
282
		$db_info = $this->_get_db_info();
283
		$all_meta_ids = array_map( 'absint', $wpdb->get_col( $wpdb->prepare( "
284
			SELECT " . $db_info['meta_id_field'] . " FROM " . $db_info['table'] . "
285
			WHERE " . $db_info['object_id_field'] . " = %d", $this->get_id() ) . "
286
			AND meta_key NOT IN ('" . implode( "','", array_map( 'esc_sql', $this->get_internal_meta_keys() ) ) . "');
287
		" ) );
288
		$set_meta_ids = array();
289
290
		foreach ( $this->_meta_data as $array_key => $meta ) {
291
			if ( empty( $meta->meta_id ) ) {
292
				$new_meta_id    = add_metadata( $this->_meta_type, $this->get_id(), $meta->key, $meta->value, false );
293
				$set_meta_ids[] = $new_meta_id;
294
				$this->_meta_data[ $array_key ]->meta_id = $new_meta_id;
295
			} else {
296
				update_metadata_by_mid( $this->_meta_type, $meta->meta_id, $meta->value, $meta->key );
297
				$set_meta_ids[] = absint( $meta->meta_id );
298
			}
299
		}
300
301
		// Delete no longer set meta data
302
		$delete_meta_ids = array_diff( $all_meta_ids, $set_meta_ids );
303
304
		foreach ( $delete_meta_ids as $meta_id ) {
305
			delete_metadata_by_mid( $this->_meta_type, $meta_id );
306
		}
307
308
		if ( ! empty ( $this->_cache_group ) ) {
309
			WC_Cache_Helper::incr_cache_prefix( $this->_cache_group );
310
		}
311
		$this->read_meta_data();
312
	}
313
314
	/**
315
	 * Table structure is slightly different between meta types, this function will return what we need to know.
316
	 * @since 2.6.0
317
	 * @return array Array elements: table, object_id_field, meta_id_field
318
	 */
319
	protected function _get_db_info() {
320
		global $wpdb;
321
322
		$meta_id_field   = 'meta_id'; // for some reason users calls this umeta_id so we need to track this as well.
323
		$table           = $wpdb->prefix;
324
325
		// If we are dealing with a type of metadata that is not a core type, the table should be prefixed.
326
		if ( ! in_array( $this->_meta_type, array( 'post', 'user', 'comment', 'term' ) ) ) {
327
			$table .= 'woocommerce_';
328
		}
329
330
		$table .= $this->_meta_type . 'meta';
331
		$object_id_field = $this->_meta_type . '_id';
332
333
		// Figure out our field names.
334
		if ( 'user' === $this->_meta_type ) {
335
			$meta_id_field   = 'umeta_id';
336
		}
337
338
		if ( ! empty( $this->object_id_field_for_meta ) ) {
339
			$object_id_field = $this->object_id_field_for_meta;
340
		}
341
342
		return array(
343
			'table'           => $table,
344
			'object_id_field' => $object_id_field,
345
			'meta_id_field'   => $meta_id_field,
346
		);
347
	}
348
349
}
350