1071 lines
31 KiB
PHP
1071 lines
31 KiB
PHP
<?php
|
|
|
|
declare( strict_types = 1);
|
|
|
|
use Automattic\Jetpack\Constants;
|
|
|
|
/**
|
|
* WC_Brands class.
|
|
*
|
|
* Important: For internal use only by the Automattic\WooCommerce\Internal\Brands package.
|
|
*
|
|
* @version 9.5.0
|
|
*/
|
|
class WC_Brands {
|
|
|
|
/**
|
|
* Template URL -- filterable.
|
|
*
|
|
* @var mixed|null
|
|
*/
|
|
public $template_url;
|
|
|
|
/**
|
|
* __construct function.
|
|
*/
|
|
public function __construct() {
|
|
$this->template_url = apply_filters( 'woocommerce_template_url', 'woocommerce/' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
|
|
|
add_action( 'plugins_loaded', array( $this, 'register_hooks' ), 11 );
|
|
|
|
$this->register_shortcodes();
|
|
}
|
|
|
|
/**
|
|
* Register our hooks
|
|
*/
|
|
public function register_hooks() {
|
|
add_action( 'woocommerce_register_taxonomy', array( __CLASS__, 'init_taxonomy' ) );
|
|
add_action( 'widgets_init', array( $this, 'init_widgets' ) );
|
|
|
|
if ( ! wc_current_theme_is_fse_theme() ) {
|
|
add_filter( 'template_include', array( $this, 'template_loader' ) );
|
|
}
|
|
|
|
add_action( 'wp_enqueue_scripts', array( $this, 'styles' ) );
|
|
add_action( 'wp', array( $this, 'body_class' ) );
|
|
|
|
add_action( 'woocommerce_product_meta_end', array( $this, 'show_brand' ) );
|
|
add_filter( 'woocommerce_structured_data_product', array( $this, 'add_structured_data' ), 20 );
|
|
|
|
// duplicate product brands.
|
|
add_action( 'woocommerce_product_duplicate_before_save', array( $this, 'duplicate_store_temporary_brands' ), 10, 2 );
|
|
add_action( 'woocommerce_new_product', array( $this, 'duplicate_add_product_brand_terms' ) );
|
|
add_action( 'woocommerce_new_product', array( $this, 'invalidate_wc_layered_nav_counts_cache' ), 10, 0 );
|
|
add_action( 'woocommerce_update_product', array( $this, 'invalidate_wc_layered_nav_counts_cache' ), 10, 0 );
|
|
add_action( 'transition_post_status', array( $this, 'reset_layered_nav_counts_on_status_change' ), 10, 3 );
|
|
|
|
add_filter( 'post_type_link', array( $this, 'post_type_link' ), 11, 2 );
|
|
|
|
if ( 'yes' === get_option( 'wc_brands_show_description' ) ) {
|
|
add_action( 'woocommerce_archive_description', array( $this, 'brand_description' ) );
|
|
}
|
|
|
|
add_filter( 'woocommerce_product_query_tax_query', array( $this, 'update_product_query_tax_query' ), 10, 1 );
|
|
|
|
// REST API.
|
|
add_action( 'rest_api_init', array( $this, 'rest_api_register_routes' ) );
|
|
add_action( 'woocommerce_rest_insert_product', array( $this, 'rest_api_maybe_set_brands' ), 10, 2 );
|
|
add_filter( 'woocommerce_rest_prepare_product', array( $this, 'rest_api_prepare_brands_to_product' ), 10, 2 ); // WC 2.6.x.
|
|
add_filter( 'woocommerce_rest_prepare_product_object', array( $this, 'rest_api_prepare_brands_to_product' ), 10, 2 ); // WC 3.x.
|
|
add_action( 'woocommerce_rest_insert_product', array( $this, 'rest_api_add_brands_to_product' ), 10, 3 ); // WC 2.6.x.
|
|
add_action( 'woocommerce_rest_insert_product_object', array( $this, 'rest_api_add_brands_to_product' ), 10, 3 ); // WC 3.x.
|
|
add_filter( 'woocommerce_rest_product_object_query', array( $this, 'rest_api_filter_products_by_brand' ), 10, 2 );
|
|
add_filter( 'rest_product_collection_params', array( $this, 'rest_api_product_collection_params' ), 10, 2 );
|
|
|
|
// Layered nav widget compatibility.
|
|
add_filter( 'woocommerce_layered_nav_term_html', array( $this, 'woocommerce_brands_update_layered_nav_link' ), 10, 4 );
|
|
|
|
// Filter the list of taxonomies overridden for the original term count.
|
|
add_filter( 'woocommerce_change_term_counts', array( $this, 'add_brands_to_terms' ) );
|
|
add_action( 'woocommerce_product_set_stock_status', array( $this, 'recount_after_stock_change' ) );
|
|
add_action( 'woocommerce_update_options_products_inventory', array( $this, 'recount_all_brands' ) );
|
|
|
|
// Product Editor compatibility.
|
|
add_action( 'woocommerce_layout_template_after_instantiation', array( $this, 'wc_brands_on_block_template_register' ), 10, 3 );
|
|
}
|
|
|
|
/**
|
|
* Add product_brand to the taxonomies overridden for the original term count.
|
|
*
|
|
* @param array $taxonomies List of taxonomies.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function add_brands_to_terms( $taxonomies ) {
|
|
$taxonomies[] = 'product_brand';
|
|
return $taxonomies;
|
|
}
|
|
|
|
/**
|
|
* Recount the brands after the stock amount changes.
|
|
*
|
|
* @param int $product_id Product ID.
|
|
*/
|
|
public function recount_after_stock_change( $product_id ) {
|
|
if ( 'yes' !== get_option( 'woocommerce_hide_out_of_stock_items' ) || empty( $product_id ) ) {
|
|
return;
|
|
}
|
|
|
|
$product_terms = get_the_terms( $product_id, 'product_brand' );
|
|
|
|
if ( $product_terms ) {
|
|
$product_brands = array();
|
|
|
|
foreach ( $product_terms as $term ) {
|
|
$product_brands[ $term->term_id ] = $term->parent;
|
|
}
|
|
|
|
_wc_term_recount( $product_brands, get_taxonomy( 'product_brand' ), false, false );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recount all brands.
|
|
*/
|
|
public function recount_all_brands() {
|
|
$product_brands = get_terms(
|
|
array(
|
|
'taxonomy' => 'product_brand',
|
|
'hide_empty' => false,
|
|
'fields' => 'id=>parent',
|
|
)
|
|
);
|
|
_wc_term_recount( $product_brands, get_taxonomy( 'product_brand' ), true, false );
|
|
}
|
|
|
|
/**
|
|
* Update the main product fetch query to filter by selected brands.
|
|
*
|
|
* @param array $tax_query array of current taxonomy filters.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function update_product_query_tax_query( array $tax_query ) {
|
|
if ( isset( $_GET['filter_product_brand'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$filter_product_brand = wc_clean( wp_unslash( $_GET['filter_product_brand'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$brands_filter = array_filter( array_map( 'absint', explode( ',', $filter_product_brand ) ) );
|
|
|
|
if ( $brands_filter ) {
|
|
$tax_query[] = array(
|
|
'taxonomy' => 'product_brand',
|
|
'terms' => $brands_filter,
|
|
'operator' => 'IN',
|
|
);
|
|
}
|
|
}
|
|
|
|
return $tax_query;
|
|
}
|
|
|
|
/**
|
|
* Filter to allow product_brand in the permalinks for products.
|
|
*
|
|
* @param string $permalink The existing permalink URL.
|
|
* @param WP_Post $post The post.
|
|
* @return string
|
|
*/
|
|
public function post_type_link( $permalink, $post ) {
|
|
// Abort if post is not a product.
|
|
if ( 'product' !== $post->post_type ) {
|
|
return $permalink;
|
|
}
|
|
|
|
// Abort early if the placeholder rewrite tag isn't in the generated URL.
|
|
if ( false === strpos( $permalink, '%' ) ) {
|
|
return $permalink;
|
|
}
|
|
|
|
// Get the custom taxonomy terms in use by this post.
|
|
$terms = get_the_terms( $post->ID, 'product_brand' );
|
|
|
|
if ( empty( $terms ) ) {
|
|
// If no terms are assigned to this post, use a string instead (can't leave the placeholder there).
|
|
$product_brand = _x( 'uncategorized', 'slug', 'woocommerce' );
|
|
} else {
|
|
// Replace the placeholder rewrite tag with the first term's slug.
|
|
$first_term = array_shift( $terms );
|
|
$product_brand = $first_term->slug;
|
|
}
|
|
|
|
$find = array(
|
|
'%product_brand%',
|
|
);
|
|
|
|
$replace = array(
|
|
$product_brand,
|
|
);
|
|
|
|
$replace = array_map( 'sanitize_title', $replace );
|
|
|
|
$permalink = str_replace( $find, $replace, $permalink );
|
|
|
|
return $permalink;
|
|
}
|
|
|
|
/**
|
|
* Adds filter for introducing CSS classes.
|
|
*/
|
|
public function body_class() {
|
|
if ( is_tax( 'product_brand' ) ) {
|
|
add_filter( 'body_class', array( $this, 'add_body_class' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds classes to brand taxonomy pages.
|
|
*
|
|
* @param array $classes Classes array.
|
|
*/
|
|
public function add_body_class( $classes ) {
|
|
$classes[] = 'woocommerce';
|
|
$classes[] = 'woocommerce-page';
|
|
return $classes;
|
|
}
|
|
|
|
/**
|
|
* Enqueues styles.
|
|
*/
|
|
public function styles() {
|
|
$version = Constants::get_constant( 'WC_VERSION' );
|
|
wp_enqueue_style( 'brands-styles', WC()->plugin_url() . '/assets/css/brands.css', array(), $version );
|
|
}
|
|
|
|
/**
|
|
* Initializes brand taxonomy.
|
|
*/
|
|
public static function init_taxonomy() {
|
|
$shop_page_id = wc_get_page_id( 'shop' );
|
|
|
|
$base_slug = $shop_page_id > 0 && get_page( $shop_page_id ) ? get_page_uri( $shop_page_id ) : 'shop';
|
|
$category_base = get_option( 'woocommerce_prepend_shop_page_to_urls' ) === 'yes' ? trailingslashit( $base_slug ) : '';
|
|
|
|
$slug = $category_base . __( 'brand', 'woocommerce' );
|
|
if ( '' === $category_base ) {
|
|
$slug = get_option( 'woocommerce_brand_permalink', '' );
|
|
}
|
|
|
|
// Can't provide transatable string as get_option default.
|
|
if ( '' === $slug ) {
|
|
$slug = __( 'brand', 'woocommerce' );
|
|
}
|
|
|
|
register_taxonomy(
|
|
'product_brand',
|
|
array( 'product' ),
|
|
/**
|
|
* Filter the brand taxonomy.
|
|
*
|
|
* @since 9.4.0
|
|
*
|
|
* @param array $args Args.
|
|
*/
|
|
apply_filters(
|
|
'register_taxonomy_product_brand',
|
|
array(
|
|
'hierarchical' => true,
|
|
'update_count_callback' => '_update_post_term_count',
|
|
'label' => __( 'Brands', 'woocommerce' ),
|
|
'labels' => array(
|
|
'name' => __( 'Brands', 'woocommerce' ),
|
|
'singular_name' => __( 'Brand', 'woocommerce' ),
|
|
'template_name' => _x( 'Products by Brand', 'Template name', 'woocommerce' ),
|
|
'search_items' => __( 'Search Brands', 'woocommerce' ),
|
|
'all_items' => __( 'All Brands', 'woocommerce' ),
|
|
'parent_item' => __( 'Parent Brand', 'woocommerce' ),
|
|
'parent_item_colon' => __( 'Parent Brand:', 'woocommerce' ),
|
|
'edit_item' => __( 'Edit Brand', 'woocommerce' ),
|
|
'update_item' => __( 'Update Brand', 'woocommerce' ),
|
|
'add_new_item' => __( 'Add New Brand', 'woocommerce' ),
|
|
'new_item_name' => __( 'New Brand Name', 'woocommerce' ),
|
|
'not_found' => __( 'No Brands Found', 'woocommerce' ),
|
|
'back_to_items' => __( '← Go to Brands', 'woocommerce' ),
|
|
),
|
|
|
|
'show_ui' => true,
|
|
'show_admin_column' => true,
|
|
'show_in_nav_menus' => true,
|
|
'show_in_rest' => true,
|
|
'capabilities' => array(
|
|
'manage_terms' => 'manage_product_terms',
|
|
'edit_terms' => 'edit_product_terms',
|
|
'delete_terms' => 'delete_product_terms',
|
|
'assign_terms' => 'assign_product_terms',
|
|
),
|
|
|
|
'rewrite' => array(
|
|
'slug' => $slug,
|
|
'with_front' => false,
|
|
'hierarchical' => true,
|
|
),
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Initializes brand widgets.
|
|
*/
|
|
public function init_widgets() {
|
|
// Include.
|
|
require_once WC()->plugin_path() . '/includes/widgets/class-wc-widget-brand-description.php';
|
|
require_once WC()->plugin_path() . '/includes/widgets/class-wc-widget-brand-nav.php';
|
|
require_once WC()->plugin_path() . '/includes/widgets/class-wc-widget-brand-thumbnails.php';
|
|
|
|
// Register.
|
|
register_widget( 'WC_Widget_Brand_Description' );
|
|
register_widget( 'WC_Widget_Brand_Nav' );
|
|
register_widget( 'WC_Widget_Brand_Thumbnails' );
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Handles template usage so that we can use our own templates instead of the themes.
|
|
*
|
|
* Templates are in the 'templates' folder. woocommerce looks for theme
|
|
* overides in /theme/woocommerce/ by default
|
|
*
|
|
* For beginners, it also looks for a woocommerce.php template first. If the user adds
|
|
* this to the theme (containing a woocommerce() inside) this will be used for all
|
|
* woocommerce templates.
|
|
*
|
|
* @param string $template Template.
|
|
*/
|
|
public function template_loader( $template ) {
|
|
$find = array( 'woocommerce.php' );
|
|
$file = '';
|
|
|
|
if ( is_tax( 'product_brand' ) ) {
|
|
|
|
$term = get_queried_object();
|
|
|
|
$file = 'taxonomy-' . $term->taxonomy . '.php';
|
|
$find[] = 'taxonomy-' . $term->taxonomy . '-' . $term->slug . '.php';
|
|
$find[] = $this->template_url . 'taxonomy-' . $term->taxonomy . '-' . $term->slug . '.php';
|
|
$find[] = $file;
|
|
$find[] = $this->template_url . $file;
|
|
|
|
}
|
|
|
|
if ( $file ) {
|
|
$template = locate_template( $find );
|
|
if ( ! $template ) {
|
|
$template = WC()->plugin_path() . '/templates/brands/' . $file;
|
|
}
|
|
}
|
|
|
|
return $template;
|
|
}
|
|
|
|
/**
|
|
* Displays brand description.
|
|
*/
|
|
public function brand_description() {
|
|
if ( ! is_tax( 'product_brand' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! get_query_var( 'term' ) ) {
|
|
return;
|
|
}
|
|
|
|
$thumbnail = '';
|
|
|
|
$term = get_term_by( 'slug', get_query_var( 'term' ), 'product_brand' );
|
|
$thumbnail = wc_get_brand_thumbnail_url( $term->term_id, 'full' );
|
|
|
|
wc_get_template(
|
|
'brand-description.php',
|
|
array(
|
|
'thumbnail' => $thumbnail,
|
|
),
|
|
'woocommerce',
|
|
WC()->plugin_path() . '/templates/brands/'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Displays brand.
|
|
*/
|
|
public function show_brand() {
|
|
global $post;
|
|
|
|
if ( is_singular( 'product' ) ) {
|
|
$terms = get_the_terms( $post->ID, 'product_brand' );
|
|
$brand_count = is_array( $terms ) ? count( $terms ) : 0;
|
|
|
|
$taxonomy = get_taxonomy( 'product_brand' );
|
|
$labels = $taxonomy->labels;
|
|
|
|
/* translators: %s - Label name */
|
|
echo wc_get_brands( $post->ID, ', ', ' <span class="posted_in">' . sprintf( _n( '%s: ', '%s: ', $brand_count, 'woocommerce' ), $labels->singular_name, $labels->name ), '</span>' ); // phpcs:ignore WordPress.Security.EscapeOutput
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add structured data to product page.
|
|
*
|
|
* @param array $markup Markup.
|
|
* @return array $markup
|
|
*/
|
|
public function add_structured_data( $markup ) {
|
|
global $post;
|
|
|
|
if ( array_key_exists( 'brand', $markup ) ) {
|
|
return $markup;
|
|
}
|
|
|
|
$brands = get_the_terms( $post->ID, 'product_brand' );
|
|
|
|
if ( ! empty( $brands ) && is_array( $brands ) ) {
|
|
// Can only return one brand, so pick the first.
|
|
$markup['brand'] = array(
|
|
'@type' => 'Brand',
|
|
'name' => $brands[0]->name,
|
|
);
|
|
}
|
|
|
|
return $markup;
|
|
}
|
|
|
|
/**
|
|
* Registers shortcodes.
|
|
*/
|
|
public function register_shortcodes() {
|
|
add_shortcode( 'product_brand', array( $this, 'output_product_brand' ) );
|
|
add_shortcode( 'product_brand_thumbnails', array( $this, 'output_product_brand_thumbnails' ) );
|
|
add_shortcode( 'product_brand_thumbnails_description', array( $this, 'output_product_brand_thumbnails_description' ) );
|
|
add_shortcode( 'product_brand_list', array( $this, 'output_product_brand_list' ) );
|
|
add_shortcode( 'brand_products', array( $this, 'output_brand_products' ) );
|
|
}
|
|
|
|
/**
|
|
* Displays product brand.
|
|
*
|
|
* @param array $atts Attributes from the shortcode.
|
|
* @return string The generated output.
|
|
*/
|
|
public function output_product_brand( $atts ) {
|
|
global $post;
|
|
|
|
$args = shortcode_atts(
|
|
array(
|
|
'width' => '',
|
|
'height' => '',
|
|
'class' => 'aligncenter',
|
|
'post_id' => '',
|
|
),
|
|
$atts
|
|
);
|
|
|
|
if ( ! $args['post_id'] && ! $post ) {
|
|
return '';
|
|
}
|
|
|
|
if ( ! $args['post_id'] ) {
|
|
$args['post_id'] = $post->ID;
|
|
}
|
|
|
|
$brands = wp_get_post_terms( $args['post_id'], 'product_brand', array( 'fields' => 'ids' ) );
|
|
|
|
// Bail early if we don't have any brands registered.
|
|
if ( 0 === count( $brands ) ) {
|
|
return '';
|
|
}
|
|
|
|
ob_start();
|
|
|
|
foreach ( $brands as $brand ) {
|
|
$thumbnail = wc_get_brand_thumbnail_url( $brand );
|
|
if ( empty( $thumbnail ) ) {
|
|
continue;
|
|
}
|
|
|
|
$args['thumbnail'] = $thumbnail;
|
|
$args['term'] = get_term_by( 'id', $brand, 'product_brand' );
|
|
|
|
if ( $args['width'] || $args['height'] ) {
|
|
$args['width'] = ! empty( $args['width'] ) ? $args['width'] : 'auto';
|
|
$args['height'] = ! empty( $args['height'] ) ? $args['height'] : 'auto';
|
|
}
|
|
|
|
wc_get_template(
|
|
'shortcodes/single-brand.php',
|
|
$args,
|
|
'woocommerce',
|
|
WC()->plugin_path() . '/templates/brands/'
|
|
);
|
|
}
|
|
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Displays product brand list.
|
|
*
|
|
* @param array $atts Attributes from the shortcode.
|
|
* @return string
|
|
*/
|
|
public function output_product_brand_list( $atts ) {
|
|
$args = shortcode_atts(
|
|
array(
|
|
'show_top_links' => true,
|
|
'show_empty' => true,
|
|
'show_empty_brands' => false,
|
|
),
|
|
$atts
|
|
);
|
|
|
|
$show_top_links = $args['show_top_links'];
|
|
$show_empty = $args['show_empty'];
|
|
$show_empty_brands = $args['show_empty_brands'];
|
|
|
|
if ( 'false' === $show_top_links ) {
|
|
$show_top_links = false;
|
|
}
|
|
|
|
if ( 'false' === $show_empty ) {
|
|
$show_empty = false;
|
|
}
|
|
|
|
if ( 'false' === $show_empty_brands ) {
|
|
$show_empty_brands = false;
|
|
}
|
|
|
|
$product_brands = array();
|
|
//phpcs:disable
|
|
$terms = get_terms( array( 'taxonomy' => 'product_brand', 'hide_empty' => ( $show_empty_brands ? false : true ) ) );
|
|
$alphabet = apply_filters( 'woocommerce_brands_list_alphabet', range( 'a', 'z' ) );
|
|
$numbers = apply_filters( 'woocommerce_brands_list_numbers', '0-9' );
|
|
|
|
/**
|
|
* Check for empty brands and remove them from the list.
|
|
*/
|
|
if ( ! $show_empty_brands ) {
|
|
$terms = $this->remove_terms_with_empty_products( $terms );
|
|
}
|
|
|
|
foreach ( $terms as $term ) {
|
|
$term_letter = $this->get_brand_name_first_character( $term->name );
|
|
|
|
// Allow a locale to be set for ctype_alpha().
|
|
if ( has_filter( 'woocommerce_brands_list_locale' ) ) {
|
|
setLocale( LC_CTYPE, apply_filters( 'woocommerce_brands_list_locale', 'en_US.UTF-8' ) );
|
|
}
|
|
|
|
if ( ctype_alpha( $term_letter ) ) {
|
|
|
|
foreach ( $alphabet as $i ) {
|
|
if ( $i == $term_letter ) {
|
|
$product_brands[ $i ][] = $term;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
$product_brands[ $numbers ][] = $term;
|
|
}
|
|
}
|
|
|
|
ob_start();
|
|
|
|
wc_get_template(
|
|
'shortcodes/brands-a-z.php',
|
|
array(
|
|
'terms' => $terms,
|
|
'index' => array_merge( $alphabet, array( $numbers ) ),
|
|
'product_brands' => $product_brands,
|
|
'show_empty' => $show_empty,
|
|
'show_top_links' => $show_top_links,
|
|
),
|
|
'woocommerce',
|
|
WC()->plugin_path() . '/templates/brands/'
|
|
);
|
|
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Get the first letter of the brand name, returning lowercase and without accents.
|
|
*
|
|
* @param string $name
|
|
*
|
|
* @return string
|
|
* @since 9.4.0
|
|
*/
|
|
private function get_brand_name_first_character( $name ) {
|
|
// Convert to lowercase and remove accents.
|
|
$clean_name = strtolower( sanitize_title( $name ) );
|
|
// Return the first letter of the name.
|
|
return substr( $clean_name, 0, 1 );
|
|
}
|
|
|
|
/**
|
|
* Displays brand thumbnails.
|
|
*
|
|
* @param mixed $atts
|
|
* @return void
|
|
*/
|
|
public function output_product_brand_thumbnails( $atts ) {
|
|
$args = shortcode_atts(
|
|
array(
|
|
'show_empty' => true,
|
|
'columns' => 4,
|
|
'hide_empty' => 0,
|
|
'orderby' => 'name',
|
|
'exclude' => '',
|
|
'number' => '',
|
|
'fluid_columns' => false,
|
|
),
|
|
$atts
|
|
);
|
|
|
|
$exclude = array_map( 'intval', explode( ',', $args['exclude'] ) );
|
|
$order = 'name' === $args['orderby'] ? 'asc' : 'desc';
|
|
|
|
if ( 'true' === $args['show_empty'] ) {
|
|
$hide_empty = false;
|
|
} else {
|
|
$hide_empty = true;
|
|
}
|
|
|
|
$brands = get_terms(
|
|
'product_brand',
|
|
array(
|
|
'hide_empty' => $hide_empty,
|
|
'orderby' => $args['orderby'],
|
|
'exclude' => $exclude,
|
|
'number' => $args['number'],
|
|
'order' => $order,
|
|
)
|
|
);
|
|
|
|
if ( ! $brands ) {
|
|
return;
|
|
}
|
|
|
|
if ( $hide_empty ) {
|
|
$brands = $this->remove_terms_with_empty_products( $brands );
|
|
}
|
|
|
|
ob_start();
|
|
|
|
wc_get_template(
|
|
'widgets/brand-thumbnails.php',
|
|
array(
|
|
'brands' => $brands,
|
|
'columns' => is_numeric( $args['columns'] ) ? intval( $args['columns'] ) : 4,
|
|
'fluid_columns' => wp_validate_boolean( $args['fluid_columns'] ),
|
|
),
|
|
'woocommerce',
|
|
WC()->plugin_path() . '/templates/brands/'
|
|
);
|
|
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Displays brand thumbnails description.
|
|
*
|
|
* @param mixed $atts
|
|
* @return void
|
|
*/
|
|
public function output_product_brand_thumbnails_description( $atts ) {
|
|
$args = shortcode_atts(
|
|
array(
|
|
'show_empty' => true,
|
|
'columns' => 1,
|
|
'hide_empty' => 0,
|
|
'orderby' => 'name',
|
|
'exclude' => '',
|
|
'number' => '',
|
|
),
|
|
$atts
|
|
);
|
|
|
|
$exclude = array_map( 'intval', explode( ',', $args['exclude'] ) );
|
|
$order = 'name' === $args['orderby'] ? 'asc' : 'desc';
|
|
|
|
if ( 'true' === $args['show_empty'] ) {
|
|
$hide_empty = false;
|
|
} else {
|
|
$hide_empty = true;
|
|
}
|
|
|
|
$brands = get_terms(
|
|
'product_brand',
|
|
array(
|
|
'hide_empty' => $args['hide_empty'],
|
|
'orderby' => $args['orderby'],
|
|
'exclude' => $exclude,
|
|
'number' => $args['number'],
|
|
'order' => $order,
|
|
)
|
|
);
|
|
|
|
if ( ! $brands ) {
|
|
return;
|
|
}
|
|
|
|
if ( $hide_empty ) {
|
|
$brands = $this->remove_terms_with_empty_products( $brands );
|
|
}
|
|
|
|
ob_start();
|
|
|
|
wc_get_template(
|
|
'widgets/brand-thumbnails-description.php',
|
|
array(
|
|
'brands' => $brands,
|
|
'columns' => $args['columns'],
|
|
),
|
|
'woocommerce',
|
|
WC()->plugin_path() . '/templates/brands/'
|
|
);
|
|
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Displays brand products.
|
|
*
|
|
* @param array $atts
|
|
* @return string
|
|
*/
|
|
public function output_brand_products( $atts ) {
|
|
if ( empty( $atts['brand'] ) ) {
|
|
return '';
|
|
}
|
|
|
|
// Add the brand attributes and query arguments.
|
|
add_filter( 'shortcode_atts_brand_products', array( __CLASS__, 'add_brand_products_shortcode_atts' ), 10, 4 );
|
|
add_filter( 'woocommerce_shortcode_products_query', array( __CLASS__, 'get_brand_products_query_args' ), 10, 3 );
|
|
|
|
$shortcode = new WC_Shortcode_Products( $atts, 'brand_products' );
|
|
|
|
// Remove the brand attributes and query arguments.
|
|
remove_filter( 'shortcode_atts_brand_products', array( __CLASS__, 'add_brand_products_shortcode_atts' ), 10 );
|
|
remove_filter( 'woocommerce_shortcode_products_query', array( __CLASS__, 'get_brand_products_query_args' ), 10 );
|
|
|
|
return $shortcode->get_content();
|
|
}
|
|
|
|
/**
|
|
* Adds the taxonomy query to the WooCommerce products shortcode query arguments.
|
|
*
|
|
* @param array $query_args
|
|
* @param array $attributes
|
|
* @param string $type
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_brand_products_query_args( $query_args, $attributes, $type ) {
|
|
if ( 'brand_products' !== $type || empty( $attributes['brand'] ) ) {
|
|
return $query_args;
|
|
}
|
|
|
|
$query_args['tax_query'][] = array(
|
|
'taxonomy' => 'product_brand',
|
|
'terms' => array_map( 'sanitize_title', explode( ',', $attributes['brand'] ) ),
|
|
'field' => 'slug',
|
|
'operator' => 'IN',
|
|
);
|
|
|
|
return $query_args;
|
|
}
|
|
|
|
/**
|
|
* Adds the "brand" attribute to the list of WooCommerce products shortcode attributes.
|
|
*
|
|
* @param array $out The output array of shortcode attributes.
|
|
* @param array $pairs The supported attributes and their defaults.
|
|
* @param array $atts The user defined shortcode attributes.
|
|
* @param string $shortcode The shortcode name.
|
|
*
|
|
* @return array The output array of shortcode attributes.
|
|
*/
|
|
public static function add_brand_products_shortcode_atts( $out, $pairs, $atts, $shortcode ) {
|
|
$out['brand'] = array_key_exists( 'brand', $atts ) ? $atts['brand'] : '';
|
|
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Register REST API route for /products/brands.
|
|
*
|
|
* @since 9.4.0
|
|
*
|
|
* @return void
|
|
*/
|
|
public function rest_api_register_routes() {
|
|
require_once WC()->plugin_path() . '/includes/rest-api/Controllers/Version2/class-wc-rest-product-brands-v2-controller.php';
|
|
require_once WC()->plugin_path() . '/includes/rest-api/Controllers/Version3/class-wc-rest-product-brands-controller.php';
|
|
|
|
$controllers = array(
|
|
'WC_REST_Product_Brands_V2_Controller',
|
|
'WC_REST_Product_Brands_Controller'
|
|
);
|
|
|
|
foreach ( $controllers as $controller ) {
|
|
( new $controller() )->register_routes();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maybe set brands when requesting PUT /products/<id>.
|
|
*
|
|
* @since 9.4.0
|
|
*
|
|
* @param WP_Post $post Post object
|
|
* @param WP_REST_Request $request Request object
|
|
*
|
|
* @return void
|
|
*/
|
|
public function rest_api_maybe_set_brands( $post, $request ) {
|
|
if ( isset( $request['brands'] ) && is_array( $request['brands'] ) ) {
|
|
$terms = array_map( 'absint', $request['brands'] );
|
|
wp_set_object_terms( $post->ID, $terms, 'product_brand' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prepare brands in product response.
|
|
*
|
|
* @param WP_REST_Response $response The response object.
|
|
* @param WP_Post|WC_Data $post Post object or WC object.
|
|
* @version 9.4.0
|
|
* @return WP_REST_Response
|
|
*/
|
|
public function rest_api_prepare_brands_to_product( $response, $post ) {
|
|
$post_id = is_callable( array( $post, 'get_id' ) ) ? $post->get_id() : ( ! empty( $post->ID ) ? $post->ID : null );
|
|
|
|
if ( empty( $response->data['brands'] ) ) {
|
|
$terms = array();
|
|
|
|
foreach ( wp_get_post_terms( $post_id, 'product_brand' ) as $term ) {
|
|
$terms[] = array(
|
|
'id' => $term->term_id,
|
|
'name' => $term->name,
|
|
'slug' => $term->slug,
|
|
);
|
|
}
|
|
|
|
$response->data['brands'] = $terms;
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Add brands in product response.
|
|
*
|
|
* @param WC_Data $product Inserted product object.
|
|
* @param WP_REST_Request $request Request object.
|
|
* @param boolean $creating True when creating object, false when updating.
|
|
* @version 9.4.0
|
|
*/
|
|
public function rest_api_add_brands_to_product( $product, $request, $creating = true ) {
|
|
$product_id = is_callable( array( $product, 'get_id' ) ) ? $product->get_id() : ( ! empty( $product->ID ) ? $product->ID : null );
|
|
$params = $request->get_params();
|
|
$brands = isset( $params['brands'] ) ? $params['brands'] : array();
|
|
|
|
if ( ! empty( $brands ) ) {
|
|
if ( is_array( $brands[0] ) && array_key_exists( 'id', $brands[0] ) ) {
|
|
$brands = array_map(
|
|
function ( $brand ) {
|
|
return absint( $brand['id'] );
|
|
},
|
|
$brands
|
|
);
|
|
} else {
|
|
$brands = array_map( 'absint', $brands );
|
|
}
|
|
wp_set_object_terms( $product_id, $brands, 'product_brand' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filters products by taxonomy product_brand.
|
|
*
|
|
* @param array $args Request args.
|
|
* @param WP_REST_Request $request Request data.
|
|
* @return array Request args.
|
|
* @version 9.4.0
|
|
*/
|
|
public function rest_api_filter_products_by_brand( $args, $request ) {
|
|
if ( ! empty( $request['brand'] ) ) {
|
|
$args['tax_query'][] = array(
|
|
'taxonomy' => 'product_brand',
|
|
'field' => 'term_id',
|
|
'terms' => $request['brand'],
|
|
);
|
|
}
|
|
|
|
return $args;
|
|
}
|
|
|
|
/**
|
|
* Documents additional query params for collections of products.
|
|
*
|
|
* @param array $params JSON Schema-formatted collection parameters.
|
|
* @param WP_Post_Type $post_type Post type object.
|
|
* @return array JSON Schema-formatted collection parameters.
|
|
* @version 9.4.0
|
|
*/
|
|
public function rest_api_product_collection_params( $params, $post_type ) {
|
|
$params['brand'] = array(
|
|
'description' => __( 'Limit result set to products assigned a specific brand ID.', 'woocommerce' ),
|
|
'type' => 'string',
|
|
'sanitize_callback' => 'wp_parse_id_list',
|
|
'validate_callback' => 'rest_validate_request_arg',
|
|
);
|
|
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* Injects Brands filters into layered nav links.
|
|
*
|
|
* @param string $term_html Original link html.
|
|
* @param mixed $term Term that is currently added.
|
|
* @param string $link Original layered nav item link.
|
|
* @param number $count Number of items in that filter.
|
|
* @return string Term html.
|
|
* @version 9.4.0
|
|
*/
|
|
public function woocommerce_brands_update_layered_nav_link( $term_html, $term, $link, $count ) {
|
|
if ( empty( $_GET['filter_product_brand'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
return $term_html;
|
|
}
|
|
|
|
$filter_product_brand = wc_clean( wp_unslash( $_GET['filter_product_brand'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$current_attributes = array_map( 'intval', explode( ',', $filter_product_brand ) );
|
|
$current_values = ! empty( $current_attributes ) ? $current_attributes : array();
|
|
$link = add_query_arg(
|
|
array(
|
|
'filtering' => '1',
|
|
'filter_product_brand' => implode( ',', $current_values ),
|
|
),
|
|
wp_specialchars_decode( $link )
|
|
);
|
|
$term_html = '<a rel="nofollow" href="' . esc_url( $link ) . '">' . esc_html( $term->name ) . '</a>';
|
|
$term_html .= ' ' . apply_filters( 'woocommerce_layered_nav_count', '<span class="count">(' . absint( $count ) . ')</span>', $count, $term );
|
|
return $term_html;
|
|
}
|
|
|
|
/**
|
|
* Temporarily tag a post with meta before it is saved in order
|
|
* to allow us to be able to use the meta when the product is saved to add
|
|
* the brands when an ID has been generated.
|
|
*
|
|
*
|
|
* @param WC_Product $duplicate
|
|
* @return WC_Product $original
|
|
*/
|
|
public function duplicate_store_temporary_brands( $duplicate, $original ) {
|
|
$terms = get_the_terms( $original->get_id(), 'product_brand' );
|
|
if ( ! is_array( $terms ) ) {
|
|
return;
|
|
}
|
|
|
|
$ids = array();
|
|
foreach ( $terms as $term ) {
|
|
$ids[] = $term->term_id;
|
|
}
|
|
$duplicate->add_meta_data( 'duplicate_temp_brand_ids', $ids );
|
|
}
|
|
|
|
/**
|
|
* After product was added check if there are temporary brands and
|
|
* add them officially and remove the temporary brands.
|
|
*
|
|
* @since 9.4.0
|
|
*
|
|
* @param int $product_id
|
|
*/
|
|
public function duplicate_add_product_brand_terms( $product_id ) {
|
|
$product = wc_get_product( $product_id );
|
|
// Bail if product isn't found.
|
|
if ( ! $product instanceof WC_Product ) {
|
|
return;
|
|
}
|
|
$term_ids = $product->get_meta( 'duplicate_temp_brand_ids' );
|
|
if ( empty( $term_ids ) ) {
|
|
return;
|
|
}
|
|
$term_taxonomy_ids = wp_set_object_terms( $product_id, $term_ids, 'product_brand' );
|
|
$product->delete_meta_data( 'duplicate_temp_brand_ids' );
|
|
$product->save();
|
|
}
|
|
|
|
/**
|
|
* Remove terms with empty products.
|
|
*
|
|
* @param WP_Term[] $terms The terms array that needs to be removed of empty products.
|
|
*
|
|
* @return WP_Term[]
|
|
*/
|
|
private function remove_terms_with_empty_products( $terms ) {
|
|
return array_filter(
|
|
$terms,
|
|
function ( $term ) {
|
|
return $term->count > 0;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Invalidates the layered nav counts cache.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function invalidate_wc_layered_nav_counts_cache() {
|
|
$taxonomy = 'product_brand';
|
|
delete_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ) );
|
|
}
|
|
|
|
/**
|
|
* Reset Layered Nav cached counts on product status change.
|
|
*
|
|
* @param $new_status
|
|
* @param $old_status
|
|
* @param $post
|
|
*
|
|
* @return void
|
|
*/
|
|
function reset_layered_nav_counts_on_status_change( $new_status, $old_status, $post ) {
|
|
if ( $post->post_type === 'product' && $old_status !== $new_status ) {
|
|
$this->invalidate_wc_layered_nav_counts_cache();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a new block to the template.
|
|
*
|
|
* @param string $template_id Template ID.
|
|
* @param string $template_area Template area.
|
|
* @param BlockTemplateInterface $template Template instance.
|
|
*/
|
|
public function wc_brands_on_block_template_register( $template_id, $template_area, $template ) {
|
|
|
|
if ( 'simple-product' === $template->get_id() ) {
|
|
$section = $template->get_section_by_id( 'product-catalog-section' );
|
|
if ( $section !== null ) {
|
|
$section->add_block(
|
|
array(
|
|
'id' => 'woocommerce-brands-select',
|
|
'blockName' => 'woocommerce/product-taxonomy-field',
|
|
'order' => 15,
|
|
'attributes' => array(
|
|
'label' => __( 'Brands', 'woocommerce-brands' ),
|
|
'createTitle' => __( 'Create new brand', 'woocommerce-brands' ),
|
|
'slug' => 'product_brand',
|
|
'property' => 'brands',
|
|
),
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$GLOBALS['WC_Brands'] = new WC_Brands();
|