389 lines
8.2 KiB
PHP
389 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace Blocksy;
|
|
|
|
class WooCommerceAddToCart {
|
|
use WordPressActionsManager;
|
|
|
|
private $finalize_action_name = '';
|
|
|
|
private $handled_product_ids = [];
|
|
|
|
private $actions = [
|
|
[
|
|
'action' => 'woocommerce_before_add_to_cart_form',
|
|
'priority' => 10
|
|
],
|
|
|
|
[
|
|
'action' => 'woocommerce_before_add_to_cart_quantity',
|
|
'priority' => PHP_INT_MAX
|
|
],
|
|
|
|
[
|
|
'action' => 'woocommerce_before_add_to_cart_button',
|
|
'priority' => PHP_INT_MAX
|
|
],
|
|
|
|
[
|
|
'action' => 'woocommerce_after_add_to_cart_button',
|
|
'priority' => 100
|
|
],
|
|
|
|
[
|
|
'action' => 'woocommerce_post_class',
|
|
'priority' => 10
|
|
]
|
|
];
|
|
|
|
public function __construct() {
|
|
$this->attach_hooks([
|
|
'exclude' => [
|
|
'woocommerce_after_add_to_cart_button'
|
|
]
|
|
]);
|
|
|
|
if (isset($_REQUEST['blocksy_add_to_cart'])) {
|
|
add_filter(
|
|
'woocommerce_add_to_cart_redirect',
|
|
'__return_false'
|
|
);
|
|
}
|
|
|
|
// TODO: maybe extract block watcher in a separate class and use from one
|
|
// single place, in case we will need to detect more of them.
|
|
add_filter('pre_render_block', function ($pre_render, $parsed_block, $parent_block) {
|
|
if ($parsed_block['blockName'] === 'woocommerce/add-to-cart-form') {
|
|
global $blocksy_detect_woo_block_render;
|
|
|
|
$blocksy_detect_woo_block_render = true;
|
|
}
|
|
|
|
return $pre_render;
|
|
}, 10, 3);
|
|
|
|
// Make sure we process response exactly same way as Woo does. In the
|
|
// exact point of time.
|
|
//
|
|
// https://github.com/woocommerce/woocommerce/blob/9f26128dc2a223d5b9e5482e6a41b5052bfc91c4/plugins/woocommerce/includes/class-wc-ajax.php#L30
|
|
add_action('template_redirect', [$this, 'do_wc_ajax'], 0);
|
|
}
|
|
|
|
public function do_wc_ajax() {
|
|
if (! isset($_REQUEST['blocksy_add_to_cart'])) {
|
|
return;
|
|
}
|
|
|
|
// Signal that this is an AJAX request for plugins that check wp_doing_ajax().
|
|
// Since this request doesn't go through admin-ajax.php, the DOING_AJAX constant
|
|
// is not defined. Plugins like "Advanced Product Fields for WooCommerce" use
|
|
// wp_doing_ajax() to detect mini-cart context and display custom field data.
|
|
//
|
|
// https://wordpress.org/plugins/advanced-product-fields-for-woocommerce/
|
|
add_filter('wp_doing_ajax', '__return_true');
|
|
|
|
ob_start();
|
|
wc_print_notices();
|
|
$notices = ob_get_clean();
|
|
|
|
ob_start();
|
|
woocommerce_mini_cart();
|
|
$mini_cart = ob_get_clean();
|
|
|
|
$data = [
|
|
'notices' => $notices,
|
|
'fragments' => apply_filters(
|
|
'woocommerce_add_to_cart_fragments',
|
|
[
|
|
'div.widget_shopping_cart_content' => '<div class="widget_shopping_cart_content">' . $mini_cart . '</div>',
|
|
]
|
|
),
|
|
'cart_hash' => WC()->cart->get_cart_hash()
|
|
];
|
|
|
|
wp_send_json_success($data);
|
|
}
|
|
|
|
private function product_was_handled($product) {
|
|
if (! $product instanceof \WC_Product) {
|
|
return false;
|
|
}
|
|
|
|
return in_array($product->get_id(), $this->handled_product_ids);
|
|
}
|
|
|
|
private function output_cart_action_open() {
|
|
if (
|
|
(
|
|
blocksy_manager()->screen->is_product()
|
|
||
|
|
wp_doing_ajax()
|
|
)
|
|
&&
|
|
! blocksy_manager()->screen->uses_woo_default_template()
|
|
) {
|
|
return;
|
|
}
|
|
|
|
global $blocksy_detect_woo_block_render;
|
|
|
|
if (
|
|
isset($blocksy_detect_woo_block_render)
|
|
&&
|
|
$blocksy_detect_woo_block_render
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$attr = apply_filters('blocksy:woocommerce:cart-actions:attr', [
|
|
'class' => 'ct-cart-actions'
|
|
]);
|
|
|
|
echo '<div ' . blocksy_attr_to_html($attr) . '>';
|
|
|
|
$this->attach_hooks([
|
|
'only' => [
|
|
'woocommerce_after_add_to_cart_button'
|
|
]
|
|
]);
|
|
}
|
|
|
|
public function finalize_add_to_cart() {
|
|
global $product;
|
|
|
|
if ($this->product_was_handled($product)) {
|
|
return;
|
|
}
|
|
|
|
if (! $product) {
|
|
return;
|
|
}
|
|
|
|
// On single product pages we know for sure that there's only one
|
|
// product that needs handling. On other pages or during AJAX requests,
|
|
// we need to make sure that we don't handle the same product twice.
|
|
if (blocksy_manager()->screen->is_product()) {
|
|
$this->detach_hooks();
|
|
} else {
|
|
$this->handled_product_ids[] = $product->get_id();
|
|
}
|
|
|
|
remove_action(
|
|
$this->finalize_action_name,
|
|
[$this, 'finalize_add_to_cart'],
|
|
50
|
|
);
|
|
}
|
|
|
|
public function woocommerce_before_add_to_cart_form() {
|
|
global $product;
|
|
global $root_product;
|
|
|
|
if ($this->product_was_handled($product)) {
|
|
return;
|
|
}
|
|
|
|
$root_product = $product;
|
|
}
|
|
|
|
public function woocommerce_before_add_to_cart_quantity() {
|
|
global $product;
|
|
global $root_product;
|
|
|
|
if ($this->product_was_handled($product)) {
|
|
return;
|
|
}
|
|
|
|
if (! $root_product) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
! $root_product->is_type('simple')
|
|
&&
|
|
! $root_product->is_type('variation')
|
|
&&
|
|
! $root_product->is_type('variable')
|
|
&&
|
|
! $this->check_product_type($root_product)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$this->output_cart_action_open();
|
|
|
|
// If we will encounter an woocommerce_before_add_to_cart_button action before the actual next woocommerce_post_class,
|
|
// this means that the add to cart form for the current product is rendered
|
|
// one more time. So, we need to ignore it and finalize logic in this case.
|
|
//
|
|
// This check is needed only on single product pages. The archive pages logic is covered
|
|
// in finalize logic already.
|
|
//
|
|
// This happened for Free Downloads WooCommerce Pro plugin.
|
|
if (blocksy_manager()->screen->is_product()) {
|
|
$this->finalize_action_name = 'woocommerce_before_add_to_cart_button';
|
|
add_action($this->finalize_action_name, [$this, 'finalize_add_to_cart'], 50);
|
|
}
|
|
}
|
|
|
|
public function woocommerce_before_add_to_cart_button() {
|
|
global $product;
|
|
global $root_product;
|
|
|
|
if ($this->product_was_handled($product)) {
|
|
return;
|
|
}
|
|
|
|
if (! $root_product) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
! $root_product->is_type('grouped')
|
|
&&
|
|
! $root_product->is_type('external')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$this->output_cart_action_open();
|
|
}
|
|
|
|
public function woocommerce_after_add_to_cart_button() {
|
|
global $product;
|
|
|
|
if ($this->product_was_handled($product)) {
|
|
return;
|
|
}
|
|
|
|
if (! $product) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
! $product->is_type('simple')
|
|
&&
|
|
! $product->is_type('variable')
|
|
&&
|
|
! $product->is_type('grouped')
|
|
&&
|
|
! $product->is_type('external')
|
|
&&
|
|
! $this->check_product_type($product)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
(
|
|
$product->is_type('simple')
|
|
||
|
|
$product->is_type('variable')
|
|
||
|
|
$this->check_product_type($product)
|
|
)
|
|
&&
|
|
! did_action('woocommerce_before_add_to_cart_quantity')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
global $blocksy_detect_woo_block_render;
|
|
|
|
if (
|
|
isset($blocksy_detect_woo_block_render)
|
|
&&
|
|
$blocksy_detect_woo_block_render
|
|
) {
|
|
return;
|
|
}
|
|
|
|
echo '</div>';
|
|
|
|
}
|
|
|
|
public function woocommerce_post_class($classes) {
|
|
global $product;
|
|
global $woocommerce_loop;
|
|
|
|
if ($this->product_was_handled($product)) {
|
|
return $classes;
|
|
}
|
|
|
|
$classes = apply_filters(
|
|
'blocksy:woocommerce:single-product:post-class',
|
|
$classes
|
|
);
|
|
|
|
$default_product_layout = blocksy_get_woo_single_layout_defaults();
|
|
|
|
$layout = blocksy_get_theme_mod(
|
|
'woo_single_layout',
|
|
blocksy_get_woo_single_layout_defaults()
|
|
);
|
|
|
|
if (
|
|
(
|
|
function_exists('blocksy_has_product_specific_layer')
|
|
&&
|
|
! blocksy_has_product_specific_layer('product_add_to_cart')
|
|
)
|
|
||
|
|
! $product
|
|
||
|
|
$product->is_type('external')
|
|
||
|
|
(
|
|
! blocksy_manager()->screen->is_product()
|
|
&&
|
|
! wp_doing_ajax()
|
|
)
|
|
) {
|
|
return $classes;
|
|
}
|
|
|
|
$has_ajax_add_to_cart = blocksy_get_theme_mod(
|
|
'has_ajax_add_to_cart',
|
|
'yes'
|
|
);
|
|
|
|
if (
|
|
$has_ajax_add_to_cart === 'yes'
|
|
&&
|
|
get_option('woocommerce_cart_redirect_after_add', 'no') === 'no'
|
|
) {
|
|
$classes[] = 'ct-ajax-add-to-cart';
|
|
}
|
|
|
|
if (! empty($this->finalize_action_name)) {
|
|
remove_action(
|
|
$this->finalize_action_name,
|
|
[$this, 'finalize_add_to_cart'],
|
|
50
|
|
);
|
|
}
|
|
|
|
// https://github.com/woocommerce/woocommerce/blob/701d9341dde6fa8861684fb161cdca9ec94a7a4d/plugins/woocommerce/includes/wc-template-functions.php#L1784
|
|
$this->finalize_action_name = 'woocommerce_' . $product->get_type() . '_add_to_cart';
|
|
|
|
add_action(
|
|
$this->finalize_action_name,
|
|
[$this, 'finalize_add_to_cart'],
|
|
50
|
|
);
|
|
|
|
return $classes;
|
|
}
|
|
|
|
public function check_product_type($product) {
|
|
$allowed_custom_product_types = [
|
|
'subscription',
|
|
'variable-subscription',
|
|
'woosb'
|
|
];
|
|
|
|
return in_array($product->get_type(), $allowed_custom_product_types);
|
|
}
|
|
}
|
|
|