1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
4
|
|
|
exit; // Exit if accessed directly |
5
|
|
|
} |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Structured data's handler and generator using JSON-LD format. |
9
|
|
|
* |
10
|
|
|
* @class WC_Structured_Data |
11
|
|
|
* @since 2.7.0 |
12
|
|
|
* @version 2.7.0 |
13
|
|
|
* @package WooCommerce/Classes |
14
|
|
|
* @author Clément Cazaud <[email protected]> |
15
|
|
|
*/ |
16
|
|
|
class WC_Structured_Data { |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var array $_data |
20
|
|
|
*/ |
21
|
|
|
private $_data = array(); |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Constructor. |
25
|
|
|
*/ |
26
|
|
|
public function __construct() { |
27
|
|
|
// Generate data... |
28
|
|
|
add_action( 'woocommerce_before_main_content', array( $this, 'generate_website_data' ), 30, 0 ); |
29
|
|
|
add_action( 'woocommerce_breadcrumb', array( $this, 'generate_breadcrumblist_data' ), 10, 1 ); |
30
|
|
|
add_action( 'woocommerce_shop_loop', array( $this, 'generate_product_data' ), 10, 0 ); |
31
|
|
|
add_action( 'woocommerce_single_product_summary', array( $this, 'generate_product_data' ), 60, 0 ); |
32
|
|
|
add_action( 'woocommerce_review_meta', array( $this, 'generate_review_data' ), 20, 1 ); |
33
|
|
|
add_action( 'woocommerce_email_order_details', array( $this, 'generate_order_data' ), 20, 3 ); |
34
|
|
|
|
35
|
|
|
// Output structured data... |
36
|
|
|
add_action( 'woocommerce_email_order_details', array( $this, 'output_structured_data' ), 30, 0 ); |
37
|
|
|
add_action( 'wp_footer', array( $this, 'output_structured_data' ), 10, 0 ); |
38
|
|
|
|
39
|
|
|
// Filters... |
40
|
|
|
add_filter( 'woocommerce_structured_data_product_limit', array( $this, 'limit_product_data_in_loops' ), 10, 0 ); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Sets `$this->_data`. |
45
|
|
|
* |
46
|
|
|
* @param array $data |
47
|
|
|
* @param bool $reset (default: false) |
48
|
|
|
* @return bool |
49
|
|
|
*/ |
50
|
|
|
public function set_data( $data, $reset = false ) { |
51
|
|
|
if ( ! isset( $data['@type'] ) || ! preg_match( '|^[a-zA-Z]{1,20}$|', $data['@type'] ) ) { |
52
|
|
|
return false; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
if ( $reset && isset( $this->_data ) ) { |
56
|
|
|
unset( $this->_data ); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
$this->_data[] = $data; |
60
|
|
|
|
61
|
|
|
return true; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Gets `$this->_data`. |
66
|
|
|
* |
67
|
|
|
* @return array |
68
|
|
|
*/ |
69
|
|
|
public function get_data() { |
70
|
|
|
return $this->_data; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Structures and returns data. |
75
|
|
|
* |
76
|
|
|
* List of types available by default for specific request: |
77
|
|
|
* |
78
|
|
|
* 'product', |
79
|
|
|
* 'review', |
80
|
|
|
* 'breadcrumblist', |
81
|
|
|
* 'website', |
82
|
|
|
* 'order', |
83
|
|
|
* |
84
|
|
|
* @param bool|array|string $requested_types (default: false) |
85
|
|
|
* @return array |
86
|
|
|
*/ |
87
|
|
|
public function get_structured_data( $requested_types = false ) { |
88
|
|
|
$data = $this->_data; |
89
|
|
|
|
90
|
|
|
if ( empty( $data ) || ( $requested_types && ! is_array( $requested_types ) && ! is_string( $requested_types ) || is_null( $requested_types ) ) ) { |
91
|
|
|
return array(); |
92
|
|
|
} elseif ( $requested_types && is_string( $requested_types ) ) { |
93
|
|
|
$requested_types = array( $requested_types ); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
// Put together the values of same type of structured data. |
97
|
|
|
foreach ( $data as $value ) { |
98
|
|
|
$structured_data[ strtolower( $value['@type'] ) ][] = $value; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
// Wrap the multiple values of each type inside a graph... Then add context to each type. |
102
|
|
|
foreach ( $structured_data as $type => $value ) { |
103
|
|
|
$structured_data[ $type ] = count( $value ) > 1 ? array( '@graph' => $value ) : $value[0]; |
104
|
|
|
$structured_data[ $type ] = apply_filters( 'woocommerce_structured_data_context', array( '@context' => 'http://schema.org/' ), $structured_data, $type, $value ) + $structured_data[ $type ]; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
// If requested types, pick them up... Finally change the associative array to an indexed one. |
108
|
|
|
$structured_data = $requested_types ? array_values( array_intersect_key( $structured_data, array_flip( $requested_types ) ) ) : array_values( $structured_data ); |
109
|
|
|
|
110
|
|
|
if ( ! empty( $structured_data ) ) { |
111
|
|
|
$structured_data = count( $structured_data ) > 1 ? array( '@graph' => $structured_data ) : $structured_data[0]; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
return $structured_data; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Sanitizes, encodes and outputs structured data. |
119
|
|
|
* |
120
|
|
|
* Hooked into `wp_footer` action hook. |
121
|
|
|
* Hooked into `woocommerce_email_order_details` action hook. |
122
|
|
|
* |
123
|
|
|
* @param bool|array|string $requested_types (default: true) |
124
|
|
|
* @return bool |
125
|
|
|
*/ |
126
|
|
|
public function output_structured_data( $requested_types = true ) { |
127
|
|
|
if ( true === $requested_types ) { |
128
|
|
|
$requested_types = array_filter( apply_filters( 'woocommerce_structured_data_type_for_page', array( |
129
|
|
|
is_shop() || is_product_category() || is_product() ? 'product' : null, |
130
|
|
|
is_shop() && is_front_page() ? 'website' : null, |
131
|
|
|
is_product() ? 'review' : null, |
132
|
|
|
! is_shop() ? 'breadcrumblist' : null, |
133
|
|
|
'order', |
134
|
|
|
) ) ); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
if ( $structured_data = $this->sanitize_data( $this->get_structured_data( $requested_types ) ) ) { |
138
|
|
|
echo '<script type="application/ld+json">' . wp_json_encode( $structured_data ) . '</script>'; |
139
|
|
|
|
140
|
|
|
return true; |
141
|
|
|
} else { |
142
|
|
|
return false; |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Sanitizes data. |
148
|
|
|
* |
149
|
|
|
* @param array $data |
150
|
|
|
* @return array |
151
|
|
|
*/ |
152
|
|
|
public function sanitize_data( $data ) { |
153
|
|
|
if ( ! $data || ! is_array( $data ) ) { |
|
|
|
|
154
|
|
|
return array(); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
foreach ( $data as $key => $value ) { |
158
|
|
|
$sanitized_data[ sanitize_text_field( $key ) ] = is_array( $value ) ? $this->sanitize_data( $value ) : sanitize_text_field( $value ); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
return $sanitized_data; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Generates, sanitizes, encodes and outputs specific structured data type. |
166
|
|
|
* |
167
|
|
|
* @param string $type |
168
|
|
|
* @param mixed $object (default: null) |
169
|
|
|
* @param mixed $param_1 (default: null) |
170
|
|
|
* @param mixed $param_2 (default: null) |
171
|
|
|
* @param mixed $param_3 (default: null) |
172
|
|
|
* @return bool |
173
|
|
|
*/ |
174
|
|
|
public function generate_output_structured_data( $type, $object = null, $param_1 = null, $param_2 = null, $param_3 = null ) { |
175
|
|
|
if ( ! is_string( $type ) ) { |
176
|
|
|
return false; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
$generate = 'generate_' . $type . '_data'; |
180
|
|
|
|
181
|
|
|
if ( method_exists( $this, $generate ) && $this->$generate( $object, $param_1, $param_2, $param_3 ) ) { |
182
|
|
|
return $this->output_structured_data( $type ); |
183
|
|
|
} else { |
184
|
|
|
return false; |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Limits Product structured data on taxonomies and shop page. |
190
|
|
|
* |
191
|
|
|
* Hooked into `woocommerce_structured_data_product_limit` filter hook. |
192
|
|
|
* |
193
|
|
|
* @return bool |
194
|
|
|
*/ |
195
|
|
|
public function limit_product_data_in_loops() { |
196
|
|
|
return is_product_taxonomy() || is_shop() ? true : false; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/* |
200
|
|
|
|-------------------------------------------------------------------------- |
201
|
|
|
| Generators |
202
|
|
|
|-------------------------------------------------------------------------- |
203
|
|
|
| |
204
|
|
|
| Methods for generating specific structured data types: |
205
|
|
|
| |
206
|
|
|
| - Product |
207
|
|
|
| - Review |
208
|
|
|
| - BreadcrumbList |
209
|
|
|
| - WebSite |
210
|
|
|
| - Order |
211
|
|
|
| |
212
|
|
|
| The generated data is stored into `$this->_data`. |
213
|
|
|
| See the methods above for handling `$this->_data`. |
214
|
|
|
| |
215
|
|
|
*/ |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Generates Product structured data. |
219
|
|
|
* |
220
|
|
|
* Hooked into `woocommerce_single_product_summary` action hook. |
221
|
|
|
* Hooked into `woocommerce_shop_loop` action hook. |
222
|
|
|
* |
223
|
|
|
* @param bool|object $product (default: false) |
224
|
|
|
* @param bool $limit_data (default: false) |
225
|
|
|
* @return bool |
226
|
|
|
*/ |
227
|
|
|
public function generate_product_data( $product = false, $limit_data = false ) { |
228
|
|
|
if ( false === $product ) { |
229
|
|
|
global $product; |
230
|
|
|
} elseif ( ! is_object( $product ) ) { |
231
|
|
|
return false; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
$limit_data = apply_filters( 'woocommerce_structured_data_product_limit', $limit_data ); |
235
|
|
|
|
236
|
|
|
$markup['@type'] = 'Product'; |
237
|
|
|
$markup['@id'] = get_permalink( $product->get_id() ); |
238
|
|
|
$markup['url'] = $markup['@id']; |
239
|
|
|
$markup['name'] = $product->get_title(); |
240
|
|
|
|
241
|
|
|
if ( true === $limit_data ) { |
242
|
|
|
return $this->set_data( apply_filters( 'woocommerce_structured_data_product_limited', $markup, $product ) ); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
if ( $is_variable = $product->is_type( 'variable' ) ) { |
246
|
|
|
$variations = $product->get_available_variations(); |
247
|
|
|
|
248
|
|
|
foreach ( $variations as $variation ) { |
249
|
|
|
$product_variations[] = wc_get_product( $variation['variation_id'] ); |
250
|
|
|
} |
251
|
|
|
} else { |
252
|
|
|
$product_variations[] = $product; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
foreach ( $product_variations as $product_variation ) { |
256
|
|
|
$markup_offers[] = array( |
257
|
|
|
'@type' => 'Offer', |
258
|
|
|
'priceCurrency' => get_woocommerce_currency(), |
259
|
|
|
'price' => $product_variation->get_price(), |
260
|
|
|
'availability' => 'http://schema.org/' . $stock = ( $product_variation->is_in_stock() ? 'InStock' : 'OutOfStock' ), |
261
|
|
|
'sku' => $product_variation->get_sku(), |
262
|
|
|
'image' => wp_get_attachment_url( $product_variation->get_image_id() ), |
263
|
|
|
'description' => $is_variable ? $product_variation->get_variation_description() : '', |
264
|
|
|
'seller' => array( |
265
|
|
|
'@type' => 'Organization', |
266
|
|
|
'name' => get_bloginfo( 'name' ), |
267
|
|
|
'url' => get_bloginfo( 'url' ), |
268
|
|
|
), |
269
|
|
|
); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
$markup['description'] = get_the_excerpt( $product->get_id() ); |
273
|
|
|
$markup['offers'] = $markup_offers; |
274
|
|
|
|
275
|
|
|
if ( $product->get_rating_count() ) { |
276
|
|
|
$markup['aggregateRating'] = array( |
277
|
|
|
'@type' => 'AggregateRating', |
278
|
|
|
'ratingValue' => $product->get_average_rating(), |
279
|
|
|
'ratingCount' => $product->get_rating_count(), |
280
|
|
|
'reviewCount' => $product->get_review_count(), |
281
|
|
|
); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
return $this->set_data( apply_filters( 'woocommerce_structured_data_product', $markup, $product ) ); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Generates Review structured data. |
289
|
|
|
* |
290
|
|
|
* Hooked into `woocommerce_review_meta` action hook. |
291
|
|
|
* |
292
|
|
|
* @param object $comment |
293
|
|
|
* @return bool |
294
|
|
|
*/ |
295
|
|
|
public function generate_review_data( $comment ) { |
296
|
|
|
if ( ! is_object( $comment ) ) { |
297
|
|
|
return false; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
$markup['@type'] = 'Review'; |
301
|
|
|
$markup['@id'] = get_comment_link( $comment->comment_ID ); |
302
|
|
|
$markup['datePublished'] = get_comment_date( 'c', $comment->comment_ID ); |
303
|
|
|
$markup['description'] = get_comment_text( $comment->comment_ID ); |
304
|
|
|
$markup['itemReviewed'] = array( |
305
|
|
|
'@type' => 'Product', |
306
|
|
|
'name' => get_the_title( $comment->post_ID ), |
307
|
|
|
); |
308
|
|
|
$markup['reviewRating'] = array( |
309
|
|
|
'@type' => 'rating', |
310
|
|
|
'ratingValue' => get_comment_meta( $comment->comment_ID, 'rating', true ), |
311
|
|
|
); |
312
|
|
|
$markup['author'] = array( |
313
|
|
|
'@type' => 'Person', |
314
|
|
|
'name' => get_comment_author( $comment->comment_ID ), |
315
|
|
|
); |
316
|
|
|
|
317
|
|
|
return $this->set_data( apply_filters( 'woocommerce_structured_data_review', $markup, $comment ) ); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Generates BreadcrumbList structured data. |
322
|
|
|
* |
323
|
|
|
* Hooked into `woocommerce_breadcrumb` action hook. |
324
|
|
|
* |
325
|
|
|
* @param object $breadcrumbs |
326
|
|
|
* @return bool|void |
327
|
|
|
*/ |
328
|
|
|
public function generate_breadcrumblist_data( $breadcrumbs ) { |
329
|
|
|
if ( ! is_object( $breadcrumbs ) ) { |
330
|
|
|
return false; |
331
|
|
|
} elseif ( ! $crumbs = $breadcrumbs->get_breadcrumb() ) { |
332
|
|
|
return; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
foreach ( $crumbs as $key => $crumb ) { |
336
|
|
|
$markup_crumbs[ $key ] = array( |
337
|
|
|
'@type' => 'ListItem', |
338
|
|
|
'position' => $key + 1, |
339
|
|
|
'item' => array( |
340
|
|
|
'name' => $crumb[0], |
341
|
|
|
), |
342
|
|
|
); |
343
|
|
|
|
344
|
|
|
if ( ! empty( $crumb[1] ) && sizeof( $crumbs ) !== $key + 1 ) { |
345
|
|
|
$markup_crumbs[ $key ]['item'] += array( '@id' => $crumb[1] ); |
346
|
|
|
} |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
$markup['@type'] = 'BreadcrumbList'; |
350
|
|
|
$markup['itemListElement'] = $markup_crumbs; |
351
|
|
|
|
352
|
|
|
return $this->set_data( apply_filters( 'woocommerce_structured_data_breadcrumblist', $markup, $breadcrumbs ) ); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* Generates WebSite structured data. |
357
|
|
|
* |
358
|
|
|
* Hooked into `woocommerce_before_main_content` action hook. |
359
|
|
|
* |
360
|
|
|
* @return bool |
361
|
|
|
*/ |
362
|
|
|
public function generate_website_data() { |
363
|
|
|
$markup['@type'] = 'WebSite'; |
364
|
|
|
$markup['name'] = get_bloginfo( 'name' ); |
365
|
|
|
$markup['url'] = get_bloginfo( 'url' ); |
366
|
|
|
$markup['potentialAction'] = array( |
367
|
|
|
'@type' => 'SearchAction', |
368
|
|
|
'target' => get_bloginfo( 'url' ) . '/?s={search_term_string}&post_type=product', |
369
|
|
|
'query-input' => 'required name=search_term_string', |
370
|
|
|
); |
371
|
|
|
|
372
|
|
|
return $this->set_data( apply_filters( 'woocommerce_structured_data_website', $markup ) ); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Generates Order structured data. |
377
|
|
|
* |
378
|
|
|
* Hooked into `woocommerce_email_order_details` action hook. |
379
|
|
|
* |
380
|
|
|
* @param object $order |
381
|
|
|
* @param bool $sent_to_admin (default: false) |
382
|
|
|
* @param bool $plain_text (default: false) |
383
|
|
|
* @return bool|void |
384
|
|
|
*/ |
385
|
|
|
public function generate_order_data( $order, $sent_to_admin = false, $plain_text = false ) { |
386
|
|
|
if ( ! is_object( $order ) ) { |
387
|
|
|
return false; |
388
|
|
|
} elseif ( $plain_text ) { |
389
|
|
|
return; |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
foreach ( $order->get_items() as $item ) { |
393
|
|
|
if ( ! apply_filters( 'woocommerce_order_item_visible', true, $item ) ) { |
394
|
|
|
continue; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
$product = apply_filters( 'woocommerce_order_item_product', $order->get_product_from_item( $item ), $item ); |
398
|
|
|
$product_exists = is_object( $product ); |
399
|
|
|
$is_visible = $product_exists && $product->is_visible(); |
400
|
|
|
$order_url = $sent_to_admin ? admin_url( 'post.php?post=' . absint( $order->id ) . '&action=edit' ) : $order->get_view_order_url(); |
401
|
|
|
|
402
|
|
|
$markup_offers[] = array( |
403
|
|
|
'@type' => 'Offer', |
404
|
|
|
'price' => $order->get_line_subtotal( $item ), |
405
|
|
|
'priceCurrency' => $order->get_currency(), |
406
|
|
|
'priceSpecification' => array( |
407
|
|
|
'price' => $order->get_line_subtotal( $item ), |
408
|
|
|
'priceCurrency' => $order->get_currency(), |
409
|
|
|
'eligibleQuantity' => array( |
410
|
|
|
'@type' => 'QuantitativeValue', |
411
|
|
|
'value' => apply_filters( 'woocommerce_email_order_item_quantity', $item['qty'], $item ), |
412
|
|
|
), |
413
|
|
|
), |
414
|
|
|
'itemOffered' => array( |
415
|
|
|
'@type' => 'Product', |
416
|
|
|
'name' => apply_filters( 'woocommerce_order_item_name', $item['name'], $item, $is_visible ), |
417
|
|
|
'sku' => $product_exists ? $product->get_sku() : '', |
418
|
|
|
'image' => $product_exists ? wp_get_attachment_image_url( $product->get_image_id() ) : '', |
419
|
|
|
'url' => $is_visible ? get_permalink( $product->get_id() ) : get_home_url(), |
420
|
|
|
), |
421
|
|
|
'seller' => array( |
422
|
|
|
'@type' => 'Organization', |
423
|
|
|
'name' => get_bloginfo( 'name' ), |
424
|
|
|
'url' => get_bloginfo( 'url' ), |
425
|
|
|
), |
426
|
|
|
); |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
switch ( $order->get_status() ) { |
430
|
|
|
case 'pending': |
431
|
|
|
$order_status = 'http://schema.org/OrderPaymentDue'; |
432
|
|
|
break; |
433
|
|
|
case 'processing': |
434
|
|
|
$order_status = 'http://schema.org/OrderProcessing'; |
435
|
|
|
break; |
436
|
|
|
case 'on-hold': |
437
|
|
|
$order_status = 'http://schema.org/OrderProblem'; |
438
|
|
|
break; |
439
|
|
|
case 'completed': |
440
|
|
|
$order_status = 'http://schema.org/OrderDelivered'; |
441
|
|
|
break; |
442
|
|
|
case 'cancelled': |
443
|
|
|
$order_status = 'http://schema.org/OrderCancelled'; |
444
|
|
|
break; |
445
|
|
|
case 'refunded': |
446
|
|
|
$order_status = 'http://schema.org/OrderReturned'; |
447
|
|
|
break; |
448
|
|
|
case 'failed': |
449
|
|
|
$order_status = 'http://schema.org/OrderProblem'; |
450
|
|
|
break; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
$markup['@type'] = 'Order'; |
454
|
|
|
$markup['url'] = $order_url; |
455
|
|
|
$markup['orderStatus'] = $order_status; |
456
|
|
|
$markup['orderNumber'] = $order->get_order_number(); |
457
|
|
|
$markup['orderDate'] = date( 'c', $order->get_date_created() ); |
458
|
|
|
$markup['acceptedOffer'] = $markup_offers; |
459
|
|
|
$markup['discount'] = $order->get_total_discount(); |
460
|
|
|
$markup['discountCurrency'] = $order->get_currency(); |
461
|
|
|
$markup['price'] = $order->get_total(); |
462
|
|
|
$markup['priceCurrency'] = $order->get_currency(); |
463
|
|
|
$markup['priceSpecification'] = array( |
464
|
|
|
'price' => $order->get_total(), |
465
|
|
|
'priceCurrency' => $order->get_currency(), |
466
|
|
|
'valueAddedTaxIncluded' => true, |
467
|
|
|
); |
468
|
|
|
$markup['billingAddress'] = array( |
469
|
|
|
'@type' => 'PostalAddress', |
470
|
|
|
'name' => $order->get_formatted_billing_full_name(), |
471
|
|
|
'streetAddress' => $order->get_billing_address_1(), |
472
|
|
|
'postalCode' => $order->get_billing_postcode(), |
473
|
|
|
'addressLocality' => $order->get_billing_city(), |
474
|
|
|
'addressRegion' => $order->get_billing_state(), |
475
|
|
|
'addressCountry' => $order->get_billing_country(), |
476
|
|
|
'email' => $order->get_billing_email(), |
477
|
|
|
'telephone' => $order->get_billing_phone(), |
478
|
|
|
); |
479
|
|
|
$markup['customer'] = array( |
480
|
|
|
'@type' => 'Person', |
481
|
|
|
'name' => $order->get_formatted_billing_full_name(), |
482
|
|
|
); |
483
|
|
|
$markup['merchant'] = array( |
484
|
|
|
'@type' => 'Organization', |
485
|
|
|
'name' => get_bloginfo( 'name' ), |
486
|
|
|
'url' => get_bloginfo( 'url' ), |
487
|
|
|
); |
488
|
|
|
$markup['potentialAction'] = array( |
489
|
|
|
'@type' => 'ViewAction', |
490
|
|
|
'name' => 'View Order', |
491
|
|
|
'url' => $order_url, |
492
|
|
|
'target' => $order_url, |
493
|
|
|
); |
494
|
|
|
|
495
|
|
|
return $this->set_data( apply_filters( 'woocommerce_structured_data_order', $markup, $sent_to_admin, $order ), true ); |
496
|
|
|
} |
497
|
|
|
} |
498
|
|
|
|
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.