diff --git a/includes/class-wc-data-structurer.php b/includes/class-wc-data-structurer.php new file mode 100644 index 00000000000..87d3bd54b99 --- /dev/null +++ b/includes/class-wc-data-structurer.php @@ -0,0 +1,164 @@ +structured_data... + * + * @param array $json Partially formatted JSON-LD + * @return false If the param $json is not an array + */ + public function set_structured_data( $json ) { + if ( ! is_array( $json ) ) { + return false; + } + $this->structured_data[] = $json; + } + + /** + * Formats and returns the structured data... + * + * @return mixed bool|array If $this->structured_data is set, returns the fully formatted and encoded JSON-LD, otherwise returns false + */ + public function get_structured_data() { + if ( ! $this->structured_data ) { + return false; + } + $context['@context'] = 'http://schema.org/'; + + if ( count( $this->structured_data ) > 1 ) { + if ( is_product() ) { + foreach ( $this->structured_data as $value ) { + if ( isset( $value['@type'] ) ) { + if ( 'Product' === $value['@type'] ) { + $product = $value; + } + elseif ( 'Review' === $value['@type'] ) { + $reviews[] = $value; + } + } + } + $structured_data = isset( $reviews ) ? $product + array( 'review' => $reviews ) : $product; + } + elseif ( is_product_category() ) { + $structured_data = array( '@graph' => $this->structured_data ); + } + } + else { + $structured_data = $this->structured_data[0]; + } + return wp_json_encode( $context + $structured_data ); + } + + /** + * Contructor + */ + public function __construct() { + add_action( 'woocommerce_before_shop_loop_item', array( $this, 'init_product_category_structured_data' ) ); + add_action( 'woocommerce_single_product_summary', array( $this, 'init_product_structured_data' ) ); + add_action( 'woocommerce_review_meta', array( $this, 'init_product_review_structured_data' ) ); + add_action( 'wp_footer', array( $this, 'enqueue_structured_data' ) ); + } + + /** + * If structured data is set, echoes the encoded structured data into the `wp_footer` action hook. + */ + public function enqueue_structured_data() { + if ( $structured_data = $this->get_structured_data() ) { + echo ''; + } + } + + /** + * Generates the product category structured data... + * Hooked into the `woocommerce_before_shop_loop_item` action hook... + */ + public function init_product_category_structured_data() { + if ( ! is_product_category() ) { + return; + } + $this->init_product_structured_data(); + } + /** + * Generates the product structured data... + * Hooked into the `woocommerce_single_product_summary` action hook... + * Applies the `woocommerce_product_structured_data` filter hook for clean structured data customization... + */ + public function init_product_structured_data() { + global $product; + + $json['@type'] = 'Product'; + $json['@id'] = 'product-' . get_the_ID(); + $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(); + $json['brand'] = array( + '@type' => 'Thing', + 'name' => $product->get_attribute( __( 'brand', 'woocommerce' ) ) + ); + 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() + ); + } + $json['offers'] = array( + '@type' => 'Offer', + 'priceCurrency' => get_woocommerce_currency(), + 'price' => $product->get_price(), + 'itemCondition' => 'http://schema.org/NewCondition', + 'availability' => 'http://schema.org/' . $stock = ( $product->is_in_stock() ? 'InStock' : 'OutOfStock' ), + 'seller' => array( + '@type' => 'Organization', + 'name' => get_bloginfo( 'name' ) + ) + ); + $this->set_structured_data( apply_filters( 'woocommerce_product_structured_data', $json ) ); + } + + /** + * Generates the product review structured data... + * Hooked into the `woocommerce_review_meta` action hook... + * Applies the `woocommerce_product_review_structured_data` filter hook for clean structured data customization... + */ + public function init_product_review_structured_data() { + global $comment; + + $rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) ); + + $json['@type'] = 'Review'; + $json['@id'] = 'li-comment-' . get_comment_ID(); + $json['datePublished'] = get_comment_date( 'c' ); + $json['description'] = get_comment_text(); + $json['reviewRating'] = array( + '@type' => 'rating', + 'ratingValue' => $rating + ); + $json['author'] = array( + '@type' => 'Person', + 'name' => get_comment_author() + ); + $this->set_structured_data( apply_filters( 'woocommerce_product_review_structured_data', $json ) ); + } +} diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index a33509b286c..5c993c3ff5a 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -686,36 +686,6 @@ if ( ! function_exists( 'woocommerce_show_product_loop_sale_flash' ) ) { } } -if ( ! function_exists( 'woocommerce_get_product_schema' ) ) { - - /** - * Get a products Schema. - * @return string - */ - function woocommerce_get_product_schema() { - global $product; - - $schema = "Product"; - - // Downloadable product schema handling - if ( $product->is_downloadable() ) { - switch ( $product->download_type ) { - case 'application' : - $schema = "SoftwareApplication"; - break; - case 'music' : - $schema = "MusicAlbum"; - break; - default : - $schema = "Product"; - break; - } - } - - return 'http://schema.org/' . $schema; - } -} - if ( ! function_exists( 'woocommerce_get_product_thumbnail' ) ) { /** @@ -1218,7 +1188,7 @@ if ( ! function_exists( 'woocommerce_review_display_comment_text' ) ) { * Display the review content. */ function woocommerce_review_display_comment_text() { - echo '
get_price_html(); ?>
- - - -
', '
' ); +the_title( '', '
' ); diff --git a/woocommerce.php b/woocommerce.php index 8fd54d966a3..23cc51e49f0 100644 --- a/woocommerce.php +++ b/woocommerce.php @@ -295,6 +295,7 @@ final class WooCommerce { include_once( 'includes/class-wc-customer.php' ); // Customer class include_once( 'includes/class-wc-shortcodes.php' ); // Shortcodes class include_once( 'includes/class-wc-embed.php' ); // Embeds + include_once( 'includes/class-wc-data-structurer.php' ); // Data Structurer class } /** @@ -328,8 +329,9 @@ final class WooCommerce { // Classes/actions loaded for the frontend and for ajax requests. if ( $this->is_request( 'frontend' ) ) { - $this->cart = new WC_Cart(); // Cart class, stores the cart contents - $this->customer = new WC_Customer(); // Customer class, handles data such as customer location + $this->cart = new WC_Cart(); // Cart class, stores the cart contents + $this->customer = new WC_Customer(); // Customer class, handles data such as customer location + $this->data_structurer = new WC_Data_Structurer(); // Microdata class, generates and encodes a JSON-LD } $this->load_webhooks();