PluginProbe
Tiered Pricing Table for WooCommerce / 8.0.2
Tiered Pricing Table for WooCommerce v8.0.2
8.0.2 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 All 91 releases
← All changes | src/PricingTable.php +185 -6 7.1.1 → 8.0.2 View file →
@@ -178,8 +178,19 @@
178 178 $templateType = 'options';
179 179 }
180 180 }
181 181
182 + // layouts with a single style option: dropdown, plain text
183 + $styledLayouts = array(
184 + 'dropdown' => 'dropdown_style',
185 + 'plain-text' => 'plain_text_style',
186 + );
187 +
188 + if ( isset( $styledLayouts[ $displayType ] ) ) {
189 + $style = $settings[ $styledLayouts[ $displayType ] ] ?? 'default';
190 + $templateType = 'default' !== $style && '' !== $style ? $displayType . '-' . $style : $displayType;
191 + }
192 +
182 193 $template = "tiered-pricing-$templateType.php";
183 194
184 195 do_action( 'tiered_pricing_table/before_rendering_tiered_pricing/inner', $priceRule, $product, $settings );
185 196
@@ -196,8 +207,10 @@
196 207 'settings' => $settings,
197 208 'pricing_type' => $priceRule->getType(),
198 209 'id' => $this->getUniqueTieredPricingId(),
199 210 ) );
211 +
212 + $this->renderMoreTiersLink( $parentProduct );
200 213 } else {
201 214 // Provide fallback data for products without pricing rules,
202 215 // which allows the frontend JS (e.g. dynamic totals, you save, etc.)
203 216 // to function for all products via the 'tiered_price_update' event.
@@ -207,20 +220,37 @@
207 220
208 221 $price_excl_taxes = wc_get_price_excluding_tax( $product, array(
209 222 'price' => $product->get_price(),
210 223 ) );
224 +
225 + $sale_price = $product->get_sale_price();
226 +
227 + if ( $sale_price ) {
228 + $sale_price = wc_get_price_to_display( $product, array(
229 + 'price' => $sale_price,
230 + ) );
231 + }
232 +
233 + $regular_price = wc_get_price_to_display( $product, array(
234 + 'price' => $product->get_regular_price(),
235 + ) );
236 +
237 + $price = wc_get_price_to_display( $product, array(
238 + 'price' => $product->get_price(),
239 + ) );
240 +
211 241 ?>
212 242 <div style="display:none;"
213 243 class="tiered-pricing-fallback-data"
214 244 data-product-id="<?php echo esc_attr( $product->get_id() ); ?>"
215 245 data-price-rules="{}"
216 - data-minimum="1"
246 + data-minimum="<?php echo esc_attr( $priceRule->getMinimum( true ) ); ?>"
217 247 data-product-name="<?php echo esc_attr( $product->get_name() ); ?>"
218 - data-regular-price="<?php echo esc_attr( $product->get_regular_price() ); ?>"
219 - data-sale-price="<?php echo esc_attr( $product->get_sale_price() ); ?>"
220 - data-price="<?php echo esc_attr( $product->get_price() ); ?>"
248 + data-regular-price="<?php echo esc_attr( $regular_price ); ?>"
249 + data-sale-price="<?php echo esc_attr( $sale_price ); ?>"
250 + data-price="<?php echo esc_attr( $price ); ?>"
221 251 data-product-price-suffix="<?php echo esc_attr( $product->get_price_suffix() ); ?>"
222 - data-tiered-price="<?php echo esc_attr( $product->get_price() ); ?>"
252 + data-tiered-price="<?php echo esc_attr( $price ); ?>"
223 253 data-tiered-price-exclude-taxes="<?php echo esc_attr( $price_excl_taxes ); ?>"
224 254 data-tiered-price-include-taxes="<?php echo esc_attr( $price_incl_taxes ); ?>"
225 255 ></div>
226 256 <?php
@@ -226,8 +256,148 @@
226 256 <?php
227 257 }
228 258 }
229 259
260 + /**
261 + * Discount of a tier as the store shows it: a percentage, the amount saved per item, or both.
262 + *
263 + * @param float $percent Discount in percent, as the templates compute it.
264 + * @param PricingRule $pricingRule
265 + * @param WC_Product $product
266 + * @param int|null $quantity Tier quantity, or null for the base tier (its discount is the sale price).
267 + * @param array $settings Renderer settings.
268 + * @param string $style 'plain' ("10%", "$4.50", "10% ($4.50)") or 'off' ("10% off", "$4.50 off", "10% off, save $4.50").
269 + *
270 + * @return string HTML
271 + */
272 + public static function formatDiscount(
273 + float $percent,
274 + PricingRule $pricingRule,
275 + WC_Product $product,
276 + ?int $quantity,
277 + array $settings,
278 + string $style = 'plain'
279 + ): string {
280 + $format = $settings['discount_format'] ?? 'percentage';
281 + $format = in_array( $format, array( 'percentage', 'amount', 'both' ), true ) ? $format : 'percentage';
282 +
283 + $percentLabel = round( $percent, 2 ) . '%';
284 + $amountLabel = '';
285 +
286 + if ( 'percentage' !== $format ) {
287 + $saving = 0;
288 +
289 + if ( null === $quantity ) {
290 + if ( $product->is_on_sale() ) {
291 + $saving = (float) $product->get_regular_price() - (float) $product->get_sale_price();
292 + }
293 + } else {
294 + $base = CalculationLogic::calculateDiscountBasedOnRegularPrice() ? $product->get_regular_price() : $product->get_price();
295 + $saving = (float) $base - (float) $pricingRule->getTierPrice( $quantity, false );
296 + }
297 +
298 + $amountLabel = wc_price( wc_get_price_to_display( $product, array( 'price' => max( 0, $saving ) ) ) );
299 + }
300 +
301 + if ( 'off' === $style ) {
302 + switch ( $format ) {
303 + case 'amount':
304 + // translators: %s: amount saved per item
305 + return sprintf( __( '%s off', 'tier-pricing-table' ), $amountLabel );
306 + case 'both':
307 + // translators: 1: discount percentage, 2: amount saved per item
308 + return sprintf( __( '%1$s off, save %2$s', 'tier-pricing-table' ), $percentLabel, $amountLabel );
309 + default:
310 + // translators: %s: discount percentage
311 + return sprintf( __( '%s off', 'tier-pricing-table' ), $percentLabel );
312 + }
313 + }
314 +
315 + switch ( $format ) {
316 + case 'amount':
317 + return $amountLabel;
318 + case 'both':
319 + return $percentLabel . ' (' . $amountLabel . ')';
320 + default:
321 + return $percentLabel;
322 + }
323 + }
324 +
325 + /**
326 + * The "Spacing" and "Cell padding" options as a style attribute for a layout's root element: CSS
327 + * variables the stylesheet reads for the gap between tiers and the padding inside table cells.
328 + * Empty when neither option is set (each style keeps its own spacing and padding).
329 + */
330 + public static function layoutStyleAttribute( array $settings ): string {
331 + $variables = array();
332 +
333 + foreach ( array( 'layout_spacing' => '--tiered-pricing-gap', 'cell_padding' => '--tiered-pricing-cell-padding' ) as $option => $variable ) {
334 + $value = $settings[ $option ] ?? '';
335 +
336 + if ( '' === $value || null === $value || ! is_numeric( $value ) ) {
337 + continue;
338 + }
339 +
340 + $variables[] = $variable . ': ' . max( 0, min( 80, (int) $value ) ) . 'px';
341 + }
342 +
343 + return $variables ? ' style="' . esc_attr( implode( '; ', $variables ) ) . '"' : '';
344 + }
345 +
346 + /**
347 + * @deprecated 7.1.9 Use layoutStyleAttribute().
348 + */
349 + public static function spacingAttribute( array $settings ): string {
350 + return self::layoutStyleAttribute( $settings );
351 + }
352 +
353 + /**
354 + * Tiers the last rendered layout left out because of the "tiers per grid item" limit.
355 + */
356 + protected static $hiddenTiers = 0;
357 +
358 + /**
359 + * Tier rows in the configured order (ascending by quantity, or the biggest discount first), cut to
360 + * the "tiers per grid item" limit when one is set.
361 + *
362 + * @param string[] $rows Rendered rows, ascending.
363 + * @param array $settings Renderer settings.
364 + *
365 + * @return string HTML (the rows were escaped when rendered)
366 + */
367 + public static function orderTiers( array $rows, array $settings ): string {
368 + if ( 'desc' === ( $settings['tiers_order'] ?? 'asc' ) ) {
369 + $rows = array_reverse( $rows );
370 + }
371 +
372 + $limit = (int) ( $settings['tiers_limit'] ?? 0 );
373 + self::$hiddenTiers = 0;
374 +
375 + if ( $limit > 0 && count( $rows ) > $limit ) {
376 + self::$hiddenTiers = count( $rows ) - $limit;
377 + $rows = array_slice( $rows, 0, $limit );
378 + }
379 +
380 + return implode( '', $rows );
381 + }
382 +
383 + /**
384 + * The "+N more tiers" link to the product page, after a layout the limit shortened.
385 + */
386 + protected function renderMoreTiersLink( WC_Product $parentProduct ) {
387 + $hidden = self::$hiddenTiers;
388 + self::$hiddenTiers = 0;
389 +
390 + if ( $hidden < 1 ) {
391 + return;
392 + }
393 +
394 + /* translators: %d: number of tiers not listed */
395 + $label = sprintf( _n( '+%d more tier', '+%d more tiers', $hidden, 'tier-pricing-table' ), $hidden );
396 +
397 + echo '<a class="tiered-pricing-more-tiers" href="' . esc_url( $parentProduct->get_permalink() ) . '">' . esc_html( $label ) . '</a>';
398 + }
399 +
230 400 protected function getDefaultSettings( $productId = false ): array {
231 401 $hasRules = true;
232 402
233 403 if ( $productId ) {
@@ -242,9 +412,9 @@
242 412 'no' ) === 'yes';
243 413
244 414 $settings = array(
245 415 'display_context' => 'product-page',
246 - 'display' => $this->getContainer()->getSettings()->get( 'display', 'yes' ) === 'yes',
416 + 'display' => GeneralSection::isAutomaticDisplayEnabled(),
247 417 'display_type' => $this->getContainer()->getSettings()->get( 'display_type', 'table' ),
248 418 'title' => $this->getContainer()->getSettings()->get( 'table_title', '' ),
249 419 'table_class' => $this->getContainer()->getSettings()->get( 'table_css_class', '' ),
250 420 'quantity_column_title' => $this->getContainer()->getSettings()->get( 'head_quantity_text',
@@ -259,8 +429,14 @@
259 429 'clickable_rows' => $this->getContainer()->getSettings()->get( 'clickable_table_rows',
260 430 'yes' ) === 'yes',
261 431 'active_tier_color' => $this->getContainer()->getSettings()->get( 'selected_quantity_color',
262 432 '#3858e9' ),
433 + 'discount_format' => $this->getContainer()->getSettings()->get( 'discount_format', 'percentage' ),
434 + 'layout_spacing' => $this->getContainer()->getSettings()->get( 'layout_spacing', '' ),
435 + 'cell_padding' => $this->getContainer()->getSettings()->get( 'cell_padding', '' ),
436 + 'discount_badge_color' => $this->getContainer()->getSettings()->get( 'discount_badge_color', '' ),
437 + 'active_tier_border' => $this->getContainer()->getSettings()->get( 'table_active_border', 'yes' ),
438 + 'tiers_order' => $this->getContainer()->getSettings()->get( 'tiers_order', 'asc' ),
263 439 'tooltip_border' => $this->getContainer()->getSettings()->get( 'tooltip_border',
264 440 'yes' ) === 'yes',
265 441
266 442 'table_style' => GeneralSection::getPricingTableStyle(),
@@ -266,8 +442,11 @@
266 442 'table_style' => GeneralSection::getPricingTableStyle(),
267 443 'compact_layout' => $this->getContainer()->getSettings()->get( 'compact_layout', 'no' ),
268 444 'blocks_style' => GeneralSection::getPricingBlocksStyle(),
269 445 'options_style' => GeneralSection::getPricingOptionsStyle(),
446 +
447 + 'dropdown_style' => GeneralSection::getPricingDropdownStyle(),
448 + 'plain_text_style' => GeneralSection::getPricingPlainTextStyle(),
270 449
271 450 'options_show_total' => GeneralSection::isShowOptionTotal(),
272 451 'options_show_original_product_price' => GeneralSection::isShowOriginalProductPrice(),
273 452 'options_show_default_option' => GeneralSection::isDefaultOptionEnabled(),