php - How do I ensure that the URL parameters are updated correctly when multiple filters are applied

admin2025-01-07  6

I am writing a product filter, and ran into this issue.

I have 2 different attributes of my product filter: Tilt -- as a checkbox with "Yes" or "No", and Multibrand Compatible -- as a checkbox with Yes or No. If I click Multibrand Compatible "No", the products without the attribute of Multibrand Compatible no are displayed.

Let's say initially I click on Multibrand with the attribute of "No". If I then click on Tilt with the attribute of "Yes", the products that have a tilt, and are NOT Multibrand Compatible should be displayed.

Instead, I get all of the products with a tilt, including ones with a positive Multibrand Compatibility.

The URL Path is:

/?multibrand=no&tilt=yes 

Here is my HTML:

function multiplan_compatible_filter() {
    // Start output buffering to capture the HTML output
    ob_start();
    ?>
    <!-- HTML structure for the Multibrand filter dropdown -->
    <div id="multibrand-tilt-filter-container" class="multibrand-dropdown">
        <div class="multibrand-dropdown-wrapper">
            <button class="multibrand-dropdown-btn">Multibrand Compatible ▼</button>
            <div class="multibrand-dropdown-content">
                <label>
                    <input type="checkbox" id="multibrand-yes" value="yes" data-attribute="pa_multibrand-compatible">
                    Yes
                </label>
                <label>
                    <input type="checkbox" id="multibrand-no" value="no" data-attribute="pa_multibrand-compatible">
                    No
                </label>
            </div>
        </div>
    </div>

Here is the main Javascript and AJAX:

   <!-- JavaScript for handling the filter functionality -->
    <script>
    jQuery(function($) {
        // Toggle dropdown visibility when the button is clicked
        $('.multibrand-dropdown-btn').click(function(e) {
            e.stopPropagation();
            $('.multibrand-dropdown-content').toggleClass('show');
        });

        // Close dropdown when clicking outside
        $(window).click(function() {
            $('.multibrand-dropdown-content, .tilt-dropdown-content').removeClass('show');
        });

        // Handle checkbox changes
        $('#multibrand-yes, #multibrand-no').on('change', function() {
            const currentCheckbox = $(this);
            const isChecked = currentCheckbox.is(':checked');
            const isYesFilter = currentCheckbox.attr('id') === 'multibrand-yes';

            // Update URL parameter
            let currentUrl = new URL(window.location.href);
            let params = currentUrl.searchParams;
            params.delete('multibrand');
            if (isChecked) {
                params.set('multibrand', isYesFilter ? 'yes' : 'no');
            }
            history.pushState(null, '', '?' + params.toString());

            // Ensure only one checkbox is checked at a time
            if (isYesFilter) {
                $('#multibrand-no').prop('checked', false);
            } else {
                $('#multibrand-yes').prop('checked', false);
            }

            // Show loading overlay
            $('body').block({
                message: 'Updating products...',
                overlayCSS: { background: '#fff', opacity: 0.6 }
            });

            // Make AJAX request to update products
            $.ajax({
                url: wc_add_to_cart_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'multibrand_compatible_products',
                    filter_yes: isYesFilter && isChecked,
                    filter_no: !isYesFilter && isChecked
                },
                success: function(response) {
                    if (response.success) {
                        // Update product display
                        $('.products').html(response.data.filtered_products);
                        if (response.data.other_products) {
                            $('.products').append('<h2>Other Products</h2>' + response.data.other_products);
                        }
                    }
                    $('body').unblock();
                },
                error: function() {
                    console.error('Failed to update products');
                    $('body').unblock();
                },
            });
        });

        // Initialize state on page load
        $(document).ready(function() {
            let params = new URLSearchParams(window.location.search);
            let multibrandValue = params.get('multibrand');

            // Reset checkboxes
            $('#multibrand-yes, #multibrand-no').prop('checked', false);

            // Set initial state based on URL parameter
            if (multibrandValue === 'yes') {
                $('#multibrand-yes').prop('checked', true);
            } else if (multibrandValue === 'no') {
                $('#multibrand-no').prop('checked', true);
            }

            // Trigger initial filter if needed
            if (multibrandValue) {
                $('#multibrand-yes, #multibrand-no').first(':checked').trigger('change');
            }
        });
    });
    </script>
    <?php
    // Return the captured HTML output
    return ob_get_clean();
}

