PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.86
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.86
51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 All 40 releases
king-addons / includes / widgets / Woo_Product_Variations / Woo_Product_Variations.php

Woo_Product_Variations.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.86, at includes/widgets/Woo_Product_Variations/Woo_Product_Variations.php

342 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Woo Product Variations / Swatches widget.
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons;
9
10 use Elementor\Controls_Manager;
11 use King_Addons\Woo_Builder\Context as Woo_Context;
12 use WC_Product_Variable;
13
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 /**
19 * Renders variation attributes selector.
20 */
21 class Woo_Product_Variations extends Abstract_Single_Widget
22 {
23 /**
24 * Widget slug.
25 *
26 * @return string
27 */
28 public function get_name(): string
29 {
30 return 'woo_product_variations';
31 }
32
33 /**
34 * Widget title.
35 *
36 * @return string
37 */
38 public function get_title(): string
39 {
40 return esc_html__('Product Variations', 'king-addons');
41 }
42
43 /**
44 * Widget icon.
45 *
46 * @return string
47 */
48 public function get_icon(): string
49 {
50 return 'king-addons-icon king-addons-woo-product-variations';
51 }
52
53 /**
54 * Widget categories.
55 *
56 * @return array<int,string>
57 */
58 public function get_categories(): array
59 {
60 return ['king-addons-woo-builder'];
61 }
62
63 /**
64 * Style dependencies.
65 *
66 * @return array<int,string>
67 */
68 public function get_style_depends(): array
69 {
70 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-product-variations-style'];
71 }
72
73 /**
74 * Script dependencies.
75 *
76 * @return array<int,string>
77 */
78 public function get_script_depends(): array
79 {
80 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-product-variations-script'];
81 }
82
83 protected function register_controls(): void
84 {
85 $this->start_controls_section(
86 'section_content',
87 [
88 'label' => esc_html__('Content', 'king-addons'),
89 'tab' => Controls_Manager::TAB_CONTENT,
90 ]
91 );
92
93 $this->add_control(
94 'swatches',
95 [
96 'label' => esc_html__('Enable swatches', 'king-addons'),
97 'type' => Controls_Manager::SWITCHER,
98 'return_value' => 'yes',
99 ]
100 );
101
102 $this->add_control(
103 'swatch_source',
104 [
105 'label' => esc_html__('Swatch source', 'king-addons'),
106 'type' => Controls_Manager::SELECT,
107 'options' => [
108 'auto' => esc_html__('Term meta (color/image) fallback to text', 'king-addons'),
109 'text' => esc_html__('Text only', 'king-addons'),
110 ],
111 'default' => 'auto',
112 'condition' => [
113 'swatches' => 'yes',
114 ],
115 ]
116 );
117
118 $this->add_control(
119 'show_selected',
120 [
121 'label' => esc_html__('Show selected value', 'king-addons'),
122 'type' => Controls_Manager::SWITCHER,
123 'return_value' => 'yes',
124 'default' => 'yes',
125 ]
126 );
127
128 $this->add_control(
129 'unavailable_behavior',
130 [
131 'label' => esc_html__('Unavailable behavior', 'king-addons'),
132 'type' => Controls_Manager::SELECT,
133 'options' => [
134 'disable' => esc_html__('Disable', 'king-addons'),
135 'hide' => esc_html__('Hide', 'king-addons'),
136 ],
137 'default' => 'disable',
138 ]
139 );
140
141 $this->add_control(
142 'swatch_shape',
143 [
144 'label' => esc_html__('Swatch shape', 'king-addons'),
145 'type' => Controls_Manager::SELECT,
146 'options' => [
147 'square' => esc_html__('Square', 'king-addons'),
148 'round' => esc_html__('Round', 'king-addons'),
149 ],
150 'default' => 'square',
151 'condition' => [
152 'swatches' => 'yes',
153 ],
154 ]
155 );
156
157 $this->add_responsive_control(
158 'swatch_size',
159 [
160 'label' => esc_html__('Swatch size (px)', 'king-addons'),
161 'type' => Controls_Manager::SLIDER,
162 'range' => [
163 'px' => ['min' => 16, 'max' => 80],
164 ],
165 'selectors' => [
166 '{{WRAPPER}} .ka-woo-variations__swatch' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};',
167 ],
168 'condition' => [
169 'swatches' => 'yes',
170 ],
171 ]
172 );
173
174 $this->add_responsive_control(
175 'swatch_spacing',
176 [
177 'label' => esc_html__('Swatch spacing (px)', 'king-addons'),
178 'type' => Controls_Manager::SLIDER,
179 'range' => [
180 'px' => ['min' => 0, 'max' => 24],
181 ],
182 'selectors' => [
183 '{{WRAPPER}} .ka-woo-variations__swatches' => 'gap: {{SIZE}}{{UNIT}};',
184 ],
185 'condition' => [
186 'swatches' => 'yes',
187 ],
188 ]
189 );
190
191 $this->end_controls_section();
192 }
193
194 /**
195 * Find any published variable product, for editor preview only.
196 *
197 * @return WC_Product_Variable|null
198 */
199 protected function find_variable_product_for_preview(): ?WC_Product_Variable
200 {
201 if (!function_exists('wc_get_products')) {
202 return null;
203 }
204
205 $found = wc_get_products([
206 'status' => 'publish',
207 'type' => 'variable',
208 'limit' => 1,
209 'orderby' => 'date',
210 'order' => 'DESC',
211 ]);
212
213 $candidate = $found[0] ?? null;
214
215 return $candidate instanceof WC_Product_Variable ? $candidate : null;
216 }
217
218 protected function render(): void
219 {
220 if (
221 !class_exists('WooCommerce') ||
222 !function_exists('wc_attribute_taxonomy_name') ||
223 !function_exists('wc_get_product_terms') ||
224 !function_exists('wc_attribute_label') ||
225 !function_exists('wc_get_template')
226 ) {
227 return;
228 }
229
230 $product = $this->get_product();
231 if (!$product) {
232 $this->render_missing_product_notice();
233 return;
234 }
235
236 $is_editor = class_exists(Woo_Context::class) && Woo_Context::is_editor();
237
238 if (!$product instanceof WC_Product_Variable && $is_editor) {
239 // The builder picks the preview product on its own and the author
240 // cannot choose it, so a store whose newest product is simple would
241 // show this widget as broken. Borrow a variable product to preview.
242 $preview = $this->find_variable_product_for_preview();
243 if ($preview instanceof WC_Product_Variable) {
244 $GLOBALS['product'] = $preview;
245 $product = $preview;
246 }
247 }
248
249 if (!$product instanceof WC_Product_Variable) {
250 if ($is_editor) {
251 echo '<div class="king-addons-woo-builder-notice">' . esc_html__('This widget needs a variable product. Add one to preview it here.', 'king-addons') . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
252 }
253 return;
254 }
255
256 $settings = $this->get_settings_for_display();
257
258 // Swatches ship in the free tier: both freemium competitors give them away,
259 // and it is the first thing compared side by side.
260 $use_swatches = !empty($settings['swatches']);
261 $unavailable_behavior = $settings['unavailable_behavior'] ?? 'disable';
262 $unavailable_behavior = in_array($unavailable_behavior, ['disable', 'hide'], true) ? $unavailable_behavior : 'disable';
263 $show_selected = !empty($settings['show_selected']);
264 $shape = $settings['swatch_shape'] ?? 'square';
265
266 // Build the swatch map from the product's attributes and terms.
267 $swatch_map = [];
268 if ($use_swatches) {
269 $attributes = $product->get_variation_attributes();
270 foreach ($attributes as $attr_name => $options) {
271 // get_variation_attributes() hands back the taxonomy name as-is
272 // for global attributes (pa_color) and the plain label for custom
273 // ones (Size). Only the first kind has terms to read colours from.
274 $taxonomy = taxonomy_exists($attr_name) ? $attr_name : '';
275 $swatches = [];
276 if ('' !== $taxonomy) {
277 $terms = wc_get_product_terms($product->get_id(), $taxonomy, ['fields' => 'all']);
278 foreach ($terms as $term) {
279 $color_raw = get_term_meta($term->term_id, 'color', true);
280 $color = '';
281 if (is_string($color_raw)) {
282 $hex = sanitize_hex_color($color_raw);
283 $color = $hex ? $hex : '';
284 }
285 $thumb_id = get_term_meta($term->term_id, 'thumbnail_id', true);
286 $image = $thumb_id ? esc_url_raw(wp_get_attachment_image_url($thumb_id, 'thumbnail')) : '';
287 $swatches[$term->slug] = [
288 'label' => sanitize_text_field($term->name),
289 'color' => $color,
290 'image' => $image,
291 ];
292 }
293 } else {
294 foreach ($options as $opt) {
295 $swatches[$opt] = [
296 // The attribute name already labels the row; repeating
297 // it inside every swatch reads as "Size Small".
298 'label' => sanitize_text_field($opt),
299 'color' => '',
300 'image' => '',
301 ];
302 }
303 }
304 // Key the map the way WooCommerce names the select it belongs to
305 // (wc_dropdown_variation_attribute_options uses
306 // "attribute_" . sanitize_title($name)); the script looks the
307 // swatches up by that name.
308 $swatch_map['attribute_' . sanitize_title($attr_name)] = $swatches;
309 }
310 }
311
312 $data = [
313 'swatches' => $use_swatches ? 'yes' : 'no',
314 'unavailable' => $unavailable_behavior,
315 'show_selected' => $show_selected ? 'yes' : 'no',
316 'shape' => $shape,
317 'map' => $swatch_map,
318 'source' => in_array(($settings['swatch_source'] ?? 'auto'), ['auto', 'text'], true) ? ($settings['swatch_source'] ?? 'auto') : 'auto',
319 ];
320
321 $classes = 'ka-woo-variations';
322 if (Woo_Context::template_has_widget('woo_product_add_to_cart', 'single_product')) {
323 $classes .= ' ka-woo-variations--atc-external';
324 }
325
326 echo '<div class="' . esc_attr($classes) . '" id="ka-satc-form-anchor" data-swatches="' . ($use_swatches ? 'yes' : 'no') . '" data-unavailable="' . esc_attr($unavailable_behavior) . '" data-swatches-map="' . esc_attr(wp_json_encode($data)) . '">';
327 wc_get_template('single-product/add-to-cart/variable.php', [
328 'available_variations' => $product->get_available_variations(),
329 'attributes' => $product->get_variation_attributes(),
330 'selected_attributes' => $product->get_default_attributes(),
331 ]);
332 echo '</div>';
333 }
334 }
335
336
337
338
339
340
341
342