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
|
|
|
* Stores meta in cache for future reads. |
20
|
|
|
* A group must be set to to enable caching. |
21
|
|
|
* @var string |
22
|
|
|
*/ |
23
|
|
|
protected $_cache_group = ''; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Meta type. This should match up with |
27
|
|
|
* the types avaiable at https://codex.wordpress.org/Function_Reference/add_metadata. |
28
|
|
|
* WP defines 'post', 'user', 'comment', and 'term'. |
29
|
|
|
*/ |
30
|
|
|
protected $_meta_type = 'post'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* This only needs set if you are using a custom metadata type (for example payment tokens. |
34
|
|
|
* This should be the name of the field your table uses for associating meta with objects. |
35
|
|
|
* For example, in payment_tokenmeta, this would be payment_token_id. |
36
|
|
|
* @var string |
37
|
|
|
*/ |
38
|
|
|
protected $object_id_field_for_meta = ''; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Stores additonal meta data. |
42
|
|
|
* @var array |
43
|
|
|
*/ |
44
|
|
|
protected $_meta_data = array(); |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Internal meta keys we don't want exposed for the object. |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
protected $_internal_meta_keys = array(); |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Returns the unique ID for this object. |
54
|
|
|
* @return int |
55
|
|
|
*/ |
56
|
|
|
abstract public function get_id(); |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Returns all data for this object. |
60
|
|
|
* @return array |
61
|
|
|
*/ |
62
|
|
|
abstract public function get_data(); |
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
|
|
|
* Get All Meta Data |
92
|
|
|
* @since 2.6.0 |
93
|
|
|
* @return array |
94
|
|
|
*/ |
95
|
|
|
public function get_meta_data() { |
96
|
|
|
return $this->_meta_data; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Internal meta keys we don't want exposed as part of meta_data. This is in |
101
|
|
|
* addition to all data props with _ prefix. |
102
|
|
|
* @since 2.6.0 |
103
|
|
|
* @return array() |
|
|
|
|
104
|
|
|
*/ |
105
|
|
|
protected function prefix_key( $key ) { |
106
|
|
|
return '_' === substr( $key, 0, 1 ) ? $key : '_' . $key; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Internal meta keys we don't want exposed as part of meta_data. This is in |
111
|
|
|
* addition to all data props with _ prefix. |
112
|
|
|
* @since 2.6.0 |
113
|
|
|
* @return array() |
|
|
|
|
114
|
|
|
*/ |
115
|
|
|
protected function get_internal_meta_keys() { |
116
|
|
|
return array_merge( array_map( array( $this, 'prefix_key' ), array_keys( $this->_data ) ), $this->_internal_meta_keys ); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Get Meta Data by Key. |
121
|
|
|
* @since 2.6.0 |
122
|
|
|
* @param string $key |
123
|
|
|
* @param bool $single return first found meta with key, or all with $key |
124
|
|
|
* @return mixed |
125
|
|
|
*/ |
126
|
|
|
public function get_meta( $key = '', $single = true ) { |
127
|
|
|
$meta_ids = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key ); |
128
|
|
|
$value = ''; |
129
|
|
|
|
130
|
|
|
if ( $meta_ids ) { |
|
|
|
|
131
|
|
|
if ( $single ) { |
132
|
|
|
$value = $this->_meta_data[ current( $meta_ids ) ]->value; |
133
|
|
|
} else { |
134
|
|
|
$value = array_intersect_key( $this->_meta_data, array_flip( $meta_ids ) ); |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
return $value; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Set all meta data from array. |
143
|
|
|
* @since 2.6.0 |
144
|
|
|
* @param array $data Key/Value pairs |
145
|
|
|
*/ |
146
|
|
|
public function set_meta_data( $data ) { |
147
|
|
|
if ( ! empty( $data ) && is_array( $data ) ) { |
148
|
|
|
foreach ( $data as $meta_id => $meta ) { |
149
|
|
|
$meta = (array) $meta; |
150
|
|
|
if ( isset( $meta['key'], $meta['value'] ) ) { |
151
|
|
|
$this->_meta_data[ $meta_id ] = (object) array( |
152
|
|
|
'key' => $meta['key'], |
153
|
|
|
'value' => $meta['value'], |
154
|
|
|
); |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Add meta data. |
162
|
|
|
* @since 2.6.0 |
163
|
|
|
* @param array $key Meta key |
164
|
|
|
* @param array $value Meta value |
165
|
|
|
* @param array $unique Should this be a unique key? |
166
|
|
|
*/ |
167
|
|
|
public function add_meta_data( $key, $value, $unique = false ) { |
168
|
|
|
if ( $unique ) { |
169
|
|
|
$meta_ids = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key ); |
170
|
|
|
$this->_meta_data = array_diff_key( $this->_meta_data, array_fill_keys( $meta_ids, '' ) ); |
171
|
|
|
} |
172
|
|
|
$this->_meta_data[ 'new-' . sizeof( $this->_meta_data ) ] = (object) array( |
173
|
|
|
'key' => $key, |
174
|
|
|
'value' => $value, |
175
|
|
|
); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Update meta data by key or ID, if provided. |
180
|
|
|
* @since 2.6.0 |
181
|
|
|
* @param string $key |
182
|
|
|
* @param string $value |
183
|
|
|
* @param int $meta_id |
184
|
|
|
*/ |
185
|
|
|
public function update_meta_data( $key, $value, $meta_id = '' ) { |
186
|
|
|
if ( $meta_id && isset( $this->_meta_data[ $meta_id ] ) ) { |
187
|
|
|
$this->_meta_data[ $meta_id ] = (object) array( |
188
|
|
|
'key' => $key, |
189
|
|
|
'value' => $value, |
190
|
|
|
); |
191
|
|
|
} else { |
192
|
|
|
$this->add_meta_data( $key, $value, true ); |
|
|
|
|
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Delete meta data. |
198
|
|
|
* @since 2.6.0 |
199
|
|
|
* @param array $key Meta key |
200
|
|
|
*/ |
201
|
|
|
public function delete_meta_data( $key ) { |
202
|
|
|
$meta_ids = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key ); |
203
|
|
|
$this->_meta_data = array_diff_key( $this->_meta_data, array_fill_keys( $meta_ids, '' ) ); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Read Meta Data from the database. Ignore any internal properties. |
208
|
|
|
* @since 2.6.0 |
209
|
|
|
*/ |
210
|
|
|
protected function read_meta_data() { |
211
|
|
|
$this->_meta_data = array(); |
212
|
|
|
$cache_loaded = false; |
213
|
|
|
|
214
|
|
|
if ( ! $this->get_id() ) { |
215
|
|
|
return; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
if ( ! empty ( $this->_cache_group ) ) { |
219
|
|
|
$cache_key = WC_Cache_Helper::get_cache_prefix( $this->_cache_group ) . $this->get_id(); |
220
|
|
|
$cached_meta = wp_cache_get( $cache_key, $this->_cache_group ); |
221
|
|
|
|
222
|
|
|
if ( false !== $cached_meta ) { |
223
|
|
|
$this->_meta_data = $cached_meta; |
224
|
|
|
$cache_loaded = true; |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
if ( ! $cache_loaded ) { |
229
|
|
|
global $wpdb; |
230
|
|
|
$db_info = $this->_get_db_info(); |
231
|
|
|
$raw_meta_data = $wpdb->get_results( $wpdb->prepare( " |
232
|
|
|
SELECT " . $db_info['meta_id_field'] . ", meta_key, meta_value |
233
|
|
|
FROM " . $db_info['table'] . " |
234
|
|
|
WHERE " . $db_info['object_id_field'] . " = %d ORDER BY " . $db_info['meta_id_field'] . " |
235
|
|
|
", $this->get_id() ) ); |
236
|
|
|
|
237
|
|
|
foreach ( $raw_meta_data as $meta ) { |
238
|
|
|
if ( in_array( $meta->meta_key, $this->get_internal_meta_keys() ) ) { |
239
|
|
|
continue; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
$this->_meta_data[ $meta->{$db_info['meta_id_field']} ] = (object) array( 'key' => $meta->meta_key, 'value' => $meta->meta_value ); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
if ( ! empty ( $this->_cache_group ) ) { |
246
|
|
|
wp_cache_set( $cache_key, $this->_meta_data, $this->_cache_group ); |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Update Meta Data in the database. |
253
|
|
|
* @since 2.6.0 |
254
|
|
|
*/ |
255
|
|
|
protected function save_meta_data() { |
256
|
|
|
global $wpdb; |
257
|
|
|
$db_info = $this->_get_db_info(); |
258
|
|
|
$all_meta_ids = array_map( 'absint', $wpdb->get_col( $wpdb->prepare( " |
259
|
|
|
SELECT " . $db_info['meta_id_field'] . " FROM " . $db_info['table'] . " |
260
|
|
|
WHERE " . $db_info['object_id_field'] . " = %d", $this->get_id() ) . " |
261
|
|
|
AND meta_key NOT IN ('" . implode( "','", array_map( 'esc_sql', $this->get_internal_meta_keys() ) ) . "'); |
262
|
|
|
" ) ); |
263
|
|
|
$set_meta_ids = array(); |
264
|
|
|
|
265
|
|
|
foreach ( $this->_meta_data as $meta_id => $meta ) { |
266
|
|
|
if ( 'new' === substr( $meta_id, 0, 3 ) ) { |
267
|
|
|
$set_meta_ids[] = add_metadata( $this->_meta_type, $this->get_id(), $meta->key, $meta->value, false ); |
268
|
|
|
} else { |
269
|
|
|
update_metadata_by_mid( $this->_meta_type, $meta_id, $meta->value, $meta->key ); |
270
|
|
|
$set_meta_ids[] = absint( $meta_id ); |
271
|
|
|
} |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
// Delete no longer set meta data |
275
|
|
|
$delete_meta_ids = array_diff( $all_meta_ids, $set_meta_ids ); |
276
|
|
|
|
277
|
|
|
foreach ( $delete_meta_ids as $meta_id ) { |
278
|
|
|
delete_metadata_by_mid( $this->_meta_type, $meta_id ); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
if ( ! empty ( $this->_cache_group ) ) { |
282
|
|
|
WC_Cache_Helper::incr_cache_prefix( $this->_cache_group ); |
283
|
|
|
} |
284
|
|
|
$this->read_meta_data(); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Table structure is slightly different between meta types, this function will return what we need to know. |
289
|
|
|
* @since 2.6.0 |
290
|
|
|
* @return array Array elements: table, object_id_field, meta_id_field |
291
|
|
|
*/ |
292
|
|
|
protected function _get_db_info() { |
293
|
|
|
global $wpdb; |
294
|
|
|
|
295
|
|
|
$meta_id_field = 'meta_id'; // for some reason users calls this umeta_id so we need to track this as well. |
296
|
|
|
$table = $wpdb->prefix; |
297
|
|
|
|
298
|
|
|
// If we are dealing with a type of metadata that is not a core type, the table should be prefixed. |
299
|
|
|
if ( ! in_array( $this->_meta_type, array( 'post', 'user', 'comment', 'term' ) ) ) { |
300
|
|
|
$table .= 'woocommerce_'; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$table .= $this->_meta_type . 'meta'; |
304
|
|
|
$object_id_field = $this->_meta_type . '_id'; |
305
|
|
|
|
306
|
|
|
// Figure out our field names. |
307
|
|
|
if ( 'user' === $this->_meta_type ) { |
308
|
|
|
$meta_id_field = 'umeta_id'; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
if ( ! empty( $this->object_id_field_for_meta ) ) { |
312
|
|
|
$object_id_field = $this->object_id_field_for_meta; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
return array( |
316
|
|
|
'table' => $table, |
317
|
|
|
'object_id_field' => $object_id_field, |
318
|
|
|
'meta_id_field' => $meta_id_field, |
319
|
|
|
); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
} |
323
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.