PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.28
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.28
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / app / Services / Renderer / PricingTableRenderer.php

PricingTableRenderer.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.28, at app/Services/Renderer/PricingTableRenderer.php

433 lines 16.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Renderer;
4
5 use FluentCart\Api\CurrencySettings;
6 use FluentCart\Api\StoreSettings;
7 use FluentCart\App\Helpers\Helper;
8 use FluentCart\App\Models\Product;
9 use FluentCart\App\Models\ProductVariation;
10 use FluentCart\App\Vite;
11 use FluentCart\Framework\Support\Arr;
12 use FluentCart\App\App;
13 use FluentCart\Framework\Support\Str;
14 use FluentCart\App\Helpers\CurrenciesHelper;
15 use FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\AddToCartShortcode;
16 use FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\DirectCheckoutShortcode;
17
18 class PricingTableRenderer
19 {
20 protected $viewData;
21
22 protected $variants;
23
24 protected $storeSettings;
25
26 protected $sign;
27
28 protected $repeatInterval = null;
29
30 protected $itemPrice = null;
31
32 protected $comparePrice = null;
33
34 protected $paymentInfo = '';
35
36 protected $setupFeeInfo = '';
37
38 protected $featureItems = [];
39
40 protected $showCartButton = 1;
41
42 protected $paymentType = '';
43
44 protected $licenseEnable = 'no';
45
46
47 public function __construct($viewData)
48 {
49 $this->viewData = $viewData;
50 $this->variants = $this->viewData['variants'];
51 $this->storeSettings = new StoreSettings();
52
53 $signName = $this->storeSettings->get('currency');
54
55 $this->sign = CurrenciesHelper::getCurrencySign($signName);
56
57 $this->showCartButton = Arr::get($this->viewData, 'show_cart_button', 1);
58
59
60 }
61
62 public function render()
63 {
64 $groupBy = Arr::get($this->viewData, 'group_by', 'repeat_interval');
65 $groups = $this->groupVariants($groupBy);
66 $useTabs = $groupBy !== 'none' && count($groups) > 1;
67
68 ?>
69 <div class="fluent-cart-pricing-table" data-fluent-cart-pricing-table role="region"
70 aria-label="<?php esc_attr_e('Pricing table', 'fluent-cart'); ?>">
71 <?php if ($useTabs) : ?>
72 <?php $this->renderTabs($groups); ?>
73 <?php else : ?>
74 <div class="fluent-cart-pricing-table-variants-iterator" role="list">
75 <?php $this->renderVariant($this->variants); ?>
76 </div>
77 <?php endif; ?>
78 </div>
79 <?php
80 }
81
82 protected function groupVariants(string $groupBy): array
83 {
84 if ($groupBy === 'none') {
85 return ['onetime' => $this->variants];
86 }
87
88 $key = $groupBy === 'payment_type' ? 'payment_type' : 'repeat_interval';
89
90 $groups = [];
91 foreach ($this->variants as $variant) {
92 $bucket = Arr::get($variant, "other_info.{$key}") ?: 'onetime';
93 if (Arr::get($variant, 'other_info.installment') === 'yes') {
94 $bucket = 'installment';
95 }
96 $groups[$bucket][] = $variant;
97 }
98
99 return $groups;
100 }
101
102 protected function renderTabs(array $groups)
103 {
104 $groupKeys = array_keys($groups);
105 $activeIndex = (int) Arr::get($this->viewData, 'active_tab', 0);
106 if (!isset($groupKeys[$activeIndex])) {
107 $activeIndex = 0;
108 }
109 $activeKey = $groupKeys[$activeIndex];
110 $activeVariantMap = Arr::get($this->viewData, 'active_variant', []);
111 if (!\is_array($activeVariantMap)) {
112 $activeVariantMap = [];
113 }
114
115 ?>
116 <div class="fluent-cart-pricing-table-tab" data-fluent-cart-pricing-table-tab>
117 <div class="fluent-cart-pricing-table-tab-nav">
118 <div class="fluent-cart-pricing-table-tab-nav-inner-wrap">
119 <div class="fluent-cart-pricing-table-tab-nav-inner" role="tablist">
120 <div class="tab-active-bar" data-tab-active-bar></div>
121 <?php foreach ($groupKeys as $key) :
122 $isActive = $key === $activeKey;
123 ?>
124 <div class="fluent-cart-pricing-table-tab-nav-item<?php echo $isActive ? ' active' : ''; ?>"
125 data-tab="<?php echo esc_attr($key); ?>">
126 <?php echo esc_html($this->getTabLabel($key)); ?>
127 </div>
128 <?php endforeach; ?>
129 </div>
130 </div>
131 </div>
132 <div class="fluent-cart-pricing-table-tab-content" data-tab-contents>
133 <?php foreach ($groups as $key => $groupVariants) :
134 $isActive = $key === $activeKey;
135 $currentActiveVariant = Arr::get($activeVariantMap, $key, '');
136 ?>
137 <div id="<?php echo esc_attr($key); ?>"
138 class="fluent-cart-pricing-table-tab-pane<?php echo $isActive ? ' active' : ''; ?>"
139 data-tab-content
140 data-active_variant="<?php echo esc_attr($currentActiveVariant); ?>"
141 role="tabpanel">
142 <div class="fluent-cart-pricing-table-variants-iterator" role="list">
143 <?php $this->renderVariant($groupVariants); ?>
144 </div>
145 </div>
146 <?php endforeach; ?>
147 </div>
148 </div>
149 <?php
150 }
151
152 protected function getTabLabel(string $key): string
153 {
154 $labels = [
155 'daily' => __('Daily', 'fluent-cart'),
156 'weekly' => __('Weekly', 'fluent-cart'),
157 'monthly' => __('Monthly', 'fluent-cart'),
158 'yearly' => __('Yearly', 'fluent-cart'),
159 'onetime' => __('One Time', 'fluent-cart'),
160 'subscription' => __('Subscription', 'fluent-cart'),
161 'installment' => __('Installment', 'fluent-cart'),
162 ];
163
164 return $labels[$key] ?? ucfirst(str_replace('_', ' ', $key));
165 }
166
167 public function renderVariant(array $variants = null)
168 {
169 $variants = $variants ?? $this->variants;
170
171 foreach ($variants as $index => $variant) {
172 $this->repeatInterval = Arr::get($variant, 'other_info.repeat_interval', null);
173
174 $this->itemPrice = esc_html(Helper::toDecimal(Arr::get($variant, 'item_price', 0), false, false, false));
175
176 $this->comparePrice = esc_html(Helper::toDecimal(Arr::get($variant, 'compare_price', 0), false, false, false, false));
177
178 $otherInfo = Arr::get($variant, 'other_info', null);
179
180 $this->paymentInfo = Helper::generateSubscriptionInfo($otherInfo, $this->itemPrice);
181
182 $this->setupFeeInfo = Helper::generateSetupFeeInfo($otherInfo);
183
184 $featureDescription = Arr::get($variant, 'other_info.description', '');
185
186 $this->featureItems = explode("\n", $featureDescription);
187
188 $this->paymentType = Arr::get($variant, 'payment_type');
189
190 $licenseMeta = Arr::get($variant, 'product.licenses_meta.meta_value', null);
191
192 $this->licenseEnable = Arr::get($licenseMeta, 'enabled', 'no');
193
194 ?>
195
196 <div class="fluent-cart-pricing-table-variant" data-fluent-cart-pricing-table-variant role="listitem">
197
198 <div class="fluent-cart-pricing-table-variant-contents">
199 <div class="fluent-cart-pricing-table-variant-title">
200 <?php echo esc_html(Arr::get($variant, 'product.post_title', '')); ?>
201 </div>
202
203 <div class="fluent-cart-pricing-table-variant-sub-title">
204 <?php echo esc_html(Arr::get($variant, 'variation_title', '')); ?>
205 </div>
206
207 <div class="variant-divider" aria-hidden="true"></div>
208
209 <div class="fluent-cart-pricing-table-variant-price-wrap">
210 <div
211 class="fluent-cart-pricing-table-variant-price"
212 aria-label="<?php
213 printf(
214 /* translators: 1: Currency sign, 2: Item price, 3: Billing interval or unit */
215 esc_attr__('Price: %1$s%2$s per %3$s', 'fluent-cart'),
216 esc_attr($this->sign),
217 esc_attr($this->itemPrice),
218 esc_attr($this->repeatInterval ?? __('unit', 'fluent-cart'))
219 );
220 ?>">
221 <sup><?php echo esc_html($this->sign) ?></sup>
222
223 <?php echo esc_html($this->itemPrice) ?>
224
225 <?php if (isset($this->repeatInterval)): ?>
226 <span class="repeat-interval">
227 /<?php echo esc_html($this->repeatInterval) ?>
228 </span>
229 <?php endif; ?>
230
231 </div>
232
233 <div class="fluent-cart-pricing-table-variant-compare-price">
234 <span><?php echo esc_html($this->sign) ?></span>
235 <?php
236 $aria_label = sprintf(
237 /* translators: 1: Currency sign, 2: Compare price */
238 esc_attr__('Compare at: %1$s%2$s', 'fluent-cart'),
239 $this->sign,
240 $this->comparePrice
241 );
242 ?>
243 <span aria-label="<?php echo esc_attr($aria_label); ?>">
244 <del><?php echo esc_html($this->comparePrice); ?></del>
245 </span>
246 </div>
247 </div>
248
249 <div class="fluent-cart-pricing-table-variant-payment-type">
250 <span> <?php echo esc_html($this->paymentInfo); ?> </span>
251 <span class="setup-fee"
252 aria-label="<?php esc_attr_e('Setup fee information', 'fluent-cart'); ?>">
253 <?php echo esc_html($this->setupFeeInfo); ?>
254 </span>
255 </div>
256
257 <div class="fluent-cart-pricing-table-variant-description">
258 <?php $this->renderFeatureItems(); ?>
259 </div>
260
261 </div>
262
263 <div class="fluent-cart-pricing-table-variant-buttons">
264 <?php
265 if (
266 $this->showCartButton == 1 &&
267 $this->paymentType !== 'subscription' &&
268 $this->licenseEnable !== 'yes'
269 ) {
270 $this->renderCartButton($variant);
271 }
272
273 if (Arr::get($this->viewData, 'show_checkout_button', 1) == 1) {
274 $this->renderDirectCheckoutButton($variant);
275 }
276
277
278 ?>
279 </div>
280
281
282 </div>
283
284 <?php };
285 }
286
287 public function renderFeatureItems()
288 {
289
290 ?>
291 <ul class="fluent-cart-pricing-table-variant-features" role="list">
292 <?php
293 foreach ($this->featureItems as $index => $featureItem) {
294 if (!empty($featureItem)) : ?>
295
296 <li class="fluent-cart-pricing-table-variant-feature" role="listitem">
297 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none"
298 aria-hidden="true">
299 <path d="M17.3332 9.00004C17.3332 4.39767 13.6022 0.666706 8.99984 0.666706C4.39746 0.666706 0.666504 4.39767 0.666504 9.00004C0.666504 13.6024 4.39746 17.3334 8.99984 17.3334C13.6022 17.3334 17.3332 13.6024 17.3332 9.00004Z"
300 fill="currentColor"></path>
301 <path d="M5.6665 9.62502C5.6665 9.62502 6.99984 10.3855 7.6665 11.5C7.6665 11.5 9.6665 7.12502 12.3332 5.66669"
302 stroke="white" stroke-width="1.5" stroke-linecap="round"
303 stroke-linejoin="round"></path>
304 </svg>
305
306 <?php echo esc_html($featureItem); ?>
307 </li>
308
309 <?php endif;
310 }
311 ?>
312 </ul>
313
314 <?php
315
316 }
317
318 public function renderCartButton($variant)
319 {
320 $buttonOptions = Arr::get($this->viewData, 'button_options', '');
321 $buttonValues = self::stringToAssocArray($buttonOptions);
322 $buttonText = !empty($buttonValues['cartButtonText']) ? $buttonValues['cartButtonText'] : __('Add To Cart', 'fluent-cart');
323
324 if ($variant['stock_status'] === Helper::IN_STOCK) {
325 $data = [
326 'add_to_cart_button_text' => $buttonText,
327 'product_id' => Arr::get($variant, 'id', ''),
328 'quantity' => 1,
329 'variation_type' => Arr::get($variant, 'variation_type', ''),
330 'payment_type' => Arr::get($variant, 'payment_type', ''),
331 ];
332
333 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in view template
334 echo AddToCartShortcode::make()->enqueueAssets()->render($data);
335 } else {
336 $this->renderOutOfStockButton();
337 }
338
339 }
340
341 public function renderOutOfStockButton()
342 {
343 ?>
344 <button
345 class="fct-out-of-stock-button"
346 disabled
347 aria-disabled="true"
348 aria-label="<?php
349 /* translators: Button aria-label shown when a product is out of stock */
350 esc_attr_e('This item is out of stock and cannot be added to cart', 'fluent-cart');
351 ?>"
352 >
353 <?php
354 echo esc_html(
355 $this->storeSettings->get(
356 'out_of_stock_button_text',
357 __('Not Available', 'fluent-cart')
358 )
359 );
360 ?>
361 </button>
362 <?php
363 }
364
365
366 public function renderDirectCheckoutButton($variant)
367 {
368 $directCheckoutButtonText = $this->storeSettings->get('direct_checkout_button_text', __('Buy Now', 'fluent-cart'));
369
370 $buttonOptions = Arr::get($this->viewData, 'button_options', '');
371 $buttonValues = self::stringToAssocArray($buttonOptions);
372 $buttonText = !empty($buttonValues['text']) ? $buttonValues['text'] : $directCheckoutButtonText;
373
374 $checkoutUrl = add_query_arg([
375 'fluent-cart' => 'instant_checkout'
376 ], site_url());
377
378 if ($variant['stock_status'] === Helper::IN_STOCK) {
379
380 // Handle product parameters
381 $productId = Arr::get($variant, 'id');
382 $quantity = Arr::get($this->viewData, 'quantity', 1);
383
384 // Build base parameters
385 $params = [
386 'item_id' => $productId,
387 'quantity' => $quantity,
388 ];
389
390 // Merge in any additional params
391 $extraParams = Arr::get($this->viewData, 'url_params', '');
392 if (!empty($extraParams)) {
393 // If it's a query string like "foo=bar&baz=qux"
394 parse_str($extraParams, $extraArray);
395 $params = array_merge($params, $extraArray);
396 }
397
398 $checkoutUrl = add_query_arg($params, $checkoutUrl);
399
400 $data = [
401 'direct_checkout_button_text' => $buttonText,
402 'checkout_url' => $checkoutUrl,
403 'product_id' => Arr::get($variant, 'id'),
404 'is_pricing_table' => true,
405 'quantity' => 1,
406 'variation_type' => Arr::get($variant, 'variation_type', ''),
407 'stock_availability' => Arr::get($variant, 'stock_status', ''),
408 ];
409 }
410 }
411
412 private static function stringToAssocArray($string): array
413 {
414 $result = [];
415
416 if (!empty($string)) {
417 $pairs = explode(', ', $string);
418
419 foreach ($pairs as $pair) {
420 // Check if $pair contains '=' using Str::contains() before splitting
421 if (Str::contains($pair, '=')) {
422 list($key, $value) = explode('=', $pair);
423 $result[trim($key)] = trim($value);
424 }
425 }
426 }
427
428 return $result;
429 }
430
431
432 }
433