improved structured data

This commit is contained in:
opportus 2016-07-17 04:42:46 +02:00
parent 1d1a5d937c
commit d74a3408b3
13 changed files with 184 additions and 54 deletions

View File

@ -0,0 +1,164 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Structured data's handler and generator using JSON-LD format.
*
* @class WC_Data_Structurer
* @version 2.7.0
* @package WooCommerce/Classes
* @category Class
* @author Clement Cazaud
*/
class WC_Data_Structurer {
/**
* @var array Partially formatted structured data
*/
private $structured_data;
/**
* Checks if the passed $json variable is an array and stores it into $this->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 '<script type="application/ld+json">' . $structured_data . '</script>';
}
}
/**
* 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 ) );
}
}

View File

@ -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 '<div itemprop="description" class="description">';
echo '<div class="description">';
comment_text();
echo '</div>';
}

View File

@ -36,7 +36,7 @@ if ( ! defined( 'ABSPATH' ) ) {
}
?>
<div itemscope itemtype="<?php echo woocommerce_get_product_schema(); ?>" id="product-<?php the_ID(); ?>" <?php post_class(); ?>>
<div id="product-<?php the_ID(); ?>" <?php post_class(); ?>>
<?php
/**
@ -78,8 +78,6 @@ if ( ! defined( 'ABSPATH' ) ) {
do_action( 'woocommerce_after_single_product_summary' );
?>
<meta itemprop="url" content="<?php the_permalink(); ?>" />
</div><!-- #product-<?php the_ID(); ?> -->
<?php do_action( 'woocommerce_after_single_product' ); ?>

View File

@ -32,7 +32,7 @@ $tag_count = sizeof( get_the_terms( $post->ID, 'product_tag' ) );
<?php if ( wc_product_sku_enabled() && ( $product->get_sku() || $product->is_type( 'variable' ) ) ) : ?>
<span class="sku_wrapper"><?php _e( 'SKU:', 'woocommerce' ); ?> <span class="sku" itemprop="sku"><?php echo ( $sku = $product->get_sku() ) ? $sku : __( 'N/A', 'woocommerce' ); ?></span></span>
<span class="sku_wrapper"><?php _e( 'SKU:', 'woocommerce' ); ?> <span class="sku"><?php echo ( $sku = $product->get_sku() ) ? $sku : __( 'N/A', 'woocommerce' ); ?></span></span>
<?php endif; ?>

View File

@ -23,12 +23,8 @@ if ( ! defined( 'ABSPATH' ) ) {
global $product;
?>
<div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
<div>
<p class="price"><?php echo $product->get_price_html(); ?></p>
<meta itemprop="price" content="<?php echo esc_attr( $product->get_display_price() ); ?>" />
<meta itemprop="priceCurrency" content="<?php echo esc_attr( get_woocommerce_currency() ); ?>" />
<link itemprop="availability" href="http://schema.org/<?php echo $product->is_in_stock() ? 'InStock' : 'OutOfStock'; ?>" />
</div>

View File

@ -32,7 +32,7 @@ global $post, $product;
'title' => $props['title'],
'alt' => $props['alt'],
) );
echo apply_filters( 'woocommerce_single_product_image_html', sprintf( '<a href="%s" itemprop="image" class="woocommerce-main-image zoom" title="%s" data-rel="prettyPhoto' . $gallery . '">%s</a>', $props['url'], $props['caption'], $image ), $post->ID );
echo apply_filters( 'woocommerce_single_product_image_html', sprintf( '<a href="%s" class="woocommerce-main-image zoom" title="%s" data-rel="prettyPhoto' . $gallery . '">%s</a>', $props['url'], $props['caption'], $image ), $post->ID );
} else {
echo apply_filters( 'woocommerce_single_product_image_html', sprintf( '<img src="%s" alt="%s" />', wc_placeholder_img_src(), __( 'Placeholder', 'woocommerce' ) ), $post->ID );
}

View File

@ -32,14 +32,14 @@ $average = $product->get_average_rating();
if ( $rating_count > 0 ) : ?>
<div class="woocommerce-product-rating" itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">
<div class="woocommerce-product-rating">
<div class="star-rating" title="<?php printf( __( 'Rated %s out of 5', 'woocommerce' ), $average ); ?>">
<span style="width:<?php echo ( ( $average / 5 ) * 100 ); ?>%">
<strong itemprop="ratingValue" class="rating"><?php echo esc_html( $average ); ?></strong> <?php printf( __( 'out of %s5%s', 'woocommerce' ), '<span itemprop="bestRating">', '</span>' ); ?>
<?php printf( _n( 'based on %s customer rating', 'based on %s customer ratings', $rating_count, 'woocommerce' ), '<span itemprop="ratingCount" class="rating">' . $rating_count . '</span>' ); ?>
<strong class="rating"><?php echo esc_html( $average ); ?></strong> <?php printf( __( 'out of %s5%s', 'woocommerce' ), '<span>', '</span>' ); ?>
<?php printf( _n( 'based on %s customer rating', 'based on %s customer ratings', $rating_count, 'woocommerce' ), '<span class="rating">' . $rating_count . '</span>' ); ?>
</span>
</div>
<?php if ( comments_open() ) : ?><a href="#reviews" class="woocommerce-review-link" rel="nofollow">(<?php printf( _n( '%s customer review', '%s customer reviews', $review_count, 'woocommerce' ), '<span itemprop="reviewCount" class="count">' . $review_count . '</span>' ); ?>)</a><?php endif ?>
<?php if ( comments_open() ) : ?><a href="#reviews" class="woocommerce-review-link" rel="nofollow">(<?php printf( _n( '%s customer review', '%s customer reviews', $review_count, 'woocommerce' ), '<span class="count">' . $review_count . '</span>' ); ?>)</a><?php endif ?>
</div>
<?php endif; ?>

View File

@ -30,13 +30,13 @@ if ( '0' === $comment->comment_approved ) { ?>
<?php } else { ?>
<p class="meta">
<strong itemprop="author"><?php comment_author(); ?></strong> <?php
<strong><?php comment_author(); ?></strong> <?php
if ( 'yes' === get_option( 'woocommerce_review_rating_verification_label' ) && $verified ) {
echo '<em class="verified">(' . esc_attr__( 'verified owner', 'woocommerce' ) . ')</em> ';
}
?>&ndash; <time itemprop="datePublished" datetime="<?php echo get_comment_date( 'c' ); ?>"><?php echo get_comment_date( wc_date_format() ); ?></time>:
?>&ndash; <time datetime="<?php echo get_comment_date( 'c' ); ?>"><?php echo get_comment_date( wc_date_format() ); ?></time>:
</p>
<?php }

View File

@ -25,8 +25,8 @@ $rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) );
if ( $rating && get_option( 'woocommerce_enable_review_rating' ) === 'yes' ) { ?>
<div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" class="star-rating" title="<?php echo sprintf( esc_attr__( 'Rated %d out of 5', 'woocommerce' ), esc_attr( $rating ) ) ?>">
<span style="width:<?php echo ( esc_attr( $rating ) / 5 ) * 100; ?>%"><strong itemprop="ratingValue"><?php echo esc_attr( $rating ); ?></strong> <?php esc_attr_e( 'out of 5', 'woocommerce' ); ?></span>
<div class="star-rating" title="<?php echo sprintf( esc_attr__( 'Rated %d out of 5', 'woocommerce' ), esc_attr( $rating ) ) ?>">
<span style="width:<?php echo ( esc_attr( $rating ) / 5 ) * 100; ?>%"><strong><?php echo esc_attr( $rating ); ?></strong> <?php esc_attr_e( 'out of 5', 'woocommerce' ); ?></span>
</div>
<?php }

View File

@ -22,7 +22,7 @@ if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
?>
<li itemprop="review" itemscope itemtype="http://schema.org/Review" <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>">
<li <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>">
<div id="comment-<?php comment_ID(); ?>" class="comment_container">

View File

@ -27,6 +27,6 @@ if ( ! $post->post_excerpt ) {
}
?>
<div itemprop="description">
<div>
<?php echo apply_filters( 'woocommerce_short_description', $post->post_excerpt ) ?>
</div>

View File

@ -20,4 +20,4 @@ if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
the_title( '<h1 itemprop="name" class="product_title entry-title">', '</h1>' );
the_title( '<h1 class="product_title entry-title">', '</h1>' );

View File

@ -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();