Puddinq.com sharing knowledge

WooCommerce cross- and up sells bi-directional update

WooCommerce cross- and up sells bi-directional update

If your would like one added (and removed) cross (or up) sell product in WooCommerce to automatically ad the ‘current’ product as an up- or cross sell to the connected code. The following code will inspire you:

class RelationalProductFields
{

    public function register(): void
    {
        if (is_admin() === false) {
            return;
        }
        // is triggered after a product object is created or updated but before the product is saved
        add_action('woocommerce_admin_process_product_object', [$this, 'saveRelationalProductFields']);
    }

    /**
     * Save relational product fields.
     *
     * @param WC_Product $product
     */
    public function saveRelationalProductFields(WC_Product $product): void
    {
        // product state before save.
        $originalProduct = wc_get_product($product->get_id());

        // Upsell arrays
        $originalUpsells = $originalProduct->get_upsell_ids();
        $newUpsells = $product->get_upsell_ids();
        $addedUpsells = array_diff($newUpsells, $originalUpsells);
        $removedUpsells = array_diff($originalUpsells, $newUpsells);
        $changedUpsells = array_merge($addedUpsells, $removedUpsells);

        // cross-sell arrays
        $originalCrossSels = $originalProduct->get_cross_sell_ids();
        $newCrossSells = $product->get_cross_sell_ids();
        $addedCrossSells = array_diff($newCrossSells, $originalCrossSels);
        $removedCrossSells = array_diff($originalCrossSels, $newCrossSells);
        $changedCrossSells = array_merge($addedCrossSells, $removedCrossSells);

        // Loop through removed and added upsells
        foreach ($changedUpsells as $changedUpsellId) {

            $relatedUpsellProduct = wc_get_product($changedUpsellId);

            if (!$relatedUpsellProduct) {
                continue;
            }

            if ($relatedUpsellProduct->get_type() === 'variation') {
                $relatedUpsellProduct = wc_get_product($relatedUpsellProduct->get_parent_id());
            }

            // Add or remove the product as upsell
            $upsellIds = $relatedUpsellProduct->get_upsell_ids();

            if (in_array($changedUpsellId, $addedUpsells, true)) {
                $upsellIds[] = $product->get_id(); // add
                $upsellIds = array_unique($upsellIds);
                $relatedUpsellProduct->set_upsell_ids($upsellIds);
            } elseif (in_array($changedUpsellId, $removedUpsells, true)) {
                $upsellIds = array_diff($upsellIds, [$product->get_id()]); //remove
                $relatedUpsellProduct->set_upsell_ids($upsellIds);
            }
            $relatedUpsellProduct->save();
        }

        // Loop through removed and added upsells
        foreach ($changedCrossSells as $changedCrossSellId) {

            $relatedCrossSellProduct = wc_get_product($changedCrossSellId);

            if (!$relatedCrossSellProduct) {
                continue;
            }

            if ($relatedCrossSellProduct->get_type() === 'variation') {
                $relatedCrossSellProduct = wc_get_product($relatedCrossSellProduct->get_parent_id());
            }

            // Add or remove the product as upsell
            $crossSellIds = $relatedCrossSellProduct->get_upsell_ids();
            if (in_array($changedCrossSellId, $addedCrossSells, true)) {
                $crossSellIds[] = $product->get_id(); // add
                $crossSellIds = array_unique($crossSellIds);
                $relatedCrossSellProduct->set_cross_sell_ids($crossSellIds);
            } elseif (in_array($changedCrossSellId, $removedCrossSells, true)) {
                $crossSellIds = array_diff($crossSellIds, [$product->get_id()]); //remove
                $relatedCrossSellProduct->set_cross_sell_ids($crossSellIds);
            }
            $relatedCrossSellProduct->save();
        }
    }
}