2016-07-17 02:42:46 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
|
|
exit; // Exit if accessed directly
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Structured data's handler and generator using JSON-LD format.
|
|
|
|
*
|
2016-07-24 21:46:30 +00:00
|
|
|
* @class WC_Structured_Data
|
|
|
|
* @version 2.7.0
|
|
|
|
* @package WooCommerce/Classes
|
|
|
|
* @category Class
|
|
|
|
* @author Clement Cazaud
|
2016-07-17 02:42:46 +00:00
|
|
|
*/
|
2016-07-19 19:36:52 +00:00
|
|
|
class WC_Structured_Data {
|
2016-07-18 21:31:29 +00:00
|
|
|
|
2016-07-17 02:42:46 +00:00
|
|
|
/**
|
2016-07-23 17:30:34 +00:00
|
|
|
* @var array Partially structured data
|
2016-07-17 02:42:46 +00:00
|
|
|
*/
|
2016-07-19 19:36:52 +00:00
|
|
|
private $data;
|
2016-07-18 21:31:29 +00:00
|
|
|
|
2016-07-17 02:42:46 +00:00
|
|
|
/**
|
2016-07-19 19:36:52 +00:00
|
|
|
* Checks if the passed $json variable is an array and stores it into $this->data...
|
2016-07-17 02:42:46 +00:00
|
|
|
*
|
2016-07-23 17:30:34 +00:00
|
|
|
* @param array $json Partially structured data
|
2016-07-24 07:31:12 +00:00
|
|
|
* @return bool Returns false If the param $json is not an array
|
2016-07-17 02:42:46 +00:00
|
|
|
*/
|
2016-07-19 19:36:52 +00:00
|
|
|
private function set_data( $json ) {
|
2016-07-17 02:42:46 +00:00
|
|
|
if ( ! is_array( $json ) ) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-19 19:36:52 +00:00
|
|
|
$this->data[] = $json;
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-07-23 17:30:34 +00:00
|
|
|
* Structures and returns the data...
|
2016-07-17 02:42:46 +00:00
|
|
|
*
|
2016-07-24 07:31:12 +00:00
|
|
|
* @return array If data is set, returns the structured data, otherwise returns empty array
|
2016-07-17 02:42:46 +00:00
|
|
|
*/
|
2016-07-19 19:36:52 +00:00
|
|
|
private function get_data() {
|
|
|
|
if ( ! $this->data ) {
|
|
|
|
return array();
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-19 19:36:52 +00:00
|
|
|
foreach ( $this->data as $value ) {
|
2016-07-18 21:31:29 +00:00
|
|
|
$type = isset( $value['@type'] ) ? $value['@type'] : false;
|
|
|
|
|
|
|
|
if ( 'Product' === $type || 'SoftwareApplication' === $type || 'MusicAlbum' === $type ) {
|
|
|
|
$products[] = $value;
|
2016-07-20 02:33:07 +00:00
|
|
|
} elseif ( 'Review' === $type ) {
|
2016-07-18 21:31:29 +00:00
|
|
|
$reviews[] = $value;
|
2016-07-24 07:31:12 +00:00
|
|
|
} elseif ( 'WebSite' === $type || 'BreadcrumbList' === $type ) {
|
2016-07-23 17:30:34 +00:00
|
|
|
$data[] = $value;
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-23 17:30:34 +00:00
|
|
|
|
2016-07-24 07:31:12 +00:00
|
|
|
if ( isset( $products ) ) {
|
2016-07-24 16:53:20 +00:00
|
|
|
if ( count( $products ) > 1 ) {
|
2016-07-24 07:31:12 +00:00
|
|
|
$data[] = array( '@graph' => $products );
|
2016-07-24 16:53:20 +00:00
|
|
|
} else {
|
|
|
|
$data[] = isset( $reviews ) ? $products[0] + array( 'review' => $reviews ) : $products[0];
|
2016-07-24 07:31:12 +00:00
|
|
|
}
|
2016-07-18 21:31:29 +00:00
|
|
|
}
|
2016-07-23 17:30:34 +00:00
|
|
|
|
2016-07-23 02:56:01 +00:00
|
|
|
if ( ! isset( $data ) ) {
|
2016-07-19 19:36:52 +00:00
|
|
|
return array();
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-23 02:56:01 +00:00
|
|
|
$context['@context'] = apply_filters( 'woocommerce_structured_data_context', 'http://schema.org/' );
|
2016-07-19 19:36:52 +00:00
|
|
|
|
2016-07-23 17:30:34 +00:00
|
|
|
foreach( $data as $key => $value ) {
|
|
|
|
$data[ $key ] = $context + $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( count( $data ) > 1 ) {
|
2016-07-24 07:31:12 +00:00
|
|
|
return $data = array( '@graph' => $data );
|
|
|
|
} else {
|
|
|
|
return $data[0];
|
2016-07-23 17:30:34 +00:00
|
|
|
}
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contructor
|
|
|
|
*/
|
|
|
|
public function __construct() {
|
2016-07-23 17:30:34 +00:00
|
|
|
add_action( 'woocommerce_before_main_content', array( $this, 'generate_shop_data' ) );
|
2016-07-24 21:46:30 +00:00
|
|
|
add_action( 'woocommerce_breadcrumb', array( $this, 'generate_breadcrumb_data' ), 10, 1 );
|
2016-07-19 19:36:52 +00:00
|
|
|
add_action( 'woocommerce_before_shop_loop_item', array( $this, 'generate_product_category_data' ) );
|
|
|
|
add_action( 'woocommerce_single_product_summary', array( $this, 'generate_product_data' ) );
|
2016-07-24 21:46:30 +00:00
|
|
|
add_action( 'woocommerce_review_meta', array( $this, 'generate_product_review_data' ), 10, 1 );
|
2016-07-19 19:36:52 +00:00
|
|
|
add_action( 'wp_footer', array( $this, 'enqueue_data' ) );
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-07-19 19:36:52 +00:00
|
|
|
* Sanitizes, encodes and echoes the structured data into `wp_footer` action hook.
|
2016-07-17 02:42:46 +00:00
|
|
|
*/
|
2016-07-19 19:36:52 +00:00
|
|
|
public function enqueue_data() {
|
2016-07-23 17:30:34 +00:00
|
|
|
if ( $data = $this->get_data() ) {
|
2016-07-19 19:36:52 +00:00
|
|
|
|
2016-07-23 17:30:34 +00:00
|
|
|
array_walk_recursive( $data, array( $this, 'sanitize_data' ) );
|
2016-07-19 19:36:52 +00:00
|
|
|
|
2016-07-23 17:30:34 +00:00
|
|
|
echo '<script type="application/ld+json">' . wp_json_encode( $data ) . '</script>';
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-20 02:33:07 +00:00
|
|
|
/**
|
|
|
|
* Callback function for sanitizing the structured data.
|
|
|
|
*
|
2016-07-23 17:30:34 +00:00
|
|
|
* @param ref $value
|
2016-07-20 02:33:07 +00:00
|
|
|
*/
|
|
|
|
private function sanitize_data( &$value ) {
|
|
|
|
$value = sanitize_text_field( $value );
|
|
|
|
}
|
|
|
|
|
2016-07-17 02:42:46 +00:00
|
|
|
/**
|
|
|
|
* Generates the product category structured data...
|
|
|
|
* Hooked into the `woocommerce_before_shop_loop_item` action hook...
|
|
|
|
*/
|
2016-07-19 19:36:52 +00:00
|
|
|
public function generate_product_category_data() {
|
2016-07-24 07:31:12 +00:00
|
|
|
if ( ! is_product_category() && ! is_shop() ) {
|
2016-07-17 02:42:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-19 19:36:52 +00:00
|
|
|
$this->generate_product_data();
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
2016-07-18 21:31:29 +00:00
|
|
|
|
2016-07-17 02:42:46 +00:00
|
|
|
/**
|
|
|
|
* Generates the product structured data...
|
|
|
|
* Hooked into the `woocommerce_single_product_summary` action hook...
|
2016-07-23 17:30:34 +00:00
|
|
|
* Applies the `woocommerce_structured_data_product` filter hook for clean structured data customization...
|
2016-07-17 02:42:46 +00:00
|
|
|
*/
|
2016-07-19 19:36:52 +00:00
|
|
|
public function generate_product_data() {
|
2016-07-17 02:42:46 +00:00
|
|
|
global $product;
|
|
|
|
|
2016-07-18 21:31:29 +00:00
|
|
|
if ( $product->is_downloadable() ) {
|
|
|
|
switch ( $product->download_type ) {
|
|
|
|
case 'application' :
|
|
|
|
$type = "SoftwareApplication";
|
|
|
|
break;
|
|
|
|
case 'music' :
|
|
|
|
$type = "MusicAlbum";
|
|
|
|
break;
|
|
|
|
default :
|
|
|
|
$type = "Product";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$type = "Product";
|
|
|
|
}
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-18 21:31:29 +00:00
|
|
|
$json['@type'] = $type;
|
|
|
|
$json['@id'] = get_the_permalink();
|
2016-07-17 02:42:46 +00:00
|
|
|
$json['name'] = get_the_title();
|
|
|
|
$json['image'] = wp_get_attachment_url( $product->get_image_id() );
|
|
|
|
$json['description'] = get_the_excerpt();
|
|
|
|
$json['url'] = get_the_permalink();
|
|
|
|
$json['sku'] = $product->get_sku();
|
2016-07-23 02:56:01 +00:00
|
|
|
|
2016-07-17 02:42:46 +00:00
|
|
|
if ( $product->get_rating_count() ) {
|
|
|
|
$json['aggregateRating'] = array(
|
|
|
|
'@type' => 'AggregateRating',
|
|
|
|
'ratingValue' => $product->get_average_rating(),
|
|
|
|
'ratingCount' => $product->get_rating_count(),
|
|
|
|
'reviewCount' => $product->get_review_count()
|
|
|
|
);
|
|
|
|
}
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-17 02:42:46 +00:00
|
|
|
$json['offers'] = array(
|
|
|
|
'@type' => 'Offer',
|
|
|
|
'priceCurrency' => get_woocommerce_currency(),
|
|
|
|
'price' => $product->get_price(),
|
2016-07-18 21:31:29 +00:00
|
|
|
'availability' => 'http://schema.org/' . $stock = ( $product->is_in_stock() ? 'InStock' : 'OutOfStock' )
|
2016-07-17 02:42:46 +00:00
|
|
|
);
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-23 02:56:01 +00:00
|
|
|
$this->set_data( apply_filters( 'woocommerce_structured_data_product', $json, $product ) );
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the product review structured data...
|
|
|
|
* Hooked into the `woocommerce_review_meta` action hook...
|
2016-07-23 17:30:34 +00:00
|
|
|
* Applies the `woocommerce_structured_data_product_review` filter hook for clean structured data customization...
|
2016-07-23 02:56:01 +00:00
|
|
|
*
|
2016-07-24 21:46:30 +00:00
|
|
|
* @param object $comment From `woocommerce_review_meta` action hook
|
2016-07-17 02:42:46 +00:00
|
|
|
*/
|
2016-07-23 02:56:01 +00:00
|
|
|
public function generate_product_review_data( $comment ) {
|
2016-07-17 02:42:46 +00:00
|
|
|
|
|
|
|
$json['@type'] = 'Review';
|
2016-07-23 02:56:01 +00:00
|
|
|
$json['@id'] = get_the_permalink() . '#li-comment-' . get_comment_ID();
|
2016-07-17 02:42:46 +00:00
|
|
|
$json['datePublished'] = get_comment_date( 'c' );
|
|
|
|
$json['description'] = get_comment_text();
|
|
|
|
$json['reviewRating'] = array(
|
|
|
|
'@type' => 'rating',
|
2016-07-23 02:56:01 +00:00
|
|
|
'ratingValue' => intval( get_comment_meta( $comment->comment_ID, 'rating', true ) )
|
2016-07-17 02:42:46 +00:00
|
|
|
);
|
|
|
|
$json['author'] = array(
|
|
|
|
'@type' => 'Person',
|
|
|
|
'name' => get_comment_author()
|
|
|
|
);
|
2016-07-20 02:33:07 +00:00
|
|
|
|
2016-07-23 02:56:01 +00:00
|
|
|
$this->set_data( apply_filters( 'woocommerce_structured_data_product_review', $json, $comment ) );
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|
2016-07-23 17:30:34 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the breadcrumbs structured data...
|
2016-07-24 21:46:30 +00:00
|
|
|
* Hooked into the `woocommerce_breadcrumb` action hook...
|
|
|
|
* Applies the `woocommerce_structured_data_breadcrumb` filter hook for clean structured data customization...
|
|
|
|
*
|
|
|
|
* @param array $args From `woocommerce_breadcrumb` action hook
|
2016-07-23 17:30:34 +00:00
|
|
|
*/
|
2016-07-24 21:46:30 +00:00
|
|
|
public function generate_breadcrumb_data( $args ) {
|
2016-07-24 22:38:59 +00:00
|
|
|
if ( empty( $args['breadcrumb'] ) ) {
|
2016-07-23 17:30:34 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-07-24 21:46:30 +00:00
|
|
|
|
2016-07-24 22:38:59 +00:00
|
|
|
$breadcrumb = $args['breadcrumb'];
|
|
|
|
$position = 1;
|
2016-07-23 17:30:34 +00:00
|
|
|
|
|
|
|
foreach ( $breadcrumb as $key => $value ) {
|
|
|
|
if ( ! empty( $value[1] ) && sizeof( $breadcrumb ) !== $key + 1 ) {
|
|
|
|
$json_crumbs_item = array(
|
|
|
|
'@id' => $value[1],
|
|
|
|
'name' => $value[0]
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$json_crumbs_item = array(
|
|
|
|
'name' => $value[0]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$json_crumbs[] = array(
|
|
|
|
'@type' => 'ListItem',
|
|
|
|
'position' => $position ++,
|
|
|
|
'item' => $json_crumbs_item
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$json['@type'] = 'BreadcrumbList';
|
|
|
|
$json['itemListElement'] = $json_crumbs;
|
|
|
|
|
2016-07-24 21:46:30 +00:00
|
|
|
$this->set_data( apply_filters( 'woocommerce_structured_data_breadcrumb', $json, $breadcrumb ) );
|
2016-07-23 17:30:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-07-24 07:31:12 +00:00
|
|
|
* Generates the shop related structured data...
|
2016-07-23 17:30:34 +00:00
|
|
|
* Hooked into the `woocommerce_before_main_content` action hook...
|
|
|
|
* Applies the `woocommerce_structured_data_shop` filter hook for clean structured data customization...
|
|
|
|
*/
|
|
|
|
public function generate_shop_data() {
|
2016-07-24 21:46:30 +00:00
|
|
|
if ( ! is_shop() || ! is_front_page() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-23 17:30:34 +00:00
|
|
|
$json['@type'] = 'WebSite';
|
|
|
|
$json['name'] = get_bloginfo( 'name' );
|
|
|
|
$json['url'] = get_bloginfo( 'url' );
|
2016-07-24 21:46:30 +00:00
|
|
|
$json['potentialAction'] = array(
|
|
|
|
'@type' => 'SearchAction',
|
|
|
|
'target' => get_bloginfo( 'url' ) . '/?s={search_term_string}&post_type=product',
|
|
|
|
'query-input' => 'required name=search_term_string'
|
|
|
|
);
|
2016-07-23 17:30:34 +00:00
|
|
|
|
|
|
|
$this->set_data( apply_filters( 'woocommerce_structured_data_shop', $json ) );
|
|
|
|
}
|
2016-07-17 02:42:46 +00:00
|
|
|
}
|