// Register AJAX actions for both logged-in and non-logged-in users
add_action('wp_ajax_multibrand_compatible_products', 'multibrand_compatible_products');
add_action('wp_ajax_nopriv_multibrand_compatible_products', 'multibrand_compatible_products');

// AJAX handler for filtering products
function multibrand_compatible_products() {
    // Validate incoming data
    if (!isset($_POST['filter_yes']) || !isset($_POST['filter_no'])) {
        wp_send_json_error();
        return;
    }

    $filter_yes = $_POST['filter_yes'] === 'true';
    $filter_no = $_POST['filter_no'] === 'true';

    // Include the unified filter function
    require_once get_template_directory() . '/functions.php_FILES/Filter/unified_filter.php';

    // Apply the filter and get results
    $result = unified_product_filter('pa_multibrand-compatible', $filter_yes, $filter_no);

    // Send the filtered results back as a JSON response
    wp_send_json_success($result);
}

I made a unified Query System for the rest of my code so it is easier to scale:

<?php
function unified_product_filter($attribute, $filter_yes, $filter_no) {
    // Set up the base arguments for WP_Query to fetch products
    $filtered_args = array(
        'post_type' => 'product',
        'posts_per_page' => -1, // Retrieve all matching products
    );

    // Initialize the tax_query array for filtering by product attributes
    $filtered_args['tax_query'] = array(
        'relation' => 'AND', // All conditions in this array must be met
    );

    // Add attribute filters based on user selection
    if ($filter_yes) {
        // If 'Yes' is selected, add a filter for products with 'yes' value for the given attribute
        $filtered_args['tax_query'][] = array(
            'taxonomy' => $attribute, // The product attribute to filter by (e.g., 'pa_multibrand-compatible')
            'field' => 'slug',
            'terms' => 'yes',
        );
    } elseif ($filter_no) {
        // If 'No' is selected, add a filter for products with 'no' value for the given attribute
        $filtered_args['tax_query'][] = array(
            'taxonomy' => $attribute,
            'field' => 'slug',
            'terms' => 'no',
        );
    }

    // Perform the query to get filtered products
    $filtered_query = new WP_Query($filtered_args);

    // Initialize variables to store HTML output
    $filtered_products_html = '';
    $other_products_html = '';

    // Check if there are filtered products
    if ($filtered_query->have_posts()) {
        while ($filtered_query->have_posts()) {
            $filtered_query->the_post();
            // Generate HTML for each filtered product
            $filtered_products_html .= generate_product_html(get_the_ID());
        }
        wp_reset_postdata(); // Reset the global $post variable
    }

    // If no products match the filter, get all products
    if (empty($filtered_products_html)) {
        $all_products_query = new WP_Query(array(
            'post_type' => 'product',
            'posts_per_page' => -1,
        ));

        if ($all_products_query->have_posts()) {
            while ($all_products_query->have_posts()) {
                $all_products_query->the_post();
                // Generate HTML for each product
                $other_products_html .= generate_product_html(get_the_ID());
            }
            wp_reset_postdata();
        }
    }

    // Return the results as an array
    return array(
        'filtered_products' => $filtered_products_html,
        'other_products' => $other_products_html,
    );
}

// Helper function to generate HTML for a single product
function generate_product_html($product_id) {
    $product = wc_get_product($product_id);
    
    // Generate HTML for the product (customize as needed)
    $html = '<div class="product">';
    $html .= '<h3>' . $product->get_name() . '</h3>';
    $html .= '<p>Price: ' . $product->get_price_html() . '</p>';
    // Add more product details as needed
    $html .= '</div>';

    return $html;
}

I believe that anytime a new filter is added, whether through a click of a checkbox, or a selection of a dropdown, the on-loading query either over-writes everything else or only does a query for the toggled newest filter. Is there any way to have the filtering work on-page load for each seperate URL path of the filtered attribute? (ie: tilt=yes and multibrand=no display products with tilt of "yes" and multibrand of "no")

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1736261287a730.html

最新回复(0)