Woocommerce Attributes / Variations

Hey team,

When using swatch on Woocommerce there is a couple of issues that I have seen so far.

The first is a common one even when building a custom Single Product. It parses the attribute incorrectly into the value=“” for each option if the values have a quote (for example 50" x 100" ) will parse to value=“50”

I fixed this one for now but figured i’d mention it.

The next one is that I notice my options are commonly “disabled”

Video: CleanShot 2024-09-16 at 11.59.12 · CleanShot Cloud

Where as when I use the Woocommerce default hook for add to cart and selects it works just fine.


Another more advance topic i’d like to do my add to cart / selects with ajax so that I can use view transitions, I notice that everything renders fine when using view transitions but add to cart won’t work, likely due to a client side resource not loading as expected due to pjax.

This one isn’t as big of a deal, I can solve for it but something to be aware of.

1 Like

Hi @Agnostic,

As WooCommerce is a much used feature with Cwicly, I’m sure many people here will benefit from your solutions/fixes if you are willing to share them.

@StrangeTech

For the incorrectly parsed values you can see what I did here, I included in the block unwrapper that I made for the other post.

<?php

class CwiclyBlockUnwrapper {
    /**
     * @var string[] List of Cwicly block names to process
     */
    private $targetBlocks = ['cwicly/repeater', 'cwicly/query'];

    /**
     * @var DOMDocument The DOM representation of the block content
     */
    private $dom;

    /**
     * @var DOMXPath The XPath object for querying the DOM
     */
    private $xpath;

    /**
     * Main method to process the block content
     *
     * @param string $block_content The original block content
     * @param array $block The block information
     * @return string The processed block content
     */
    public function processBlock($block_content, $block) {
        if (in_array($block['blockName'], $this->targetBlocks)) {
            $this->initializeDom($block_content);
            $this->removeWrappers();
            $this->processOptions();
            return $this->getModifiedContent();
        }
        return $block_content;
    }

    /**
     * Initialize the DOM with the block content
     *
     * @param string $content The HTML content to process
     */
    private function initializeDom($content) {
        $this->dom = new DOMDocument();
        @$this->dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        $this->xpath = new DOMXPath($this->dom);
    }

    /**
     * Remove wrapper divs
     */
    private function removeWrappers() {
        // Find all divs without class or id that have children
        $emptyDivs = $this->xpath->query("//div[not(@class) and not(@id) and *]");
        $this->unwrapElements($emptyDivs);

        // Find all divs with class="query-item"
        $queryItems = $this->xpath->query("//div[@class='cc-query-item']");
        $this->unwrapElements($queryItems);
    }

    /**
     * Unwrap elements, replacing them with their children
     *
     * @param DOMNodeList $elements The elements to unwrap
     */
    private function unwrapElements($elements) {
        foreach ($elements as $element) {
            $fragment = $this->dom->createDocumentFragment();
            while ($element->firstChild) {
                $fragment->appendChild($element->firstChild);
            }
            $element->parentNode->replaceChild($fragment, $element);
        }
    }

    /**
     * Process <option> elements
     */
    private function processOptions() {
        $options = $this->xpath->query("//option");
        foreach ($options as $option) {
            $text = trim($option->textContent);
            $option->setAttribute('value', $text);
        }
    }

    /**
     * Get the modified HTML content
     *
     * @return string The processed HTML content
     */
    private function getModifiedContent() {
        return $this->dom->saveHTML();
    }
}

// Initialize the CwiclyBlockUnwrapper
$unwrapper = new CwiclyBlockUnwrapper();

// Add the filter
add_filter('render_block', function($block_content, $block) use ($unwrapper) {
    return $unwrapper->processBlock($block_content, $block);
}, 10, 2);

Essentially since they will always live within a Repeater I just check for options, if they exist, I take the inner text that renders correctly and move them into the values. Since that’s the way Woo expects it via the REST API which is how Cwicly’s add to cart works.

You can always work to make it more specific and etc.

Overall though, I’m not such a huge fan of their swatch component, neat concept but I’d like more control over the data structure and how it parses, but once it’s open source i’ll take a deeper look.

2 Likes

@Louis the main issue i’ve really noticed that prevents me from using Cwicly woo logic is the issue with parsing " in the product attributes / variations.

Right now it breaks the HTML completely when someone puts something like

1.5" Thick

the double quote breaks the template and parses to

value=“1.5” which then causes the cwicly JS to disable everything because there is no matching variants.

This has lead to me having to essentially work around and make a completely custom attribute picker. This seems like a quick patch item but if you have any recommendations for how to handle on my end I would appreciate.

I made it so it corrects it server side but the JS still disables all the options.

Hello @Agnostic,

Sorry for not being more active on this one.
Thanks for bringing it up.
I’ll take a look at this as it is something that wasn’t considered when we integrated it.

Hello @Agnostic,

It seems that values with quotes are automatically removed by WooCommerce when added through their UI.
Can you let me know how you’re registering the attributes?

Thanks in advance.

Not sure, client project, it was to lofty of change to convert them all from " to inch or similar.