WooBrandWidget/woo-brand-widget.php
2025-09-12 06:54:12 +00:00

271 lines
9.8 KiB
PHP

<?php
/**
* Plugin Name: Woo Brand Widget
* Plugin URI: https://informatiq.services
* Description: Brand filter widget and shortcode for WooCommerc with Shoptimizer
* Version: 1.0.0
* Author: Mălin Cenușă
* Author URI: https://mălin.ro
* License: GPL v2 or later
* Text Domain: shoptimizer-brand-filter
*/
if (!defined('ABSPATH')) exit;
class Shoptimizer_Brand_Filter_Fixed {
public function __construct() {
add_action('plugins_loaded', array($this, 'init'));
}
public function init() {
// Register shortcode
add_shortcode('brand_filter', array($this, 'shortcode'));
// Register widget
add_action('widgets_init', array($this, 'register_widget'));
// Enqueue styles
add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
}
public function enqueue_styles() {
wp_add_inline_style('wp-block-library', '
.brand-filter-widget {
margin-bottom: 30px;
}
.brand-filter-widget h3 {
margin-top: 0;
margin-bottom: 15px;
font-size: 16px;
font-weight: 600;
color: #333;
}
.brand-list {
list-style: none;
margin: 0;
padding: 0;
}
.brand-list li {
font-size: 10.5px; /* 14px * 0.75 = 10.5px (25% smaller) */
line-height: 1.09; /* 1.45 * 0.75 = 1.09 (25% smaller) */
margin-bottom: 6px;
}
.brand-list li.brand-parent {
font-weight: 600;
margin-bottom: 8px;
}
.brand-list li.brand-family {
padding-left: 15px;
font-weight: 400;
margin-bottom: 4px;
}
.brand-list a {
color: #222;
text-decoration: none;
display: block;
transition: color 0.2s;
background-color: transparent;
}
.brand-list a:hover {
color: #d43a8e;
text-decoration: underline;
text-decoration-thickness: 0.5px;
text-underline-offset: 0.18em;
}
.brand-list a:active,
.brand-list a:focus {
outline: 0;
}
.brand-count {
color: #666;
font-size: 0.9em;
font-weight: normal;
}
.brand-list li.brand-family a {
color: #555;
padding-left: 0;
}
.brand-list li.brand-family a:hover {
color: #d43a8e;
}
');
}
public function shortcode($atts) {
$atts = shortcode_atts(array(
'title' => 'Filter by Brand',
'show_count' => 'yes'
), $atts);
// Check if WooCommerce is active
if (!class_exists('WooCommerce')) {
return '<p>WooCommerce is not active</p>';
}
// First, let's check what taxonomies exist
$taxonomies = get_object_taxonomies('product');
// Try different possible brand taxonomy names
$brand_taxonomy = '';
$possible_names = array('product_brand', 'pa_brand', 'pwb-brand', 'yith_product_brand');
foreach ($possible_names as $tax_name) {
if (in_array($tax_name, $taxonomies)) {
$brand_taxonomy = $tax_name;
break;
}
}
if (empty($brand_taxonomy)) {
return '<div class="brand-filter-widget">
<h3>' . esc_html($atts['title']) . '</h3>
<p>No brand taxonomy found. Available taxonomies: ' . implode(', ', $taxonomies) . '</p>
</div>';
}
// Get terms hierarchically - get ALL terms first, then filter manually
$all_terms = get_terms(array(
'taxonomy' => $brand_taxonomy,
'hide_empty' => false, // Get all terms, we'll filter manually
'orderby' => 'name',
'order' => 'ASC'
));
if (is_wp_error($all_terms) || empty($all_terms)) {
return '<div class="brand-filter-widget">
<h3>' . esc_html($atts['title']) . '</h3>
<p>No brands found for taxonomy: ' . $brand_taxonomy . '</p>
</div>';
}
// Organize terms hierarchically - only include terms with products
$parent_terms = array();
$child_terms = array();
foreach ($all_terms as $term) {
// Check if term has published products and get accurate count (regardless of stock status)
$product_query = new WP_Query(array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1, // Get all to count them
'fields' => 'ids', // Only get IDs for performance
'tax_query' => array(
array(
'taxonomy' => $brand_taxonomy,
'field' => 'term_id',
'terms' => $term->term_id,
'include_children' => false
)
)
));
$actual_count = $product_query->found_posts;
wp_reset_postdata();
if ($actual_count > 0) {
// Store the actual count in the term object
$term->actual_count = $actual_count;
if ($term->parent == 0) {
$parent_terms[] = $term;
} else {
if (!isset($child_terms[$term->parent])) {
$child_terms[$term->parent] = array();
}
$child_terms[$term->parent][] = $term;
}
}
}
// Filter out parent terms that have no products themselves AND no child terms with products
$filtered_parent_terms = array();
foreach ($parent_terms as $parent) {
$has_products = isset($parent->actual_count) && $parent->actual_count > 0;
$has_children_with_products = isset($child_terms[$parent->term_id]) && !empty($child_terms[$parent->term_id]);
// Include parent if it has products OR if it has children with products
if ($has_products || $has_children_with_products) {
$filtered_parent_terms[] = $parent;
}
}
$parent_terms = $filtered_parent_terms;
ob_start();
?>
<div class="brand-filter-widget">
<h3><?php echo esc_html($atts['title']); ?></h3>
<ul class="brand-list">
<?php foreach ($parent_terms as $parent): ?>
<li class="brand-parent">
<a href="<?php echo esc_url(get_term_link($parent)); ?>">
<?php echo esc_html($parent->name); ?>
<?php if ($atts['show_count'] === 'yes'): ?>
<span class="brand-count">(<?php echo isset($parent->actual_count) ? $parent->actual_count : 0; ?>)</span>
<?php endif; ?>
</a>
<?php if (isset($child_terms[$parent->term_id])): ?>
<?php foreach ($child_terms[$parent->term_id] as $child): ?>
<li class="brand-family">
<a href="<?php echo esc_url(get_term_link($child)); ?>">
<?php echo esc_html($child->name); ?>
<?php if ($atts['show_count'] === 'yes'): ?>
<span class="brand-count">(<?php echo isset($child->actual_count) ? $child->actual_count : 0; ?>)</span>
<?php endif; ?>
</a>
</li>
<?php endforeach; ?>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php
return ob_get_clean();
}
public function register_widget() {
register_widget('Shoptimizer_Brand_Widget_Fixed');
}
}
class Shoptimizer_Brand_Widget_Fixed extends WP_Widget {
public function __construct() {
parent::__construct(
'shoptimizer_brand_widget_fixed',
'Brand Filter Widget',
array('description' => 'Display product brands for filtering')
);
}
public function widget($args, $instance) {
$title = !empty($instance['title']) ? $instance['title'] : 'Filter by Brand';
echo $args['before_widget'];
echo do_shortcode('[brand_filter title="' . esc_attr($title) . '"]');
echo $args['after_widget'];
}
public function form($instance) {
$title = !empty($instance['title']) ? $instance['title'] : 'Filter by Brand';
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>">Title:</label>
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
name="<?php echo $this->get_field_name('title'); ?>" type="text"
value="<?php echo esc_attr($title); ?>">
</p>
<?php
}
public function update($new_instance, $old_instance) {
return array(
'title' => sanitize_text_field($new_instance['title'])
);
}
}
// Initialize
new Shoptimizer_Brand_Filter_Fixed();