File "class-woocommerce-pro.php"
Full Path: /home/diablzlo/glucosebalnce.com/wp-content/plugins/seo-by-rank-math-pro/includes/modules/woocommerce/class-woocommerce-pro.php
File size: 18.93 KB
MIME-type: text/x-php
Charset: utf-8
<?php //phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase -- This filename format is intentionally used to match the plugin version.
/**
* WooCommerce module.
*
* @since 1.0
* @package RankMathPro
* @author Rank Math <support@rankmath.com>
*/
namespace RankMathPro;
use RankMath\Helper;
use RankMathPro\WooCommerce\Migrate_GTIN;
use RankMath\Traits\Hooker;
use RankMath\Schema\DB;
use RankMath\Schema\Product_WooCommerce;
use RankMath\Schema\Product;
defined( 'ABSPATH' ) || exit;
/**
* WooCommerce class.
*
* @codeCoverageIgnore
*/
class WooCommerce {
use Hooker;
/**
* Hold variesBy data to use in the ProductGroup schema.
*
* @var array
*/
private $varies_by = [];
/**
* Whether to noindex and remove hidden products from the Sitemap.
*
* @var bool
*/
private $noindex_hidden_products;
/**
* Include Products with specific statuses in the Sitemap.
*
* @var array
*/
private $exclude_stock_status = [];
/**
* Constructor.
*/
public function __construct() {
$this->filter( 'rank_math/database/tools', 'add_gtin_migration_tool' );
if ( is_admin() ) {
new Admin();
return;
}
$this->noindex_hidden_products = Helper::get_settings( 'general.noindex_hidden_products' );
$this->exclude_stock_status = $this->do_filter( 'woocommerce/stock_status', [] );
Migrate_GTIN::get();
$this->action( 'wp', 'init' );
$this->filter( 'rank_math/json_ld', 'add_carousels', 11, 2 );
$this->filter( 'rank_math/tools/migrate_gtin_values', 'migrate_gtin_values' );
if ( ! $this->noindex_hidden_products && empty( $this->exclude_stock_status ) ) {
return;
}
$this->filter( 'rank_math/sitemap/post_count/join', 'join_clause', 10, 2 );
$this->filter( 'rank_math/sitemap/post_count/where', 'where_clause', 10, 2 );
$this->filter( 'rank_math/sitemap/get_posts/join', 'join_clause', 10, 2 );
$this->filter( 'rank_math/sitemap/get_posts/where', 'where_clause', 10, 2 );
}
/**
* Get JOIN clause for the sitemap query.
*
* @param string $join JOIN clause.
* @param string $post_type Post type.
*/
public function join_clause( $join, $post_type ) {
if ( 'product' !== $post_type ) {
return $join;
}
global $wpdb;
return $join . " INNER JOIN {$wpdb->prefix}postmeta ON p.ID = {$wpdb->prefix}postmeta.post_id ";
}
/**
* Get WHERE clause for the sitemap query.
*
* @param string $where WHERE clause.
* @param string $post_type Post type.
*/
public function where_clause( $where, $post_type ) {
if ( 'product' !== $post_type ) {
return $where;
}
global $wpdb;
if ( $this->exclude_stock_status ) {
$where .= " AND {$wpdb->prefix}postmeta.meta_key = '_stock_status'
AND {$wpdb->prefix}postmeta.meta_value IN ( '" . implode( "', '", $this->exclude_stock_status ) . "' )";
}
if ( $this->noindex_hidden_products ) {
$where .= " AND NOT EXISTS (
SELECT 1 FROM {$wpdb->prefix}term_relationships AS tr
INNER JOIN {$wpdb->prefix}term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN {$wpdb->prefix}terms AS t ON tt.term_id = t.term_id
WHERE tr.object_id = p.ID
AND tt.taxonomy = 'product_visibility'
AND t.slug = 'exclude-from-catalog'
)";
}
return $where;
}
/**
* Filter/Hooks to add GTIN value on Product page.
*/
public function init() {
$this->filter( 'rank_math/frontend/robots', 'robots' );
if ( ! is_product() ) {
return;
}
$this->filter( 'rank_math/snippet/rich_snippet_product_entity', 'add_gtin_in_schema' );
$this->filter( 'rank_math/woocommerce/product_brand', 'add_custom_product_brand' );
$this->filter( 'rank_math/snippet/rich_snippet_product_entity', 'add_variations_data' );
$this->action( 'rank_math/opengraph/facebook', 'og_retailer_id', 60 );
$this->filter( 'rank_math/snippet/rich_snippet_product_entity', 'additional_schema_properties' );
if ( Helper::get_settings( 'general.show_gtin' ) ) {
$this->action( 'woocommerce_product_meta_start', 'add_gtin_meta' );
$this->filter( 'woocommerce_available_variation', 'add_gtin_to_variation_param', 10, 3 );
$this->action( 'wp_footer', 'add_variation_script' );
}
}
/**
* Add carousels
*
* @param array $data Array of JSON-LD data.
* @param JsonLD $jsonld JsonLD Instance.
*/
public function add_carousels( $data, $jsonld ) {
if ( ! isset( $data['ProductsPage'] ) ) {
return $data;
}
$seller = Product::get_seller( $jsonld );
$items = [];
$position = 0;
while ( have_posts() ) {
the_post();
$post_id = get_the_ID();
$product = wc_get_product( $post_id );
if ( empty( $product ) || 'grouped' === $product->get_type() ) {
continue;
}
$single = [
'@type' => 'ListItem',
'position' => ++$position,
'item' => [
'@type' => 'Product',
'name' => $jsonld->get_product_title( $product ),
'url' => $jsonld->get_post_url( $post_id ),
],
];
$images = Product_WooCommerce::get()->get_images( $product );
if ( $images ) {
$single['item']['image'] = $images;
}
$offers = Product_WooCommerce::get()->get_offers( $product, $seller );
if ( $offers ) {
$single['item']['offers'] = $offers;
}
$items[] = $single;
}
wp_reset_postdata();
if ( ! empty( $items ) ) {
unset( $data['ProductsPage'] );
$data['ProductsCarousel'] = [
'@context' => 'https://schema.org/',
'@type' => 'ItemList',
'itemListElement' => $items,
];
}
return $data;
}
/**
* Change robots for WooCommerce pages according to settings
*
* @param array $robots Array of robots to sanitize.
*
* @return array Modified robots.
*/
public function robots( $robots ) {
if ( ! $this->noindex_hidden_products ) {
return $robots;
}
if ( is_product() ) {
$product = \wc_get_product();
$is_hidden = $product && $product->get_catalog_visibility() === 'hidden';
if ( $is_hidden ) {
return [
'noindex' => 'noindex',
'nofollow' => 'nofollow',
];
}
}
global $wp_query;
if ( is_product_taxonomy() && ! $wp_query->post_count && $wp_query->queried_object->count ) {
return [
'noindex' => 'noindex',
'nofollow' => 'nofollow',
];
}
return $robots;
}
/**
* Filter to change Product brand value based on the Settings.
*
* @param string $brand Brand.
*
* @return string Modified brand.
*/
public function add_custom_product_brand( $brand ) {
return 'custom' === Helper::get_settings( 'general.product_brand' ) ? Helper::get_settings( 'general.custom_product_brand' ) : $brand;
}
/**
* Filter to add url, manufacturer & brand url in Product schema.
*
* @param array $entity Snippet Data.
* @return array
*
* @since 2.7.0
*/
public function additional_schema_properties( $entity ) {
if ( ! $this->do_filter( 'schema/woocommerce/additional_properties', false ) ) {
return $entity;
}
$type = 'company' === Helper::get_settings( 'titles.knowledgegraph_type' ) ? 'organization' : 'person';
$entity['manufacturer'] = [ '@id' => home_url( "/#{$type}" ) ];
$entity['url'] = get_the_permalink();
$taxonomy = Helper::get_settings( 'general.product_brand' );
if ( ! empty( $entity['brand'] ) && $taxonomy && taxonomy_exists( $taxonomy ) ) {
$brands = get_the_terms( get_the_ID(), $taxonomy );
$entity['brand']['url'] = is_wp_error( $brands ) || empty( $brands[0] ) ? '' : get_term_link( $brands[0], $taxonomy );
}
return $entity;
}
/**
* Filter to add GTIN in Product schema.
*
* @param array $entity Snippet Data.
* @return array
*/
public function add_gtin_in_schema( $entity ) {
$gtin_key = Helper::get_settings( 'general.gtin', 'gtin8' );
if ( ! empty( $entity[ $gtin_key ] ) ) {
return $entity;
}
global $product;
if ( ! is_object( $product ) ) {
$product = wc_get_product( get_the_ID() );
}
$gtin = $this->get_gtin_value( $product );
if ( $gtin ) {
// Remove the default gtin property added in the Free plugin so it can be overwritten with a selected key from Settings.
if ( isset( $entity['gtin'] ) ) {
unset( $entity['gtin'] );
}
$entity[ $gtin_key ] = $gtin;
}
if ( ! empty( $entity['isbn'] ) ) {
$entity['@type'] = [
'Product',
'Book',
];
}
return $entity;
}
/**
* Add GTIN data in Product metadata.
*/
public function add_gtin_meta() {
global $product;
$gtin_code = $this->get_gtin_value( $product );
if ( ! $gtin_code && ! $this->variations_have_gtin( $product ) ) {
return;
}
$hidden = ! $gtin_code ? 'hidden' : '';
echo '<span class="rank-math-gtin-wrapper" ' . esc_attr( $hidden ) . '>';
echo esc_html( $this->get_formatted_value( $gtin_code ) );
echo '</span>';
}
/**
* Add GTIN value to available variations.
*
* @param array $args Array of variation arguments.
* @param Object $product Current Product Object.
* @param Object $variation Product variation.
*
* @return array Modified robots.
*/
public function add_gtin_to_variation_param( $args, $product, $variation ) {
$gtin = $this->get_gtin_value( $variation );
if ( ! $gtin ) {
return $args;
}
$args['rank_math_gtin'] = $this->get_formatted_value( $gtin );
return $args;
}
/**
* Variation script to change GTIN when variation is changed from the dropdown.
*/
public function add_variation_script() {
global $product;
if ( ! $product->is_type( 'variable' ) ) {
return;
}
$label = $this->get_gtin_label();
?>
<script>
( function () {
const $form = jQuery( '.variations_form' );
const wrapper = jQuery( '.rank-math-gtin-wrapper' );
const gtin_code = wrapper.text();
function toggleAttributes( variation ) {
variation.rank_math_gtin ? wrapper.removeAttr( 'hidden' ) :
wrapper.attr( 'hidden', 'hidden' )
}
if ( $form.length ) {
$form.on( 'found_variation', function( event, variation ) {
toggleAttributes( variation )
if ( variation.rank_math_gtin ) {
wrapper.text( variation.rank_math_gtin );
}
} );
$form.on( 'reset_data', function() {
wrapper.text( gtin_code );
if ( '<?php echo esc_attr( $label ); ?>' === gtin_code ) {
toggleAttributes( { } )
}
} );
}
} )();
</script>
<?php
}
/**
* Filter to add Offers array in Product schema.
*
* @param array $entity Snippet Data.
* @return array
*/
public function add_variations_data( $entity ) {
$product_id = get_the_ID();
$product = wc_get_product( $product_id );
if ( ! $product->is_type( 'variable' ) ) {
return $entity;
}
$schemas = array_filter(
DB::get_schemas( $product_id ),
function ( $schema ) {
return $schema['@type'] === 'WooCommerceProduct';
}
);
if ( empty( $schemas ) && Helper::get_default_schema_type( $product_id ) !== 'WooCommerceProduct' ) {
return $entity;
}
$variations = $product->get_available_variations( 'object' );
if ( empty( $variations ) ) {
return $entity;
}
$entity['@type'] = 'ProductGroup';
$entity['url'] = $product->get_permalink();
$entity['productGroupID'] = ! empty( $entity['sku'] ) ? $entity['sku'] : $product_id;
$this->add_variable_gtin( $product_id, $entity['offers'] );
$variants = [];
foreach ( $variations as $variation ) {
$variants[] = $this->get_variant_data( $variation, $product );
}
$this->add_varies_by( $entity );
$entity['hasVariant'] = $variants;
unset( $entity['offers'] );
return $entity;
}
/**
* Add product retailer ID to the OpenGraph output.
*
* @param OpenGraph $opengraph The current opengraph network object.
*/
public function og_retailer_id( $opengraph ) {
$product = wc_get_product( get_the_ID() );
if ( empty( $product ) || ! $product->get_sku() ) {
return;
}
$opengraph->tag( 'product:retailer_item_id', $product->get_sku() );
}
/**
* Add GTIN migration tool.
*
* @param array $tools Array of tools.
*
* @return array
*/
public function add_gtin_migration_tool( $tools ) {
if ( self::add_gtin_field() ) {
return $tools;
}
$products = Migrate_GTIN::get()->find_posts();
if ( empty( $products ) || get_option( 'rank_math_gtin_migrated' ) ) {
return $tools;
}
$tools['migrate_gtin_values'] = [
'title' => esc_html__( 'GTIN Migration Tool for WooCommerce', 'rank-math-pro' ),
'description' => esc_html__( 'Migrate GTIN values from the plugin into the native WooCommerce GTIN field.', 'rank-math-pro' ),
'button_text' => esc_html__( 'Migrate', 'rank-math-pro' ),
];
return $tools;
}
/**
* Migrate GTIN values from the plugin into the native WooCommerce GTIN field.
*/
public function migrate_gtin_values() {
$products = Migrate_GTIN::get()->find_posts();
if ( empty( $products ) ) {
return [
'status' => 'error',
'message' => __( 'No products found to migrate.', 'rank-math-pro' ),
];
}
Migrate_GTIN::get()->start( $products );
return __( 'The GTIN values from the plugin are being transferred to the built-in WooCommerce GTIN field. This process runs in the background, and you\'ll receive a confirmation message once all product data has been successfully migrated. You can close this page.', 'rank-math-pro' );
}
/**
* Whether to add and use the GTIN value from the Rank Math plugin.
*
* @since 3.0.73
*/
public static function add_gtin_field() {
return apply_filters( 'rank_math/woocommerce/add_gtin_field', false );
}
/**
* Get Variant data.
*
* @param Object $variation Variation Object.
* @param WC_Product $product Product Object.
*
* @since 3.0.57
*/
private function get_variant_data( $variation, $product ) {
$description = $this->get_variant_description( $variation, $product );
$description = $this->do_filter( 'product_description/apply_shortcode', false ) ? do_shortcode( $description ) : Helper::strip_shortcodes( $description );
$variant = [
'@type' => 'Product',
'sku' => $variation->get_sku(),
'name' => $variation->get_name(),
'description' => wp_strip_all_tags( $description, true ),
'image' => wp_get_attachment_image_url( $variation->get_image_id() ),
];
$this->add_variable_attributes( $variation, $variant );
$this->add_variable_offer( $variation, $variant );
$this->add_variable_gtin( $variation->get_id(), $variant );
return $variant;
}
/**
* Add gtin value in variable offer data.
*
* @param int $variation_id Variation ID.
* @param array $entity Offer entity.
*/
private function add_variable_gtin( $variation_id, &$entity ) {
$meta_key = self::add_gtin_field() ? '_rank_math_gtin_code' : '_global_unique_id';
$gtin_key = Helper::get_settings( 'general.gtin', 'gtin8' );
$gtin = get_post_meta( $variation_id, $meta_key, true );
if ( ! $gtin || 'isbn' === $gtin_key ) {
return;
}
$entity[ $gtin_key ] = $gtin;
}
/**
* Get GTIN value from Product object.
*
* @param WC_Product $product Product Object.
*
* @since 3.0.73
*/
private function get_gtin_value( $product ) {
if ( self::add_gtin_field() ) {
return $product->get_meta( '_rank_math_gtin_code' );
}
return method_exists( $product, 'get_global_unique_id' ) ? $product->get_global_unique_id() : '';
}
/**
* Get Variant description.
*
* @param Object $variation Variation Object.
* @param WC_Product $product Product Object.
*
* @since 3.0.61
*/
private function get_variant_description( $variation, $product ) {
if ( $variation->get_description() ) {
return $variation->get_description();
}
return $product->get_short_description() ? $product->get_short_description() : $product->get_description();
}
/**
* Add variesBy property to product data.
*
* @param Object $entity Product data.
*
* @since 3.0.57
*/
private function add_varies_by( &$entity ) {
if ( empty( $this->varies_by ) ) {
return;
}
$valid_values = [
'color' => 'https://schema.org/color',
'size' => 'https://schema.org/size',
'age' => 'https://schema.org/suggestedAge',
'gender' => 'https://schema.org/suggestedGender',
'material' => 'https://schema.org/material',
'pattern' => 'https://schema.org/pattern',
];
$varies_by = [];
foreach ( array_unique( $this->varies_by ) as $attribute ) {
if ( isset( $valid_values[ $attribute ] ) ) {
$varies_by[] = $valid_values[ $attribute ];
}
}
if ( ! empty( $varies_by ) ) {
$entity['variesBy'] = array_unique( $varies_by );
}
}
/**
* Add gtin value in variable offer datta.
*
* @param Object $variation Variation Object.
* @param array $entity Variant entity.
*
* @since 3.0.57
*/
private function add_variable_offer( $variation, &$entity ) {
$price_valid_until = get_post_meta( $variation->get_id(), '_sale_price_dates_to', true );
if ( ! $price_valid_until ) {
$price_valid_until = strtotime( ( date( 'Y' ) + 1 ) . '-12-31' );
}
$entity['offers'] = [
'@type' => 'Offer',
'description' => ! empty( $entity['description'] ) ? $entity['description'] : '',
'price' => wc_get_price_to_display( $variation ),
'priceCurrency' => get_woocommerce_currency(),
'availability' => 'outofstock' === $variation->get_stock_status() ? 'https://schema.org/OutOfStock' : 'https://schema.org/InStock',
'itemCondition' => 'NewCondition',
'priceValidUntil' => date_i18n( 'Y-m-d', $price_valid_until ),
'url' => $variation->get_permalink(),
];
}
/**
* Add attributes value in variable offer datta.
*
* @param Object $variation Variation Object.
* @param array $variant Variant entity.
*
* @since 3.0.57
*/
private function add_variable_attributes( $variation, &$variant ) {
if ( empty( $variation->get_attributes() ) ) {
return;
}
foreach ( $variation->get_attributes() as $key => $value ) {
if ( ! $value ) {
continue;
}
$key = str_replace( 'pa_', '', $key );
if ( ! in_array( $key, [ 'color', 'size', 'material', 'pattern', 'weight' ], true ) ) {
continue;
}
$variant[ $key ] = $value;
$this->varies_by[] = $key;
}
}
/**
* Get formatted GTIN value with label.
*
* @param string $gtin GTIN code.
*
* @return string Formatted GTIN value with label.
*/
private function get_formatted_value( $gtin ) {
return esc_html( $this->get_gtin_label() . $gtin );
}
/**
* Checks if any of the variations have a gtin value.
*
* @param Object $product The WC Product object.
*
* @return bool
*/
private function variations_have_gtin( $product ) {
if ( ! $product->has_child() ) {
return false;
}
$args = [
'parent' => $product->get_id(),
'type' => 'variation',
'visibility' => 'visible',
];
$has_gtin = array_filter(
wc_get_products( $args ),
function ( \WC_Product_Variation $variation ) {
return ! empty( self::get_gtin_value( $variation ) );
}
);
return ! empty( $has_gtin );
}
/**
* Get GTIN label.
*
* @return string The GTIN label.
*/
private function get_gtin_label() {
$label = Helper::get_settings( 'general.gtin_label' );
$label = $label ? $label . ' ' : '';
return $this->do_filter( 'woocommerce/gtin_label', $label );
}
}