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