PluginProbe
Tiered Pricing Table for WooCommerce / trunk
Tiered Pricing Table for WooCommerce vtrunk
7.1.7 7.1.5 7.1.4 7.1.1 6.5.0 6.4.0 6.1.0 trunk 1.0 1.1 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 All 90 releases
tier-pricing-table / src / Services / ProductPageService.php

ProductPageService.php in Tiered Pricing Table for WooCommerce trunk, at src/Services/ProductPageService.php

324 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php namespace TierPricingTable\Services;
2
3 use Exception;
4 use TierPricingTable\Admin\ProductPage\TieredPricingTab;
5 use TierPricingTable\Core\ServiceContainerTrait;
6 use TierPricingTable\PriceManager;
7 use TierPricingTable\PricingTable;
8 use TierPricingTable\Settings\Sections\GeneralSection\Subsections\ProductPagePriceSubsection;
9 use TierPricingTable\TierPricingTablePlugin;
10 use WC_Product;
11 use WC_Product_Data_Store_CPT;
12 use WP_Post;
13
14 /**
15 * Class ProductPageHandler
16 *
17 * @package TierPricingTable\Frontend
18 */
19 class ProductPageService {
20
21 use ServiceContainerTrait;
22
23 public function __construct() {
24
25 add_action( 'woocommerce_quantity_input_classes', function ( $classes, $product ) {
26 if ( $product instanceof WC_Product ) {
27 $classes[] = 'quantity-input-product-' . $product->get_id();
28 }
29
30 return $classes;
31 }, 10, 2 );
32
33 add_filter( 'woocommerce_available_variation', function ( $data, WC_Product $product ) {
34 $data['parent_id'] = $product->get_id();
35
36 return $data;
37 }, 10, 2 );
38
39 // Wrap price
40 add_action( 'woocommerce_get_price_html', array( $this, 'wrapPrice' ), 101, 2 );
41
42 // Render price table
43 add_action( $this->getContainer()->getSettings()->get( 'position_hook',
44 'woocommerce_before_add_to_cart_button' ), array(
45 $this,
46 'renderPricingTableOnProductPage',
47 ), - 999 );
48
49 // Get table for variation
50 add_action( 'wc_ajax_get_pricing_table', array( $this, 'getVariationPricingTable' ), 10, 1 );
51
52 // Render tooltip if enabled
53 add_filter( 'woocommerce_get_price_html', array( $this, 'renderTooltip' ), 999, 2 );
54
55 add_filter( 'woocommerce_get_price_suffix', function ( $suffix, \WC_product $product, $price, $qty ) {
56
57 // Allow 3rd-party to control this
58 if ( ! apply_filters( 'tiered_pricing_table/frontend/modify_price_suffix', true, $suffix, $product, $price,
59 $qty ) ) {
60 return $suffix;
61 }
62
63 if ( empty( $suffix ) ) {
64 return $suffix;
65 }
66
67 $html = '';
68
69 $suffix = get_option( 'woocommerce_price_display_suffix' );
70
71 if ( $suffix && wc_tax_enabled() && 'taxable' === $product->get_tax_status() ) {
72
73 if ( '' === $price ) {
74 $price = $product->get_price();
75 }
76
77 $replacements = array(
78 '{price_including_tax}' => '<span class="tiered-pricing-dynamic-price__including_tax">' . wc_price( wc_get_price_including_tax( $product,
79 array(
80 'qty' => $qty,
81 'price' => $price,
82 ) ) ) . '</span>',
83 '{price_excluding_tax}' => '<span class="tiered-pricing-dynamic-price__excluding_tax">' . wc_price( wc_get_price_excluding_tax( $product,
84 array(
85 'qty' => $qty,
86 'price' => $price,
87 ) ) ) . '</span>',
88 );
89
90 $html = str_replace( array_keys( $replacements ), array_values( $replacements ),
91 ' <small class="woocommerce-price-suffix">' . wp_kses_post( $suffix ) . '</small>' );
92 }
93
94 return $html;
95
96 }, 10, 4 );
97 }
98
99 /**
100 * Wrap product price for managing it by JS
101 *
102 * @param mixed $priceHTML
103 * @param WC_Product $product
104 *
105 * @return string
106 */
107 public function wrapPrice( $priceHTML, WC_Product $product ) {
108
109 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
110 return $priceHTML;
111 }
112
113 // Do not wrap price in quick edit and inline edit in admin
114 if ( isset( $_POST['action'] ) && in_array( $_POST['action'], [
115 'inline-save',
116 ], true ) ) {
117 return $priceHTML;
118 }
119
120 // Allow 3rd-party to control this
121 if ( ! apply_filters( 'tiered_pricing_table/frontend/wrap_price', true, $product, $priceHTML ) ) {
122 return $priceHTML;
123 }
124
125 $parentProductID = $product->get_parent_id() ? $product->get_parent_id() : $product->get_id();
126 $variationID = TierPricingTablePlugin::isVariationProductSupported( $product ) ? $product->get_id() : null;
127 $productPagePrice = get_queried_object_id() === $parentProductID && is_product();
128 $priceDisplayContext = $productPagePrice ? 'product-page' : 'shop-loop';
129
130 $priceType = apply_filters( 'tiered_pricing_table/frontend/default_price_behaviour_type', 'dynamic', $product,
131 $priceHTML, $priceDisplayContext );
132
133 if ( $productPagePrice && $product->get_type() !== 'variation' ) {
134 // Do not format prices if tiered pricing formatting enabled on the product page.
135 if ( 'same_as_catalog' === ProductPagePriceSubsection::getFormatPriceType() ) {
136 $priceType = 'static';
137 }
138 }
139
140 $isVariable = in_array( $product->get_type(), TierPricingTablePlugin::getSupportedVariableProductTypes() );
141
142 $pricingRules = \TierPricingTable\PriceManager::getPricingRule( $product->get_id() );
143
144 // Set to no-rules if there are no pricing rules and the main dynamic price setting is disabled
145 $updatePrice = $this->getContainer()->getSettings()->get( 'update_price_on_product_page', 'yes' ) === 'yes';
146 if ( ! $isVariable && empty( $pricingRules->getRules() ) && ! $updatePrice ) {
147 $priceType = 'no-rules';
148 }
149
150 $supportedTypes = array_merge( TierPricingTablePlugin::getSupportedSimpleProductTypes(), array( 'variation' ) );
151
152 $wrapVariableProductPrice = apply_filters( 'tiered_pricing_table/frontend/wrap_variable_price', true,
153 $product );
154
155 $hasRules = PricingTable::getInstance()->productHasPricingRules( $product );
156 $showTotalPrice = $hasRules
157 ? $this->getContainer()->getSettings()->get( 'show_total_price', 'no' ) === 'yes'
158 : $this->getContainer()->getSettings()->get( 'show_total_price_non_tiered', 'no' ) === 'yes';
159
160 // Is "show total price" is enabled, we can wrap the variable product price, or it's forced by the hook
161 if ( $wrapVariableProductPrice || $showTotalPrice ) {
162 $supportedTypes = array_merge( $supportedTypes,
163 TierPricingTablePlugin::getSupportedVariableProductTypes() );
164 }
165
166 if ( in_array( $product->get_type(), $supportedTypes ) ) {
167
168 ob_start();
169
170 ?>
171 <span class="tiered-pricing-dynamic-price-wrapper<?php echo $isVariable ? ' tiered-pricing-dynamic-price-wrapper--variable' : ''; ?>"
172 data-display-context="<?php echo esc_attr( $priceDisplayContext ); ?>"
173 data-price-type="<?php echo esc_attr( $priceType ); ?>"
174 data-product-id="<?php echo esc_attr( $product->get_id() ); ?>"
175 data-parent-id="<?php echo esc_attr( $product->get_parent_id() ? $product->get_parent_id() : $product->get_id() ); ?>">
176 <?php
177 return ob_get_clean() . $priceHTML . '</span>';
178 }
179
180 return $priceHTML;
181 }
182
183 /**
184 * Render a pricing table on the product page
185 */
186 public function renderPricingTableOnProductPage() {
187
188 global $post;
189
190 if ( ! $post || ! is_product() ) {
191 return;
192 }
193
194 $variationId = $this->getVariationIdFromURL( $post->ID );
195
196 PricingTable::getInstance()->renderPricingTable( $post->ID, $variationId );
197 }
198
199 public function getVariationIdFromURL( $productId ): ?int {
200
201 $attributes = array();
202 $attributeWordLen = strlen( 'attribute_' );
203
204 foreach ( $_REQUEST as $key => $value ) {
205
206 if ( strlen( $key ) < $attributeWordLen ) {
207 continue;
208 }
209
210 $string = substr( $key, 0, $attributeWordLen );
211
212 if ( strcasecmp( $string, 'attribute_' ) === 0 ) {
213 $attributes[ $key ] = $value;
214 }
215 }
216
217 if ( empty( $attributes ) ) {
218 return null;
219 }
220
221 $product = wc_get_product( $productId );
222
223 if ( ! $product ) {
224 return null;
225 }
226
227 if ( ! TierPricingTablePlugin::isVariableProductSupported( $product ) ) {
228 return null;
229 }
230
231 return ( new WC_Product_Data_Store_CPT() )->find_matching_product_variation( $product, $attributes );
232 }
233
234 /**
235 * Render tooltip near product price if selected display type is "tooltip"
236 *
237 * @param ?string $price
238 * @param WC_Product $_product
239 *
240 * @return string
241 */
242 public function renderTooltip( ?string $price, WC_Product $_product ): ?string {
243
244 // Do not render if not display
245 if ( 'yes' !== $this->getContainer()->getSettings()->get( 'display', 'yes' ) ) {
246 return $price;
247 }
248
249 $displayType = TieredPricingTab::getProductTemplate( $_product->get_parent_id() ? $_product->get_parent_id() : $_product->get_id() );
250 $displayType = 'default' === $displayType ? $this->getContainer()->getSettings()->get( 'display_type',
251 'table' ) : $displayType;
252
253 // Do not display if display type is not the tooltip
254 if ( 'tooltip' !== $displayType ) {
255 return $price;
256 }
257
258 if ( is_product() ) {
259 $addTooltip = false;
260 $page_product_id = get_queried_object_id();
261
262 if ( $_product->is_type( 'variation' ) && $_product->get_parent_id() === $page_product_id ) {
263 $addTooltip = true;
264 } elseif ( $_product->get_id() === $page_product_id && TierPricingTablePlugin::isSimpleProductSupported( $_product ) ) {
265 $addTooltip = true;
266 }
267
268 if ( ! $addTooltip ) {
269 return $price;
270 }
271
272 $pricingRule = PriceManager::getPricingRule( $_product->get_id() );
273
274 if ( ! empty( $pricingRule->getRules() ) ) {
275
276 return $price . $this->getContainer()->getFileManager()->renderTemplate( 'frontend/tooltip.php', array(
277 'color' => $this->getContainer()->getSettings()->get( 'tooltip_color', '#3858e9' ),
278 'size' => $this->getContainer()->getSettings()->get( 'tooltip_size', 15 ) . 'px',
279 ) );
280 }
281
282 }
283
284 return $price;
285 }
286
287 /**
288 * Fired when user chooses a variation. Renders tiered pricing table for the variation
289 *
290 * @throws Exception
291 * @global WP_Post $post .
292 */
293 public function getVariationPricingTable() {
294
295 $product_id = isset( $_POST['variation_id'] ) ? sanitize_text_field( $_POST['variation_id'] ) : false;
296 $nonce = isset( $_POST['nonce'] ) ? sanitize_key( $_POST['nonce'] ) : false;
297 $displayContext = isset( $_POST['display_context'] ) ? sanitize_text_field( $_POST['display_context'] ) : 'product-page';
298
299 $renderSettings = apply_filters( 'tiered_pricing_table/frontend/variation_render_settings', array(),
300 $product_id, $displayContext );
301
302 // Some cache plugins may cache the nonce, so we need to leave the ability to disable nonce verification.
303 // Checking nonce is not critical here.
304 $verifyNonce = apply_filters( 'tiered_pricing_table/frontend/load_variation/verify_nonce', false, $nonce,
305 'get_pricing_table' );
306
307 if ( $verifyNonce && ! wp_verify_nonce( $nonce, 'get_pricing_table' ) ) {
308 return;
309 }
310
311 $product = wc_get_product( $product_id );
312
313 if ( $product ) {
314 $parentProduct = wc_get_product( $product->get_parent_id() );
315
316 if ( $parentProduct ) {
317 PricingTable::getInstance()->renderPricingTableHTML( $parentProduct, $product, $renderSettings );
318 }
319
320 }
321
322 }
323 }
324