PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.6.3
JetFormBuilder — Dynamic Blocks Form Builder v3.6.3
3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / includes / generators / base-v2.php
jetformbuilder / includes / generators Last commit date
base-v2.php 2 months ago base.php 2 years ago get-from-db.php 2 months ago get-from-rest-api.php 2 months ago get-from-users.php 2 months ago get-related-posts.php 2 months ago legacy-parser.php 2 months ago num-range-manual.php 2 months ago num-range.php 2 months ago registry.php 2 months ago
base-v2.php
443 lines
1 <?php
2 /**
3 * Enhanced base class for option generators with structured settings support.
4 *
5 * @package Jet_Form_Builder\Generators
6 */
7
8 namespace Jet_Form_Builder\Generators;
9
10 // If this file is called directly, abort.
11 if ( ! defined( 'WPINC' ) ) {
12 die;
13 }
14
15 /**
16 * Base_V2 class - Enhanced generator base with schema support.
17 *
18 * Extends the original Base class to add:
19 * - Structured settings schema for dynamic UI generation
20 * - Support for cascading/auto-update fields
21 * - Backward compatibility with legacy generators
22 */
23 abstract class Base_V2 extends Base {
24
25 /**
26 * Returns structured settings schema for this generator.
27 * Used for both editor UI generation and backend validation.
28 *
29 * Schema format:
30 * [
31 * 'field_key' => [
32 * 'type' => 'string|number|boolean|array',
33 * 'default' => mixed,
34 * 'label' => string,
35 * 'control' => 'text|number|select|toggle|textarea',
36 * 'help' => string (optional),
37 * 'placeholder' => string (optional),
38 * 'options' => array (for select control),
39 * 'min' => number (for number control),
40 * 'max' => number (for number control),
41 * 'step' => number (for number control),
42 * ],
43 * ]
44 *
45 * @return array Schema definition
46 */
47 abstract public function get_settings_schema(): array;
48
49 /**
50 * Returns block attributes definitions for this generator.
51 * Can be used to dynamically extend block.json attributes.
52 *
53 * @return array Attribute definitions compatible with block.json format
54 */
55 public function get_block_attributes(): array {
56 $attributes = array();
57
58 foreach ( $this->get_settings_schema() as $key => $field ) {
59 $attr_name = $this->get_attribute_name( $key );
60
61 $attributes[ $attr_name ] = array(
62 'type' => $this->map_schema_type_to_block_type( $field['type'] ?? 'string' ),
63 'default' => $field['default'] ?? $this->get_default_for_type( $field['type'] ?? 'string' ),
64 );
65 }
66
67 return $attributes;
68 }
69
70 /**
71 * Maps schema type to WordPress block attribute type.
72 *
73 * @param string $type Schema type.
74 *
75 * @return string Block attribute type
76 */
77 protected function map_schema_type_to_block_type( string $type ): string {
78 $map = array(
79 'string' => 'string',
80 'number' => 'number',
81 'boolean' => 'boolean',
82 'array' => 'array',
83 'object' => 'object',
84 );
85
86 return $map[ $type ] ?? 'string';
87 }
88
89 /**
90 * Returns default value for a given type.
91 *
92 * @param string $type Field type.
93 *
94 * @return mixed Default value
95 */
96 protected function get_default_for_type( string $type ) {
97 $defaults = array(
98 'string' => '',
99 'number' => 0,
100 'boolean' => false,
101 'array' => array(),
102 'object' => array(),
103 );
104
105 return $defaults[ $type ] ?? '';
106 }
107
108 /**
109 * Generates attribute name prefixed with generator ID to avoid collisions.
110 *
111 * @param string $key Setting key from schema.
112 *
113 * @return string Prefixed attribute name
114 */
115 public function get_attribute_name( string $key ): string {
116 return 'gen_' . $this->get_id() . '_' . $key;
117 }
118
119 /**
120 * Parses settings from block attributes.
121 * Extracts generator-specific settings from prefixed attributes.
122 *
123 * @param array $block_attrs All block attributes.
124 *
125 * @return array Parsed settings with original keys (without prefix)
126 */
127 public function parse_settings( array $block_attrs ): array {
128 $settings = array();
129 $prefix = 'gen_' . $this->get_id() . '_';
130 $schema = $this->get_settings_schema();
131
132 foreach ( $schema as $key => $field_def ) {
133 $attr_name = $prefix . $key;
134
135 if ( isset( $block_attrs[ $attr_name ] ) ) {
136 $settings[ $key ] = $block_attrs[ $attr_name ];
137 } else {
138 // Use default from schema
139 $settings[ $key ] = $field_def['default'] ?? $this->get_default_for_type( $field_def['type'] ?? 'string' );
140 }
141 }
142
143 return $settings;
144 }
145
146 /**
147 * Validates settings against schema.
148 *
149 * @param array $settings Settings to validate.
150 *
151 * @return array Validation result ['valid' => bool, 'errors' => array]
152 */
153 public function validate_settings( array $settings ): array {
154 $errors = array();
155 $schema = $this->get_settings_schema();
156
157 foreach ( $schema as $key => $field_def ) {
158 // Check required fields
159 if ( ! empty( $field_def['required'] ) ) {
160 if ( ! isset( $settings[ $key ] ) || '' === $settings[ $key ] ) {
161 $errors[ $key ] = sprintf(
162 /* translators: %s: field label */
163 __( '%s is required.', 'jet-form-builder' ),
164 $field_def['label'] ?? $key
165 );
166 }
167 }
168
169 // Type validation
170 if ( isset( $settings[ $key ] ) && isset( $field_def['type'] ) ) {
171 $value = $settings[ $key ];
172 $type = $field_def['type'];
173
174 if ( 'number' === $type && ! is_numeric( $value ) && '' !== $value ) {
175 $errors[ $key ] = sprintf(
176 /* translators: %s: field label */
177 __( '%s must be a number.', 'jet-form-builder' ),
178 $field_def['label'] ?? $key
179 );
180 }
181 }
182 }
183
184 return array(
185 'valid' => empty( $errors ),
186 'errors' => $errors,
187 );
188 }
189
190 /**
191 * Whether this generator supports auto-update/cascading feature.
192 * Override in child classes to enable.
193 *
194 * @return bool
195 */
196 public function supports_auto_update(): bool {
197 return false;
198 }
199
200 /**
201 * Returns list of context fields this generator can use for auto-update.
202 * Override in child classes to specify which fields can be listened to.
203 *
204 * @return array Array of context descriptions
205 */
206 public function get_auto_update_context_fields(): array {
207 return array();
208 }
209
210 /**
211 * Returns the value type supported by auto-update context.
212 *
213 * Supported values:
214 * - scalar: single-value fields only
215 * - scalar_or_array: single-value and multi-value fields
216 *
217 * @return string
218 */
219 public function get_auto_update_value_type(): string {
220 return 'scalar';
221 }
222
223 /**
224 * Returns how the generator should behave when auto-update context is empty.
225 *
226 * Supported values:
227 * - clear_on_empty: always clear dependent options
228 * - fallback_to_static: always use static generator settings
229 * - fallback_to_static_if_configured: use static settings only when a valid fallback exists
230 *
231 * @return string
232 */
233 public function get_auto_update_empty_context_policy(): string {
234 return 'clear_on_empty';
235 }
236
237 /**
238 * Whether the current generator settings provide a valid static fallback
239 * for empty auto-update context.
240 *
241 * @param array $settings Parsed generator settings.
242 *
243 * @return bool
244 */
245 public function has_auto_update_static_fallback( array $settings ): bool {
246 return false;
247 }
248
249 /**
250 * Whether the frontend should clear options immediately when auto-update
251 * context is empty for this field configuration.
252 *
253 * @param array $settings Parsed generator settings.
254 *
255 * @return bool
256 */
257 public function should_clear_on_empty_auto_update_context( array $settings ): bool {
258 $policy = $this->get_auto_update_empty_context_policy();
259
260 if ( 'fallback_to_static' === $policy ) {
261 return false;
262 }
263
264 if ( 'fallback_to_static_if_configured' === $policy ) {
265 return ! $this->has_auto_update_static_fallback( $settings );
266 }
267
268 return true;
269 }
270
271 /**
272 * Generate options with context from dependent fields.
273 * Used when auto-update is triggered by another field's change.
274 *
275 * @param array $settings Parsed settings from block attributes.
276 * @param array $context Associative array ['field_name' => 'value'] from dependent fields.
277 *
278 * @return array Generated options
279 */
280 public function generate_with_context( array $settings, array $context = array() ): array {
281 // Default implementation ignores context and calls regular generate
282 return $this->generate( $settings );
283 }
284
285 /**
286 * Override incoming_args to use schema-based parsing.
287 * For backward compatibility, this still works with the old system.
288 *
289 * @return array
290 */
291 public function incoming_args() {
292 $args = array();
293 $schema = $this->get_settings_schema();
294
295 foreach ( $schema as $key => $field_def ) {
296 $attr_name = $this->get_attribute_name( $key );
297 $args[ $attr_name ] = $this->get_parse_callback_for_type( $field_def['type'] ?? 'string' );
298 }
299
300 return $args;
301 }
302
303 /**
304 * Returns parse callback for a given type.
305 *
306 * @param string $type Field type.
307 *
308 * @return callable Parse callback
309 */
310 protected function get_parse_callback_for_type( string $type ): callable {
311 switch ( $type ) {
312 case 'number':
313 return 'floatval';
314 case 'boolean':
315 return function ( $value ) {
316 return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
317 };
318 case 'array':
319 return function ( $value ) {
320 return is_array( $value ) ? $value : array();
321 };
322 default:
323 return function ( $value ) {
324 return (string) $value;
325 };
326 }
327 }
328
329 /**
330 * Override get_values to work with new schema system.
331 * Supports multiple data sources in priority order:
332 * 1. generator_args (object) - new preferred format
333 * 2. Prefixed attributes (gen_<id>_<key>) - intermediate format
334 * 3. generator_field (pipe-delimited string) - legacy format
335 *
336 * @param array $args Block attributes.
337 *
338 * @return array Generated options
339 */
340 public function get_values( $args ) {
341
342 // Priority 1: Check for generator_args object (new format)
343 if ( ! empty( $args['generator_args'] ) && is_array( $args['generator_args'] ) ) {
344 $settings = $this->parse_generator_args( $args['generator_args'] );
345 if ( $this->has_meaningful_values( $settings ) ) {
346 $result = $this->generate( $settings );
347 return $result;
348 }
349 }
350
351 // Priority 2: Try prefixed attributes
352 $settings = $this->parse_settings( $args );
353 if ( $this->has_meaningful_values( $settings ) ) {
354 $result = $this->generate( $settings );
355 return $result;
356 }
357
358 // Priority 3: Fall back to legacy generator_field (pipe-delimited)
359 if ( ! empty( $args['generator_field'] ) ) {
360 $legacy_input = $this->enrich_legacy_field( $args['generator_field'], $args );
361 $result = $this->generate( $legacy_input );
362 return $result;
363 }
364
365 // No settings found, return empty
366 $result = $this->generate( $settings );
367 return $result;
368 }
369
370 /**
371 * Enrich legacy generator_field with additional block attributes.
372 * Override in child classes to migrate legacy attrs into generate() input.
373 *
374 * @param string $generator_field Legacy pipe-delimited string.
375 * @param array $block_attrs All block attributes.
376 *
377 * @return mixed Enriched input for generate().
378 */
379 protected function enrich_legacy_field( string $generator_field, array $block_attrs ) {
380 return $generator_field;
381 }
382
383 /**
384 * Parse settings from generator_args object.
385 *
386 * @param array $generator_args Generator args object from block attributes.
387 *
388 * @return array Parsed settings with defaults applied.
389 */
390 public function parse_generator_args( array $generator_args ): array {
391 $settings = array();
392 $schema = $this->get_settings_schema();
393
394 foreach ( $schema as $key => $field_def ) {
395 if ( isset( $generator_args[ $key ] ) ) {
396 $settings[ $key ] = $generator_args[ $key ];
397 } else {
398 $settings[ $key ] = $field_def['default'] ?? $this->get_default_for_type( $field_def['type'] ?? 'string' );
399 }
400 }
401
402 return $settings;
403 }
404
405 /**
406 * Check if settings contain meaningful (non-default) values.
407 *
408 * @param array $settings Settings array to check.
409 *
410 * @return bool True if has meaningful values.
411 */
412 protected function has_meaningful_values( array $settings ): bool {
413 $schema = $this->get_settings_schema();
414
415 foreach ( $schema as $key => $field_def ) {
416 $default = $field_def['default'] ?? $this->get_default_for_type( $field_def['type'] ?? 'string' );
417 if ( isset( $settings[ $key ] ) && $settings[ $key ] !== $default && '' !== $settings[ $key ] ) {
418 return true;
419 }
420 }
421
422 return false;
423 }
424
425 /**
426 * Returns schema for JS localization.
427 * Includes all necessary data for editor UI generation.
428 *
429 * @return array Schema data for JavaScript
430 */
431 public function get_schema_for_js(): array {
432 return array(
433 'id' => $this->get_id(),
434 'name' => $this->get_name(),
435 'schema' => $this->get_settings_schema(),
436 'supports_update' => $this->supports_auto_update(),
437 'update_context' => $this->get_auto_update_context_fields(),
438 'update_value_type' => $this->get_auto_update_value_type(),
439 'update_empty_context_policy' => $this->get_auto_update_empty_context_policy(),
440 );
441 }
442 }
443