Compare commits
3 Commits
377cabf3cc
...
8e354cb729
Author | SHA1 | Date |
---|---|---|
|
8e354cb729 | 7 days ago |
|
7499a8aa24 | 1 week ago |
|
6e2a763cb7 | 1 week ago |
@ -0,0 +1,380 @@ |
||||
<? |
||||
|
||||
/** |
||||
* Добавление событий контрибуции для FP Pixel |
||||
* только на боевом сайте АЕ |
||||
*/ |
||||
if ($site_env->site_mode == 'production' && $site_env->site_region == 'ae') { |
||||
|
||||
// Проверка на тестовый режим для метрик |
||||
// TO_DO: дуюлирует логику is_gp_test_mode в wp-content/themes/cosmopet/global-functions/multisite-functions.php |
||||
function is_gp_test_mode() |
||||
{ |
||||
if (isset($_GET['gp-test']) && $_GET['gp-test'] == '1') { |
||||
return true; |
||||
} |
||||
if (is_user_logged_in() && current_user_can('administrator')) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
add_action('wp_footer', 'add_facebook_pixel_events'); |
||||
function add_facebook_pixel_events() |
||||
{ |
||||
if (is_gp_test_mode()) |
||||
return; |
||||
|
||||
global $product; |
||||
|
||||
// 1. ViewContent |
||||
if (is_product() && $product && $product->get_price() > 0) { |
||||
?> |
||||
<script> |
||||
document.addEventListener('DOMContentLoaded', function() { |
||||
fbq('track', 'ViewContent', { |
||||
content_ids: ['<?php echo $product->get_id(); ?>'],
|
||||
content_type: 'product', |
||||
value: <?php echo $product->get_price(); ?>,
|
||||
currency: '<?php echo get_woocommerce_currency(); ?>'
|
||||
}); |
||||
}); |
||||
</script> |
||||
<?php |
||||
} |
||||
|
||||
// 2. InitiateCheckout |
||||
if (is_checkout() && !is_wc_endpoint_url('order-received') && WC()->cart && WC()->cart->get_cart_contents_count() > 0) { |
||||
?> |
||||
<script> |
||||
document.addEventListener('DOMContentLoaded', function() { |
||||
fbq('track', 'InitiateCheckout'); |
||||
}); |
||||
</script> |
||||
<?php |
||||
} |
||||
|
||||
// 3. AddToCart |
||||
if (is_product() || is_shop() || is_cart()) { |
||||
?> |
||||
<script> |
||||
document.addEventListener('DOMContentLoaded', function() { |
||||
jQuery(function($) { |
||||
$(document.body).on('added_to_cart', function(event, fragments, cart_hash, $button) { |
||||
var productId = $button.data('product_id') || ''; |
||||
var quantity = $button.data('quantity') || 1; |
||||
var productName = $button.data('product_sku') || |
||||
$button.closest('.product').find('.woocommerce-loop-product__title').text().trim() || 'Unknown'; |
||||
var priceElement = $button.closest('.product').find('.price .amount').text().replace(/[^0-9.]/g, '') || '0.00'; |
||||
var currency = '<?php echo get_woocommerce_currency(); ?>';
|
||||
|
||||
fbq('track', 'AddToCart', { |
||||
content_ids: [productId], |
||||
content_type: 'product', |
||||
value: parseFloat(priceElement) * quantity, |
||||
currency: currency |
||||
}); |
||||
|
||||
window.dataLayer = window.dataLayer || []; |
||||
window.dataLayer.push({ |
||||
'event': 'add_to_cart', |
||||
'ecommerce': { |
||||
'currency': currency, |
||||
'value': parseFloat(priceElement) * quantity, |
||||
'items': [{ |
||||
'item_id': productId, |
||||
'item_name': productName, |
||||
'price': parseFloat(priceElement), |
||||
'quantity': quantity |
||||
}] |
||||
} |
||||
}); |
||||
}); |
||||
}); |
||||
}); |
||||
</script> |
||||
<?php |
||||
} |
||||
|
||||
// 4. AddPaymentInfo |
||||
if (is_checkout() && !is_wc_endpoint_url('order-received') && WC()->cart && WC()->cart->get_cart_contents_count() > 0) { |
||||
$currency = get_woocommerce_currency(); |
||||
$cart_total = WC()->cart->get_total('edit'); |
||||
?> |
||||
<script> |
||||
document.addEventListener('DOMContentLoaded', function() { |
||||
fbq('track', 'AddPaymentInfo', { |
||||
value: <?php echo $cart_total; ?>,
|
||||
currency: '<?php echo $currency; ?>'
|
||||
}); |
||||
|
||||
window.dataLayer = window.dataLayer || []; |
||||
window.dataLayer.push({ |
||||
'event': 'add_payment_info', |
||||
'ecommerce': { |
||||
'currency': '<?php echo $currency; ?>',
|
||||
'value': <?php echo $cart_total; ?> |
||||
} |
||||
}); |
||||
}); |
||||
</script> |
||||
<?php |
||||
} |
||||
|
||||
// 5. Purchase |
||||
if (is_wc_endpoint_url('order-received')) { |
||||
$order_id = absint(get_query_var('order-received')); |
||||
if (!$order_id) |
||||
return; |
||||
|
||||
$order = wc_get_order($order_id); |
||||
if (!$order || ($order->get_status() !== 'processing' && $order->get_status() !== 'completed')) |
||||
return; |
||||
|
||||
$items = []; |
||||
foreach ($order->get_items() as $item) { |
||||
$product = $item->get_product(); |
||||
$items[] = [ |
||||
'item_id' => $product->get_id(), |
||||
'item_name' => $product->get_name(), |
||||
'price' => $product->get_price(), |
||||
'quantity' => $item->get_quantity() |
||||
]; |
||||
} |
||||
?> |
||||
<script> |
||||
document.addEventListener('DOMContentLoaded', function() { |
||||
fbq('track', 'Purchase', { |
||||
value: <?php echo $order->get_total(); ?>,
|
||||
currency: '<?php echo $order->get_currency(); ?>',
|
||||
content_ids: [<?php echo implode(',', array_column($items, 'item_id')); ?>],
|
||||
content_type: 'product' |
||||
}); |
||||
|
||||
window.dataLayer = window.dataLayer || []; |
||||
window.dataLayer.push({ |
||||
'event': 'purchase', |
||||
'ecommerce': { |
||||
'currency': '<?php echo $order->get_currency(); ?>',
|
||||
'value': <?php echo $order->get_total(); ?>,
|
||||
'items': <?php echo json_encode($items); ?> |
||||
} |
||||
}); |
||||
}); |
||||
</script> |
||||
<?php |
||||
} |
||||
} |
||||
|
||||
add_action('woocommerce_thankyou', 'send_purchase_to_metrika'); |
||||
function send_purchase_to_metrika($order_id) |
||||
{ |
||||
if (is_gp_test_mode()) |
||||
return; |
||||
|
||||
if (!$order_id) |
||||
return; |
||||
|
||||
$order = wc_get_order($order_id); |
||||
if ($order->get_status() !== 'processing' && $order->get_status() !== 'completed') |
||||
return; |
||||
|
||||
$items = []; |
||||
foreach ($order->get_items() as $item) { |
||||
$product = $item->get_product(); |
||||
$items[] = [ |
||||
'id' => $product->get_id(), |
||||
'name' => $product->get_name(), |
||||
'price' => $product->get_price(), |
||||
'quantity' => $item->get_quantity() |
||||
]; |
||||
} |
||||
|
||||
$currency = $order->get_currency(); |
||||
?> |
||||
<script> |
||||
window.dataLayer = window.dataLayer || []; |
||||
dataLayer.push({ |
||||
'ecommerce': { |
||||
'purchase': { |
||||
'actionField': { |
||||
'id': '<?php echo $order_id; ?>',
|
||||
'revenue': '<?php echo $order->get_total(); ?>',
|
||||
'currency': '<?php echo $currency; ?>'
|
||||
}, |
||||
'products': <?php echo json_encode($items); ?> |
||||
} |
||||
} |
||||
}); |
||||
|
||||
yaCounter96481053.reachGoal('purchase', { |
||||
'order_id': '<?php echo $order_id; ?>',
|
||||
'order_price': '<?php echo $order->get_total(); ?>',
|
||||
'currency': '<?php echo $currency; ?>',
|
||||
'items': <?php echo json_encode($items); ?> |
||||
}); |
||||
|
||||
fbq('track', 'Purchase', { |
||||
value: <?php echo $order->get_total(); ?>,
|
||||
currency: '<?php echo $currency; ?>',
|
||||
content_ids: [<?php echo implode(',', array_column($items, 'id')); ?>],
|
||||
content_type: 'product' |
||||
}); |
||||
</script> |
||||
<?php |
||||
} |
||||
} |
||||
|
||||
|
||||
// Функция событий Meta Pixel |
||||
|
||||
add_action('wp_head', function () { |
||||
global $site_env; |
||||
$show_pixel = false; |
||||
|
||||
// Для продакшена |
||||
if ($site_env->site_mode === 'production' && $site_env->site_region === 'ae') { |
||||
$show_pixel = true; |
||||
} |
||||
|
||||
// Для тестовых стендов AE |
||||
if ($site_env->site_mode === 'develope' && $site_env->site_region === 'ae') { |
||||
$show_pixel = true; |
||||
} |
||||
|
||||
// Дополнительные проверки |
||||
$skip_tracking = false; |
||||
|
||||
// Исключаем админов |
||||
if (current_user_can('administrator')) { |
||||
$skip_tracking = true; |
||||
} |
||||
|
||||
// Исключаем gp-test режим |
||||
if ($site_env->is_gp_test_mode) { |
||||
$skip_tracking = true; |
||||
} |
||||
|
||||
// Исключаем если есть параметр gp-test=1 в URL |
||||
if (isset($_GET['gp-test']) && $_GET['gp-test'] == '1') { |
||||
$skip_tracking = true; |
||||
} |
||||
|
||||
if ($show_pixel && !$skip_tracking) { |
||||
$context = [ |
||||
'currency' => get_woocommerce_currency(), |
||||
]; |
||||
|
||||
// Определяем тип страницы и подготавливаем данные |
||||
if (is_product()) { |
||||
// Страница товара - ViewContent |
||||
global $product; |
||||
if ($product && $product instanceof WC_Product) { |
||||
$context['pixel_event_type'] = 'ViewContent'; |
||||
$context['product'] = [ |
||||
'id' => $product->get_id(), |
||||
'name' => $product->get_name(), |
||||
'price' => floatval($product->get_price()), |
||||
'sku' => $product->get_sku(), |
||||
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '', |
||||
]; |
||||
} |
||||
} elseif (is_checkout() && !is_order_received_page()) { |
||||
// Страница чекаута - InitiateCheckout |
||||
$context['pixel_event_type'] = 'InitiateCheckout'; |
||||
if (WC()->cart && !WC()->cart->is_empty()) { |
||||
$context['cart_total'] = floatval(WC()->cart->get_total('edit')); |
||||
$context['cart_items'] = []; |
||||
$context['content_ids'] = []; |
||||
|
||||
foreach (WC()->cart->get_cart() as $cart_item) { |
||||
$product = $cart_item['data']; |
||||
if ($product && $product instanceof WC_Product) { |
||||
$item_data = [ |
||||
'item_id' => $product->get_id(), |
||||
'item_name' => $product->get_name(), |
||||
'price' => floatval($product->get_price()), |
||||
'quantity' => intval($cart_item['quantity']), |
||||
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '', |
||||
]; |
||||
$context['cart_items'][] = $item_data; |
||||
$context['content_ids'][] = $product->get_id(); |
||||
} |
||||
} |
||||
} |
||||
} elseif (is_order_received_page()) { |
||||
// Страница благодарности - Purchase |
||||
$order_id = get_query_var('order-received'); |
||||
if ($order_id) { |
||||
$order = wc_get_order($order_id); |
||||
if ($order && $order instanceof WC_Order) { |
||||
$context['pixel_event_type'] = 'Purchase'; |
||||
$order_items = []; |
||||
$content_ids = []; |
||||
|
||||
foreach ($order->get_items() as $item) { |
||||
if ($item instanceof WC_Order_Item_Product) { |
||||
$product = $item->get_product(); |
||||
if ($product && $product instanceof WC_Product) { |
||||
$order_items[] = [ |
||||
'item_id' => $product->get_id(), |
||||
'item_name' => $product->get_name(), |
||||
'price' => floatval($item->get_total() / $item->get_quantity()), |
||||
'quantity' => intval($item->get_quantity()), |
||||
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '', |
||||
]; |
||||
$content_ids[] = $product->get_id(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
$context['order'] = [ |
||||
'id' => $order->get_id(), |
||||
'total' => floatval($order->get_total()), |
||||
'currency' => $order->get_currency(), |
||||
'items' => $order_items, |
||||
'content_ids' => $content_ids, |
||||
'order_key' => $order->get_order_key(), |
||||
]; |
||||
} |
||||
} |
||||
} else { |
||||
// Все остальные страницы - PageView + подготовка для AddToCart |
||||
$context['pixel_event_type'] = 'PageView'; |
||||
$context['enable_addtocart'] = true; // Включаем обработчик AddToCart |
||||
|
||||
// Дополнительные данные для PageView |
||||
global $wp_query; |
||||
$context['page_data'] = [ |
||||
'page_title' => wp_get_document_title(), |
||||
'page_url' => home_url(add_query_arg(array(), $wp_query->request)), |
||||
'page_type' => get_page_type(), |
||||
]; |
||||
} |
||||
|
||||
// Рендерим шаблон |
||||
\Timber\Timber::render('templates/head-pixel-functions.twig', $context); |
||||
} |
||||
}); |
||||
|
||||
|
||||
function get_page_type() |
||||
{ |
||||
if (is_home() || is_front_page()) { |
||||
return 'home'; |
||||
} elseif (is_shop()) { |
||||
return 'shop'; |
||||
} elseif (is_product_category()) { |
||||
return 'category'; |
||||
} elseif (is_product_tag()) { |
||||
return 'tag'; |
||||
} elseif (is_search()) { |
||||
return 'search'; |
||||
} elseif (is_cart()) { |
||||
return 'cart'; |
||||
} elseif (is_account_page()) { |
||||
return 'account'; |
||||
} else { |
||||
return 'other'; |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
<? |
||||
|
||||
add_filter('wpseo_canonical', '__return_false'); |
||||
add_filter('wpseo_opengraph_url', '__return_false'); |
||||
add_filter('wpseo_add_x_default_hreflang', '__return_false'); |
||||
add_filter('wpseo_disable_adjacent_rel_links', '__return_true'); |
||||
|
||||
/* Добавление canonical-ссылок */ |
||||
add_action('wp_head', function () { |
||||
if (!is_admin()) { |
||||
static $canonical_added = false; |
||||
if ($canonical_added) |
||||
return; |
||||
$canonical_added = true; |
||||
$current_url = strtok(trailingslashit(home_url($_SERVER['REQUEST_URI'])), '?'); |
||||
echo '<link rel="canonical" href="' . esc_url($current_url) . '" />' . "\n"; |
||||
} |
||||
}); |
@ -0,0 +1,661 @@ |
||||
<?php |
||||
|
||||
use Timber; |
||||
use Timber\PostCollection; |
||||
use Timber\Integrations\WooCommerce\Product as TimberProduct; |
||||
|
||||
class CosmopetProduct extends TimberProduct { |
||||
|
||||
protected $sibling_categories = [ |
||||
'pa_compound', |
||||
'pa_dog-size', |
||||
'pa_reproductive-status', |
||||
'pa_age-of-the-cat', |
||||
'pa_age-of-the-dog', |
||||
'pa_series', |
||||
]; |
||||
|
||||
public function __construct ($pid = null) { |
||||
parent::__construct($pid); |
||||
} |
||||
|
||||
public function getComposition() { |
||||
return get_post_meta($this->id, '_composition', true); |
||||
} |
||||
|
||||
|
||||
public function getImageGallery($size) { |
||||
// Инициализируем массив для результатов |
||||
$images_array = array(); |
||||
|
||||
// Получаем ID основного изображения товара |
||||
$thumbnail_id = get_post_thumbnail_id($this->id); |
||||
// Добавляем основное изображение |
||||
if ($thumbnail_id) { |
||||
$images_array[] = array( |
||||
'url' => wp_get_attachment_image_url($thumbnail_id, $size), |
||||
'alt' => get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true) |
||||
); |
||||
} else { |
||||
// Используем заглушку, если нет основного изображения |
||||
$images_array[] = array( |
||||
'url' => wc_placeholder_img_src(), |
||||
'alt' => __('Placeholder image', 'woocommerce') |
||||
); |
||||
} |
||||
|
||||
// Получаем галерею изображений |
||||
if ($this->product && method_exists($this->product, 'get_gallery_image_ids')) { |
||||
$gallery_image_ids = $this->product->get_gallery_image_ids(); |
||||
} else { |
||||
$gallery_image_ids = []; |
||||
} |
||||
|
||||
// Добавляем изображения из галереи |
||||
if (!empty($gallery_image_ids)) { |
||||
foreach ($gallery_image_ids as $image_id) { |
||||
$image_url = wp_get_attachment_image_url($image_id, $size); |
||||
if ($image_url) { |
||||
$images_array[] = array( |
||||
'url' => $image_url, |
||||
'alt' => get_post_meta($image_id, '_wp_attachment_image_alt', true) |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return $images_array; |
||||
} |
||||
|
||||
|
||||
public function get_weight() { |
||||
$custom_measurement = get_post_meta($this->id, '_custom_measurement', true) ?: pll__('кг'); |
||||
$custom_size = get_post_meta($this->id, '_size_for_display', true) ?: ''; |
||||
|
||||
if ($custom_size){ |
||||
return $custom_size ? $custom_size . ' ' . $custom_measurement : ''; |
||||
} |
||||
else{ |
||||
if ($this->product && method_exists($this->product, 'get_weight')) { |
||||
return $this->product->get_weight() ? $this->product->get_weight() . ' ' . $custom_measurement : ''; |
||||
} |
||||
return ''; |
||||
} |
||||
return ''; |
||||
} |
||||
|
||||
public function getNumericWeight() { |
||||
$custom_size = get_post_meta($this->id, '_size_for_display', true) ?: ''; |
||||
|
||||
if ($custom_size){ |
||||
return floatval($custom_size); |
||||
} |
||||
else{ |
||||
if ($this->product && method_exists($this->product, 'get_weight')) { |
||||
return floatval($this->product->get_weight()); |
||||
} |
||||
return 0; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Получает CSS класс вкуса для товара |
||||
* @return string CSS класс вкуса (например, --food-fish, --treats-beef) |
||||
*/ |
||||
public function getTasteClass() { |
||||
// Получаем категории товара |
||||
$product_categories = $this->product->get_category_ids(); |
||||
|
||||
$is_food = false; |
||||
$is_treats = false; |
||||
// Определяем тип товара (корм или лакомство) |
||||
foreach ($product_categories as $cat) { |
||||
if ($cat == 365) { |
||||
$is_food = true; |
||||
} elseif ($cat == 367) { |
||||
$is_treats = true; |
||||
} |
||||
} |
||||
|
||||
// Определяем префикс типа товара |
||||
$type_prefix = ''; |
||||
if ($is_food) { |
||||
$type_prefix = 'food'; |
||||
} elseif ($is_treats) { |
||||
$type_prefix = 'treats'; |
||||
} else { |
||||
$type_prefix = 'acc'; // Дефолтный тип |
||||
} |
||||
|
||||
// Получаем термины атрибута compound (вкус) |
||||
$compound_terms = get_the_terms($this->id, 'pa_compound'); |
||||
$taste_suffix = ''; |
||||
|
||||
if ($compound_terms && !is_wp_error($compound_terms)) { |
||||
foreach ($compound_terms as $term) { |
||||
switch ($term->slug) { |
||||
case 'govyadina': |
||||
$taste_suffix = 'beef'; |
||||
break; |
||||
case 'indejka': |
||||
$taste_suffix = 'turkey'; |
||||
break; |
||||
case 'krolik': |
||||
$taste_suffix = 'rabbit'; |
||||
break; |
||||
case 'losos': |
||||
$taste_suffix = 'salmon'; |
||||
break; |
||||
case 'ryba': |
||||
$taste_suffix = 'fish'; |
||||
break; |
||||
case 'utka': |
||||
$taste_suffix = 'duck'; |
||||
break; |
||||
case 'yagnenok': |
||||
$taste_suffix = 'lamb'; |
||||
break; |
||||
} |
||||
|
||||
// Если нашли вкус, прерываем цикл |
||||
if ($taste_suffix) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Если вкус не найден, используем дефолтные суффиксы по типу товара |
||||
if (!$taste_suffix) { |
||||
if ($is_food) { |
||||
$taste_suffix = 'fish'; |
||||
} elseif ($is_treats) { |
||||
$taste_suffix = 'beef'; |
||||
} else { |
||||
$taste_suffix = ''; // Дефолтный суффикс |
||||
} |
||||
} |
||||
|
||||
return '--' . $type_prefix . '-' . $taste_suffix; |
||||
} |
||||
|
||||
public function getSizeSiblings() { |
||||
|
||||
$term = get_the_terms($this->id, 'pa_collection'); |
||||
if ($term) { |
||||
$term = $term[0]->term_id; |
||||
} else { |
||||
return []; |
||||
} |
||||
|
||||
$args = array( |
||||
'posts_per_page' => -1, |
||||
'post_type' => 'product', |
||||
'order' => 'ASC', |
||||
'orderby' => 'name', // Fixed parameter name (was 'order_by') |
||||
'tax_query' => [ |
||||
[ |
||||
'taxonomy' => 'pa_collection', |
||||
'terms' => $term, |
||||
'field' => 'id', |
||||
] |
||||
], |
||||
); |
||||
|
||||
$siblings = Timber::get_posts($args); |
||||
|
||||
// Create array with products and their weights |
||||
$siblings_with_weights = []; |
||||
foreach ($siblings as $sibling) { |
||||
$weight = $sibling->get_weight(); |
||||
$siblings_with_weights[] = [ |
||||
'post' => $sibling, |
||||
'weight' => floatval($weight) // Ensure weight is treated as a number |
||||
]; |
||||
} |
||||
|
||||
// Sort by weight |
||||
usort($siblings_with_weights, function($a, $b) { |
||||
return $a['weight'] <=> $b['weight']; |
||||
}); |
||||
|
||||
// Extract just the posts in sorted order |
||||
$sorted_siblings = array_map(function($item) { |
||||
return $item['post']; |
||||
}, $siblings_with_weights); |
||||
return $sorted_siblings; |
||||
} |
||||
|
||||
protected function get_category_products(){ |
||||
// Получаем все товары с похожими атрибутами |
||||
$args = array( |
||||
'posts_per_page' => -1, |
||||
'post_type' => 'product', |
||||
'order' => 'ASC', |
||||
'orderby' => 'name', |
||||
'post_status' => 'publish' |
||||
); |
||||
|
||||
error_log("get_category_products: Запрос товаров"); |
||||
$posts = Timber::get_posts($args); |
||||
error_log("get_category_products: Получено товаров: " . (is_array($posts) ? count($posts) : 'не массив')); |
||||
|
||||
// Преобразуем в массив, если это PostCollection или PostQuery |
||||
if ($posts instanceof \Timber\PostCollection || $posts instanceof \Timber\PostQuery) { |
||||
$result = $posts->get_posts(); |
||||
error_log("get_category_products: Преобразовано в массив: " . count($result)); |
||||
return $result; |
||||
} |
||||
|
||||
$result = is_array($posts) ? $posts : []; |
||||
error_log("get_category_products: Возвращаем: " . count($result)); |
||||
return $result; |
||||
} |
||||
|
||||
public function getProductConstructor() { |
||||
$all_products = $this->get_category_products(); |
||||
$constructor = []; |
||||
|
||||
// Получаем атрибуты текущего товара |
||||
$current_product_attributes = []; |
||||
$current_weight = $this->getNumericWeight(); |
||||
|
||||
// Получаем значения атрибутов текущего товара |
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
$terms = get_the_terms($this->id, $taxonomy); |
||||
if ($terms && !is_wp_error($terms)) { |
||||
$current_product_attributes[$taxonomy] = array_map(function($term) { |
||||
return $term->term_id; |
||||
}, $terms); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
// Получаем упорядоченные таксономии |
||||
$ordered_taxonomies = $this->getOrderedTaxonomies(); |
||||
|
||||
// Инициализируем конструктор для всех таксономий |
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
if (isset($current_product_attributes[$taxonomy])) { |
||||
$constructor[$taxonomy] = [ |
||||
'taxonomy' => $taxonomy, |
||||
'label' => $this->getAttributeLabel($taxonomy), |
||||
'current_value' => $this->getCurrentAttributeValue($taxonomy), |
||||
'options' => [] |
||||
]; |
||||
} |
||||
} |
||||
|
||||
// ОБХОДИМ МАССИВ ТОВАРОВ ТОЛЬКО ОДИН РАЗ |
||||
foreach ($all_products as $product) { |
||||
// Получаем все атрибуты сравниваемого товара за один раз |
||||
$compare_attributes = []; |
||||
$compare_weight = $product->getNumericWeight(); |
||||
|
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
$terms = get_the_terms($product->id, $taxonomy); |
||||
if ($terms && !is_wp_error($terms)) { |
||||
$compare_attributes[$taxonomy] = array_map(function($term) { |
||||
return $term->term_id; |
||||
}, $terms); |
||||
} |
||||
} |
||||
|
||||
// Проверяем, подходит ли товар для каждой таксономии |
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
// Пропускаем, если у текущего товара нет значения для этой категории |
||||
if (!isset($current_product_attributes[$taxonomy])) { |
||||
continue; |
||||
} |
||||
|
||||
// Проверяем, совпадают ли все остальные атрибуты и вес |
||||
$attributes_match = true; |
||||
|
||||
// Проверяем все атрибуты кроме текущей таксономии |
||||
foreach ($this->sibling_categories as $compare_taxonomy) { |
||||
if ($compare_taxonomy === $taxonomy) { |
||||
continue; // Пропускаем проверяемую категорию |
||||
} |
||||
|
||||
// Если у текущего товара есть значение для этой категории |
||||
if (isset($current_product_attributes[$compare_taxonomy])) { |
||||
// Проверяем, есть ли у сравниваемого товара значение для этой категории |
||||
if (!isset($compare_attributes[$compare_taxonomy])) { |
||||
$attributes_match = false; |
||||
break; |
||||
} |
||||
|
||||
// Проверяем, совпадают ли значения |
||||
$current_values = $current_product_attributes[$compare_taxonomy]; |
||||
$compare_values = $compare_attributes[$compare_taxonomy]; |
||||
|
||||
if (array_diff($current_values, $compare_values) !== [] || |
||||
array_diff($compare_values, $current_values) !== []) { |
||||
$attributes_match = false; |
||||
break; |
||||
} |
||||
} |
||||
// Если у текущего товара НЕТ значения для этой категории, |
||||
// то у сравниваемого товара тоже не должно быть значения |
||||
else { |
||||
if (isset($compare_attributes[$compare_taxonomy])) { |
||||
$attributes_match = false; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Проверяем вес (числовое сравнение) |
||||
if (abs($current_weight - $compare_weight) > 0.01) { |
||||
$attributes_match = false; |
||||
} |
||||
|
||||
// Если все атрибуты и вес совпадают, добавляем товар в группу |
||||
if ($attributes_match) { |
||||
$product_terms = get_the_terms($product->id, $taxonomy); |
||||
if ($product_terms && !is_wp_error($product_terms)) { |
||||
foreach ($product_terms as $term) { |
||||
$option_key = $term->term_id; |
||||
if (!isset($constructor[$taxonomy]['options'][$option_key])) { |
||||
$constructor[$taxonomy]['options'][$option_key] = [ |
||||
'term_id' => $term->term_id, |
||||
'name' => $this->getTermName($term->term_id, $taxonomy), |
||||
'slug' => $term->slug, |
||||
'icon' => $this->getTermIcon($term->term_id, $taxonomy), |
||||
'products' => [] |
||||
]; |
||||
} |
||||
$constructor[$taxonomy]['options'][$option_key]['products'][] = $product; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Сортируем опции по порядку терминов для каждой таксономии |
||||
foreach ($constructor as $taxonomy => $taxonomy_data) { |
||||
if (isset($ordered_taxonomies[$taxonomy])) { |
||||
$ordered_options = []; |
||||
foreach ($ordered_taxonomies[$taxonomy] as $term) { |
||||
if (isset($constructor[$taxonomy]['options'][$term->term_id])) { |
||||
$ordered_options[$term->term_id] = $constructor[$taxonomy]['options'][$term->term_id]; |
||||
} |
||||
} |
||||
$constructor[$taxonomy]['options'] = $ordered_options; |
||||
} |
||||
|
||||
} |
||||
|
||||
// Сортируем итоговый конструктор по порядку таксономий |
||||
$ordered_constructor = []; |
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
if (isset($constructor[$taxonomy])) { |
||||
$ordered_constructor[$taxonomy] = $constructor[$taxonomy]; |
||||
} |
||||
} |
||||
|
||||
return $ordered_constructor; |
||||
} |
||||
|
||||
protected function getAttributeLabel($taxonomy) { |
||||
// Fallback на старые метки |
||||
$labels = [ |
||||
'pa_age-of-the-cat' => pll__('ВОЗРАСТ КОШКИ'), |
||||
'pa_age-of-the-dog' => pll__('ВОЗРАСТ СОБАКИ'), |
||||
'pa_dog-size' => pll__('РАЗМЕР СОБАКИ'), |
||||
'pa_compound' => pll__('ВКУС КОРМА'), |
||||
'pa_reproductive-status' => pll__('ВИД КОШКИ'), |
||||
'pa_series' => pll__('СЕРИЯ'), |
||||
]; |
||||
|
||||
return $labels[$taxonomy] ?? wc_attribute_label($taxonomy); |
||||
} |
||||
|
||||
protected function getCurrentAttributeValue($taxonomy) { |
||||
$terms = get_the_terms($this->id, $taxonomy); |
||||
if ($terms && !is_wp_error($terms)) { |
||||
return $terms[0]->term_id; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Получает таксономии в правильном порядке с учетом сортировки |
||||
* @return array Массив таксономий с их порядком |
||||
*/ |
||||
protected function getOrderedTaxonomies() { |
||||
$ordered_taxonomies = []; |
||||
|
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
// Получаем все термины для данной таксономии |
||||
$terms = get_terms([ |
||||
'taxonomy' => $taxonomy, |
||||
'hide_empty' => false, |
||||
'orderby' => 'menu_order', |
||||
'order' => 'ASC' |
||||
]); |
||||
|
||||
// Если menu_order не работает, пробуем сортировку по name |
||||
if (is_wp_error($terms) || empty($terms)) { |
||||
$terms = get_terms([ |
||||
'taxonomy' => $taxonomy, |
||||
'hide_empty' => false, |
||||
'orderby' => 'name', |
||||
'order' => 'ASC' |
||||
]); |
||||
} |
||||
|
||||
if (!is_wp_error($terms) && !empty($terms)) { |
||||
$ordered_taxonomies[$taxonomy] = $terms; |
||||
} |
||||
} |
||||
|
||||
return $ordered_taxonomies; |
||||
} |
||||
|
||||
/** |
||||
* Получает таксономии в правильном порядке для отображения |
||||
* @return array Массив таксономий в порядке отображения |
||||
*/ |
||||
public function getOrderedSiblingCategories() { |
||||
return $this->sibling_categories; |
||||
} |
||||
|
||||
/** |
||||
* Получает таксономии с их метками в правильном порядке |
||||
* @return array Массив таксономий с метками |
||||
*/ |
||||
public function getOrderedTaxonomiesWithLabels() { |
||||
$taxonomies_with_labels = []; |
||||
|
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
$label = $this->getAttributeLabel($taxonomy); |
||||
$taxonomies_with_labels[$taxonomy] = $label; |
||||
} |
||||
|
||||
return $taxonomies_with_labels; |
||||
} |
||||
|
||||
/** |
||||
* Получает иконку термина из ACF поля |
||||
* @param int $term_id ID термина |
||||
* @param string $taxonomy Таксономия |
||||
* @return string|false URL иконки или false если иконка не найдена |
||||
*/ |
||||
public function getTermIcon($term_id, $taxonomy) { |
||||
// Проверяем, есть ли ACF поле 'icon' у термина |
||||
$icon = get_field('icon', $taxonomy . '_' . $term_id); |
||||
|
||||
if ($icon) { |
||||
return $icon; |
||||
} |
||||
|
||||
// Если ACF поле не найдено, возвращаем false |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Получает название термина с приоритетом описания |
||||
* @param int $term_id ID термина |
||||
* @param string $taxonomy Таксономия |
||||
* @return string Название термина |
||||
*/ |
||||
public function getTermName($term_id, $taxonomy) { |
||||
$term = get_term($term_id, $taxonomy); |
||||
|
||||
if (!$term || is_wp_error($term)) { |
||||
return ''; |
||||
} |
||||
|
||||
// Сначала пытаемся получить описание термина |
||||
$description = get_term_meta($term_id, 'description', true); |
||||
|
||||
// Также пробуем получить из ACF поля |
||||
$acf_description = get_field('description', $taxonomy . '_' . $term_id); |
||||
|
||||
// Пробуем получить из стандартного поля термина |
||||
$term_description = $term->description; |
||||
|
||||
// Пробуем ACF поле сначала |
||||
if (!empty($acf_description)) { |
||||
return $acf_description; |
||||
} |
||||
|
||||
// Потом стандартное поле термина |
||||
if (!empty($term_description)) { |
||||
return $term_description; |
||||
} |
||||
|
||||
// Потом обычное мета-поле |
||||
if (!empty($description)) { |
||||
return $description; |
||||
} |
||||
|
||||
return $term->name; |
||||
} |
||||
|
||||
/** |
||||
* Получает количество товара в корзине |
||||
* @return int Количество товара в корзине |
||||
*/ |
||||
public function getCartQuantity() { |
||||
if (!function_exists('WC') || !WC()->cart) { |
||||
return 0; |
||||
} |
||||
|
||||
$cart = WC()->cart; |
||||
$cart_item_key = $this->getCartItemKey(); |
||||
|
||||
if ($cart_item_key) { |
||||
$cart_item = $cart->get_cart_item($cart_item_key); |
||||
return $cart_item ? $cart_item['quantity'] : 0; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Получает ключ товара в корзине |
||||
* @return string|false Ключ товара в корзине или false если товар не найден |
||||
*/ |
||||
public function getCartItemKey() { |
||||
if (!function_exists('WC') || !WC()->cart) { |
||||
return false; |
||||
} |
||||
|
||||
$cart = WC()->cart; |
||||
|
||||
foreach ($cart->get_cart() as $cart_item_key => $cart_item) { |
||||
if ($cart_item['product_id'] == $this->id) { |
||||
return $cart_item_key; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Проверяет, есть ли товар в корзине |
||||
* @return bool True если товар в корзине, false если нет |
||||
*/ |
||||
public function isInCart() { |
||||
return $this->getCartQuantity() > 0; |
||||
} |
||||
|
||||
|
||||
|
||||
public function getAllAttributes() { |
||||
$attributes = []; |
||||
foreach ($this->sibling_categories as $taxonomy) { |
||||
$terms = get_the_terms($this->id, $taxonomy); |
||||
if ($terms && !is_wp_error($terms)) { |
||||
$attributes[$taxonomy] = []; |
||||
foreach ($terms as $term) { |
||||
$attributes[$taxonomy][] = [ |
||||
'term_id' => $term->term_id, |
||||
'name' => $term->name, |
||||
'slug' => $term->slug |
||||
]; |
||||
} |
||||
} |
||||
} |
||||
return $attributes; |
||||
} |
||||
|
||||
public function getPrice(){ |
||||
$price = $this->product->get_price() . ' ' . get_woocommerce_currency_symbol(); |
||||
return $price; |
||||
} |
||||
|
||||
public function getRegularPrice(){ |
||||
$regular_price = $this->product->get_regular_price() . ' ' . get_woocommerce_currency_symbol(); |
||||
if ($regular_price != $this->getPrice()){ |
||||
return $regular_price; |
||||
} |
||||
else{ |
||||
return ''; |
||||
} |
||||
} |
||||
|
||||
public function getPriceDifferenceInPercent(){ |
||||
$regular_price = $this->product->get_regular_price(); |
||||
$price = $this->product->get_price(); |
||||
$difference = ($regular_price - $price) / $regular_price * 100; |
||||
$difference = round($difference, 0) . '%'; |
||||
return $difference; |
||||
} |
||||
|
||||
public function getReviews(){ |
||||
|
||||
$reviews = get_posts(array( |
||||
'post_type' => 'p_reviews', |
||||
'numberposts' => -1, |
||||
'meta_query' => array( |
||||
array( |
||||
'key' => 'products', |
||||
'value' => '"' . $this->id . '"', |
||||
'compare' => 'LIKE' |
||||
) |
||||
) |
||||
)); |
||||
|
||||
return $reviews; |
||||
} |
||||
|
||||
public function getRelatedProducts(){ |
||||
$related_products = []; |
||||
$related_products_ids = $this->product->get_upsell_ids(); |
||||
if ($related_products_ids) { |
||||
foreach ($related_products_ids as $related_id) { |
||||
$related_product = Timber::get_post($related_id); |
||||
if ($related_product) { |
||||
$related_products[] = $related_product; |
||||
} |
||||
} |
||||
} |
||||
return $related_products; |
||||
} |
||||
}; |
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 481 B After Width: | Height: | Size: 481 B |
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 456 B |
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 954 B |
Before Width: | Height: | Size: 455 B After Width: | Height: | Size: 455 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,20 @@ |
||||
<?php |
||||
use Timber\Timber; |
||||
global $post; |
||||
|
||||
$context = Timber::get_context(); |
||||
|
||||
// Получаем текущий продукт |
||||
$product_id = get_the_ID(); |
||||
$product = wc_get_product($product_id); |
||||
|
||||
// Добавляем продукт в контекст |
||||
$context['product'] = $product; |
||||
$context['post'] = Timber::get_post(); |
||||
|
||||
include_module('shop'); |
||||
include_component('shop', 'reviews'); |
||||
|
||||
?> |
||||
<?php |
||||
Timber::render('woocommerce/single-product.twig', $context); |
@ -0,0 +1,49 @@ |
||||
/* Стили для загрузки корзины на single странице */ |
||||
|
||||
.product-incart__wrap.loading { |
||||
position: relative; |
||||
pointer-events: none; |
||||
opacity: 0.7; |
||||
} |
||||
|
||||
.product-incart__wrap.loading::before { |
||||
content: ''; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
background: rgba(255, 255, 255, 0.8); |
||||
z-index: 10; |
||||
} |
||||
|
||||
.product-incart__wrap.loading::after { |
||||
content: ''; |
||||
position: absolute; |
||||
top: 50%; |
||||
left: 50%; |
||||
width: 20px; |
||||
height: 20px; |
||||
margin: -10px 0 0 -10px; |
||||
border: 2px solid #f3f3f3; |
||||
border-top: 2px solid #3498db; |
||||
border-radius: 50%; |
||||
animation: spin 1s linear infinite; |
||||
z-index: 11; |
||||
} |
||||
|
||||
@keyframes spin { |
||||
0% { transform: rotate(0deg); } |
||||
100% { transform: rotate(360deg); } |
||||
} |
||||
|
||||
/* Стили для кнопок во время загрузки */ |
||||
.product-incart__wrap.loading .product-incart__btn { |
||||
opacity: 0.5; |
||||
pointer-events: none; |
||||
} |
||||
|
||||
/* Стили для счетчика во время загрузки */ |
||||
.product-incart__wrap.loading .product-incart__count { |
||||
opacity: 0.5; |
||||
} |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 481 B After Width: | Height: | Size: 481 B |
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 456 B |
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 954 B |
Before Width: | Height: | Size: 455 B After Width: | Height: | Size: 455 B |
@ -0,0 +1,156 @@ |
||||
<?php |
||||
|
||||
$product; |
||||
$product_type; |
||||
$context_for_twig; |
||||
|
||||
// Получаем данные продукта |
||||
$product_id = get_the_ID(); |
||||
$product = wc_get_product($product_id); |
||||
|
||||
// Проверяем, что продукт существует |
||||
if (!$product) { |
||||
return; // Просто выходим, если продукт не найден |
||||
} |
||||
|
||||
$attributes = []; |
||||
$product_attributes = $product->get_attributes(); |
||||
|
||||
/* Получение категорий */ |
||||
$tags = get_the_terms($product_id, 'product_cat'); |
||||
if (!empty($tags) && !is_wp_error($tags)) { |
||||
foreach ($tags as $tag) { |
||||
$context_for_twig['product_tags'][] = $tag->name; |
||||
} |
||||
} |
||||
|
||||
/* Получение атрибутов */ |
||||
if (!empty($product_attributes)) { |
||||
foreach ($product_attributes as $taxonomy => $attribute) { |
||||
if ($attribute->is_taxonomy()) { |
||||
$terms = wc_get_product_terms($product_id, $taxonomy, ['fields' => 'all']); |
||||
// $tags = wc_get_product_terms( $product_id, 'pa_catalog_tags' ); |
||||
|
||||
if (!empty($terms)) { |
||||
$attr_values = []; |
||||
foreach ($terms as $term) { |
||||
$attr_values[] = [ |
||||
'name' => $term->name, |
||||
'slug' => $term->slug, |
||||
'term_id' => $term->term_id, |
||||
'link' => get_term_link($term->term_id, $taxonomy), |
||||
]; |
||||
} |
||||
$attributes[wc_attribute_label($taxonomy)] = $attr_values; |
||||
} |
||||
} else { |
||||
$attributes[wc_attribute_label($taxonomy)] = $attribute->get_options(); |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
$context_for_twig['product_attributes'] = $attributes; |
||||
|
||||
/* Получение вариаций товара */ |
||||
if ($product->is_type('variable')) { |
||||
$available_variations = $product->get_available_variations(); |
||||
$variations_data = []; |
||||
|
||||
foreach ($available_variations as $variation) { |
||||
$variation_id = $variation['variation_id']; |
||||
$variation_obj = wc_get_product($variation_id); |
||||
|
||||
$variations_data[] = [ |
||||
'variation_id' => $variation_id, |
||||
'price' => $variation_obj->get_price(), |
||||
'regular_price' => $variation_obj->get_regular_price(), |
||||
'sale_price' => $variation_obj->get_sale_price(), |
||||
'attributes' => $variation['attributes'] |
||||
]; |
||||
} |
||||
|
||||
$context_for_twig['variations'] = $variations_data; |
||||
} |
||||
|
||||
/* ACF-поля и мета-данные продукта */ |
||||
$meta_fields = [ |
||||
'composition' => get_post_meta($product_id, '_composition', true), |
||||
'feeding_recommendations' => get_post_meta($product_id, '_feeding_recommendations', true), // Ранее выводились HTML-полем «Рекомендации по кормлению» |
||||
'feeding_recommendations_table' => get_field('feeding_recommendations_table', get_post(get_field('p_tables_field', $product_id))) , // Таблица «Рекомендации по кормлению» |
||||
'nutritional_value' => get_post_meta($product_id, '_nutritional_value', true), |
||||
'vitamins' => get_post_meta($product_id, '_vitamins', true), |
||||
'additives' => get_post_meta($product_id, '_additives', true), |
||||
'energy_value' => get_post_meta($product_id, '_energy_value', true), |
||||
'important' => get_post_meta($product_id, '_important', true), |
||||
]; |
||||
|
||||
$context_for_twig['product_meta'] = $meta_fields; |
||||
|
||||
|
||||
/* Товар оформляется по подписке? */ |
||||
$context_for_twig['is_subscription'] = $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' )) ? true : false; |
||||
|
||||
$context = Timber::get_context(); |
||||
$post = Timber::get_post(); |
||||
$context['post'] = $post; |
||||
$context['wc_breadcrumbs'] = array(); |
||||
|
||||
if (function_exists('woocommerce_breadcrumb')) { |
||||
$args = array( |
||||
'delimiter' => '', |
||||
'wrap_before' => '', |
||||
'wrap_after' => '', |
||||
'before' => '', |
||||
'after' => '', |
||||
'home' => _x('Home', 'breadcrumb', 'woocommerce'), |
||||
); |
||||
|
||||
$breadcrumbs = new WC_Breadcrumb(); |
||||
$breadcrumbs->generate(); |
||||
|
||||
$formatted_breadcrumbs = array(); |
||||
foreach ($breadcrumbs->get_breadcrumb() as $crumb) { |
||||
$formatted_breadcrumbs[] = array( |
||||
'text' => $crumb[0], |
||||
'url' => $crumb[1] |
||||
); |
||||
} |
||||
|
||||
$context['wc_breadcrumbs'] = $formatted_breadcrumbs; |
||||
} |
||||
|
||||
$context['product'] = $product; |
||||
|
||||
$context['related_products'] = array(); |
||||
$related_products_ids = $product->get_upsell_ids(); |
||||
if ($related_products_ids) { |
||||
foreach ($related_products_ids as $related_id) { |
||||
$related_product = wc_get_product($related_id); |
||||
if ($related_product) { |
||||
$context['related_products'][] = $related_product; |
||||
} |
||||
} |
||||
} |
||||
if (class_exists('WCS_ATT_Product_Schemes')) { |
||||
$schemes = WCS_ATT_Product_Schemes::get_subscription_schemes($product); |
||||
$context['has_subscription_options'] = !empty($schemes); |
||||
} else { |
||||
$context['has_subscription_options'] = false; |
||||
} |
||||
|
||||
Timber::render('_pages/shop/product-single.twig', $context); |
||||
|
||||
// Подключение стилей для загрузки корзины |
||||
add_action('wp_enqueue_scripts', 'single_product_cart_styles'); |
||||
|
||||
function single_product_cart_styles() { |
||||
if (is_product()) { |
||||
wp_enqueue_style( |
||||
'single-product-cart-loading', |
||||
get_template_directory_uri() . '/modules/shop/components/single-product/assets/css/cart-loading.css', |
||||
array(), |
||||
'1.0.0' |
||||
); |
||||
} |
||||
} |
@ -1,152 +0,0 @@ |
||||
<?php |
||||
|
||||
|
||||
$product; |
||||
$product_type; |
||||
$context_for_twig; |
||||
|
||||
if (function_exists('is_product') && is_product()) { |
||||
$product_id = get_the_ID(); |
||||
$product = wc_get_product($product_id); |
||||
|
||||
$attributes = []; |
||||
$product_attributes = $product->get_attributes(); |
||||
|
||||
/* Получение категорий */ |
||||
$tags = get_the_terms($product_id, 'product_cat'); |
||||
if (!empty($tags) && !is_wp_error($tags)) { |
||||
foreach ($tags as $tag) { |
||||
$context_for_twig['product_tags'][] = $tag->name; |
||||
} |
||||
} |
||||
|
||||
/* Получение атрибутов */ |
||||
if (!empty($product_attributes)) { |
||||
foreach ($product_attributes as $taxonomy => $attribute) { |
||||
if ($attribute->is_taxonomy()) { |
||||
$terms = wc_get_product_terms($product_id, $taxonomy, ['fields' => 'all']); |
||||
// $tags = wc_get_product_terms( $product_id, 'pa_catalog_tags' ); |
||||
|
||||
if (!empty($terms)) { |
||||
$attr_values = []; |
||||
foreach ($terms as $term) { |
||||
$attr_values[] = [ |
||||
'name' => $term->name, |
||||
'slug' => $term->slug, |
||||
'term_id' => $term->term_id, |
||||
'link' => get_term_link($term->term_id, $taxonomy), |
||||
]; |
||||
} |
||||
$attributes[wc_attribute_label($taxonomy)] = $attr_values; |
||||
} |
||||
} else { |
||||
$attributes[wc_attribute_label($taxonomy)] = $attribute->get_options(); |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
$context_for_twig['product_attributes'] = $attributes; |
||||
|
||||
/* Получение вариаций товара */ |
||||
if ($product->is_type('variable')) { |
||||
$available_variations = $product->get_available_variations(); |
||||
$variations_data = []; |
||||
|
||||
foreach ($available_variations as $variation) { |
||||
$variation_id = $variation['variation_id']; |
||||
$variation_obj = wc_get_product($variation_id); |
||||
|
||||
$variations_data[] = [ |
||||
'variation_id' => $variation_id, |
||||
'price' => $variation_obj->get_price(), |
||||
'regular_price' => $variation_obj->get_regular_price(), |
||||
'sale_price' => $variation_obj->get_sale_price(), |
||||
'attributes' => $variation['attributes'] |
||||
]; |
||||
} |
||||
|
||||
$context_for_twig['variations'] = $variations_data; |
||||
} |
||||
|
||||
/* ACF-поля и мета-данные продукта */ |
||||
$meta_fields = [ |
||||
'composition' => get_post_meta($product_id, '_composition', true), |
||||
'feeding_recommendations' => get_post_meta($product_id, '_feeding_recommendations', true), // Ранее выводились HTML-полем «Рекомендации по кормлению» |
||||
'feeding_recommendations_table' => get_field('feeding_recommendations_table', get_post(get_field('p_tables_field', $product_id))) , // Таблица «Рекомендации по кормлению» |
||||
'nutritional_value' => get_post_meta($product_id, '_nutritional_value', true), |
||||
'vitamins' => get_post_meta($product_id, '_vitamins', true), |
||||
'additives' => get_post_meta($product_id, '_additives', true), |
||||
'energy_value' => get_post_meta($product_id, '_energy_value', true), |
||||
'important' => get_post_meta($product_id, '_important', true), |
||||
]; |
||||
|
||||
$context_for_twig['product_meta'] = $meta_fields; |
||||
|
||||
|
||||
|
||||
/* Товар оформляется по подписке? */ |
||||
$context_for_twig['is_subscription'] = $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' )) ? true : false; |
||||
} |
||||
|
||||
|
||||
add_filter('timber/context', function ($context) use ($context_for_twig) { |
||||
return array_merge($context, $context_for_twig); |
||||
}); |
||||
|
||||
|
||||
|
||||
$context = Timber::get_context(); |
||||
$post = Timber::get_post(); |
||||
$context['post'] = $post; |
||||
|
||||
$context['wc_breadcrumbs'] = array(); |
||||
|
||||
if (function_exists('woocommerce_breadcrumb')) { |
||||
$args = array( |
||||
'delimiter' => '', |
||||
'wrap_before' => '', |
||||
'wrap_after' => '', |
||||
'before' => '', |
||||
'after' => '', |
||||
'home' => _x('Home', 'breadcrumb', 'woocommerce'), |
||||
); |
||||
|
||||
$breadcrumbs = new WC_Breadcrumb(); |
||||
$breadcrumbs->generate(); |
||||
|
||||
$formatted_breadcrumbs = array(); |
||||
foreach ($breadcrumbs->get_breadcrumb() as $crumb) { |
||||
$formatted_breadcrumbs[] = array( |
||||
'text' => $crumb[0], |
||||
'url' => $crumb[1] |
||||
); |
||||
} |
||||
|
||||
$context['wc_breadcrumbs'] = $formatted_breadcrumbs; |
||||
} |
||||
|
||||
$product_id = get_the_ID(); |
||||
$product = wc_get_product($product_id); |
||||
|
||||
$context['product'] = $product; |
||||
|
||||
$context['related_products'] = array(); |
||||
$related_products_ids = $product->get_upsell_ids(); |
||||
if ($related_products_ids) { |
||||
foreach ($related_products_ids as $related_id) { |
||||
$related_product = wc_get_product($related_id); |
||||
if ($related_product) { |
||||
$context['related_products'][] = $related_product; |
||||
} |
||||
} |
||||
} |
||||
if (class_exists('WCS_ATT_Product_Schemes')) { |
||||
$schemes = WCS_ATT_Product_Schemes::get_subscription_schemes($product); |
||||
$context['has_subscription_options'] = !empty($schemes); |
||||
} else { |
||||
$context['has_subscription_options'] = false; |
||||
} |
||||
|
||||
|
||||
Timber::render('_pages/shop/product-single.twig', $context); |
@ -1,29 +0,0 @@ |
||||
<?php |
||||
|
||||
|
||||
|
||||
$context = Timber::get_context(); |
||||
|
||||
include_component('shop', 'reviews'); |
||||
include_component('shop', 'product-card'); |
||||
|
||||
$reviews = get_posts(array( |
||||
'post_type' => 'p_reviews', |
||||
'numberposts' => -1, |
||||
)); |
||||
|
||||
$product_id = get_the_ID(); |
||||
$product = wc_get_product($product_id); |
||||
$context['related_products'] = array(); |
||||
$related_products_ids = $product->get_upsell_ids(); |
||||
if ($related_products_ids) { |
||||
foreach ($related_products_ids as $related_id) { |
||||
$related_product = wc_get_product($related_id); |
||||
if ($related_product) { |
||||
$context['related_products'][] = $related_product; |
||||
} |
||||
} |
||||
} |
||||
|
||||
$context['reviews'] = $reviews; |
||||
Timber::render('shop/single-product_page.twig', $context); |
@ -1,6 +0,0 @@ |
||||
<?php |
||||
|
||||
include_module('author'); |
||||
include_component('author', 'author-single'); |
||||
|
||||
?> |
@ -1,6 +0,0 @@ |
||||
<?php |
||||
|
||||
include_module('blog'); |
||||
include_component('blog', 'single'); |
||||
|
||||
?> |
@ -1,21 +0,0 @@ |
||||
<?php |
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) { |
||||
exit; |
||||
} |
||||
|
||||
if (is_product()) { |
||||
|
||||
include_module('shop'); |
||||
global $product; |
||||
// Get the product ID |
||||
$product_id = get_the_ID(); |
||||
if ($product_id == 1105){ |
||||
include_component('shop', 'single-product_new'); |
||||
} |
||||
else{ |
||||
include_component('shop', 'single-product'); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,26 @@ |
||||
<?php |
||||
/** |
||||
* The Template for displaying all single posts |
||||
* |
||||
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/ |
||||
*/ |
||||
|
||||
namespace App; |
||||
|
||||
use Timber\Timber; |
||||
|
||||
$context = Timber::context(); |
||||
$post = $context['post']; |
||||
|
||||
if ( is_singular('post') ) { |
||||
include_module('blog'); |
||||
include_component('blog', 'single'); |
||||
} elseif ( is_singular('blog_author') ) { |
||||
include_module('author'); |
||||
include_component('author', 'author-single'); |
||||
} else { |
||||
// Для других типов записей |
||||
$templates = [ 'views/single-' . get_post_type() . '.twig', 'views/single.twig' ]; |
||||
} |
||||
|
||||
Timber::render( $templates, $context ); |
@ -1,19 +0,0 @@ |
||||
{% extends 'profile/profile-base.twig' %} |
||||
|
||||
{% block profile_content%} |
||||
<div class="cabinet__orders cabinet-content {% if fn('is_wc_endpoint_url') %}active hide{% endif %}"> |
||||
<!-- <div class="cabinet-card cabinet-card--green-white cabinet__subscription-pc"> |
||||
<div class="cabinet-card__content"> |
||||
<p class="cabinet-card__title">{{ function('pll_e', 'Feed subscription') }}</p> |
||||
<div class="cabinet-card__element"> |
||||
<p class="cabinet-card__text">{{ function('pll_e', 'Weekly food delivery for your pet') }}</p> |
||||
</div> |
||||
<div class="cabinet-card__element"> |
||||
<button class="button button--gradient button--high-46 form-sub__btn">{{ function('pll_e', 'Get details') }}</button> |
||||
</div> |
||||
</div> |
||||
</div> --> |
||||
{% include 'profile/order-list.twig' %} |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
@ -1,27 +0,0 @@ |
||||
{% set profile_pg = 1 %} |
||||
|
||||
{% extends 'profile/profile-base.twig' %} |
||||
|
||||
{% block profile_content%} |
||||
<div class="cabinet__orders cabinet-content {% if fn('is_wc_endpoint_url') %}active hide{% endif %}"> |
||||
<!-- <div class="cabinet-card cabinet-card--green-white cabinet__subscription-pc"> |
||||
<div class="cabinet-card__content"> |
||||
<p class="cabinet-card__title">{{ function('pll_e', 'Feed subscription') }}</p> |
||||
<div class="cabinet-card__element"> |
||||
<p class="cabinet-card__text">{{ function('pll_e', 'Weekly food delivery for your pet') }}</p> |
||||
</div> |
||||
<div class="cabinet-card__element"> |
||||
<button class="button button--gradient button--high-46 form-sub__btn">{{ function('pll_e', 'Get details') }}</button> |
||||
</div> |
||||
</div> |
||||
</div> --> |
||||
{% include 'profile/order-list.twig' %} |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
</main> |
||||
|
||||
|
||||
{% endblock %} |
||||
|
@ -1,5 +1,9 @@ |
||||
{% for post in posts %} |
||||
{% include '_blocks/shop/archive-product-tease.twig' with {post: post} %} |
||||
|
||||
{% set class = post.getTasteClass %} |
||||
{% set section = class ~ ' product-archive-card' %} |
||||
{% include 'shop/product-card.twig' with {_product : fn('wc_get_product', post.id), section: section} %} |
||||
|
||||
{% endfor %} |
||||
|
||||
{% if not ended %} |
@ -0,0 +1,26 @@ |
||||
{% set in_stock = post.meta('_stock_status') == 'instock' %} |
||||
|
||||
{% set cart_quantity = post.getCartQuantity %} |
||||
{% if in_stock %} |
||||
{% if post.getCartQuantity == 0 %} |
||||
{{ function('do_action', 'woocommerce_' ~ post.product.get_type() ~ '_add_to_cart') }} |
||||
{% else %} |
||||
<div class="product-incart__wrap" data-product-id="{{ post.id }}"> |
||||
<div class="product-incart counter"> |
||||
<button class="product-incart__btn product-incart__btn--minus" data-key="{{post.getCartItemKey}}" data-action="decrease"></button> |
||||
|
||||
<div class="product-incart__count" data-key="{{post.getCartItemKey}}">{{ cart_quantity }}</div> |
||||
<button class="product-incart__btn product-incart__btn--plus" data-key="{{post.getCartItemKey}}" data-action="increase"></button> |
||||
</div> |
||||
<div class="product-incart__label"> |
||||
{{fn('pll_e', 'В корзине')}} |
||||
</div> |
||||
</div> |
||||
{% endif %} |
||||
{% else %} |
||||
<div class="detail-block-form__item detail-block-form__item--tn"> |
||||
<button type="button" data-pname="{{ product.title }}" class="to-know open-to-know" > |
||||
<p>{{ function('pll_e', 'Узнать о поступлении') }}</p> |
||||
</button> |
||||
</div> |
||||
{% endif %} |