PluginProbe ʕ •ᴥ•ʔ
ECS – Ele Custom Skin for Elementor / 4.3.6
ECS – Ele Custom Skin for Elementor v4.3.6
4.3.6 4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.11 4.1.10 4.1.9 4.1.8 4.1.6 4.1.7 4.1.5 4.1.4 4.1.1 4.1.2 trunk 1.0.0 1.0.1 1.0.9 1.1.3 1.1.4 1.1.5 1.2.0 1.2.1 1.2.4 1.2.5 1.3.10 1.3.11 1.3.3 1.3.4 1.3.6 1.3.7 1.3.9 1.4.0 2.0.2 2.1.0 2.2.0 2.2.1 2.2.2 3.0.0 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 4.1.0
ele-custom-skin / modules / dynamic-repeater / class-ecs-drb-mapper.php
ele-custom-skin / modules / dynamic-repeater Last commit date
assets 2 weeks ago sources 2 months ago class-ecs-drb-mapper.php 2 months ago class-ecs-drb-sources.php 2 months ago class-ecs-dynamic-repeater-module.php 2 weeks ago
class-ecs-drb-mapper.php
151 lines
1 <?php
2 /**
3 * Dynamic Repeater Builder — Mapper
4 *
5 * Converts source rows into Elementor-compatible repeater rows
6 * using a field mapping configuration.
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 class ECS_DRB_Mapper {
14
15 /**
16 * Resolve source rows into Elementor-compatible repeater rows.
17 *
18 * @param array $source_rows Raw rows from a source.
19 * @param array $mapping Array of mapping rules.
20 * @param array $field_types Map of repeater_field_key => Elementor control type.
21 * e.g. [ 'link' => 'url', 'image' => 'media' ]
22 * @return array
23 */
24 public static function resolve( array $source_rows, array $mapping, array $field_types = [] ): array {
25 $active_mapping = array_filter( $mapping, fn( $r ) => ! empty( $r['repeater_field'] ) && ! empty( $r['source_field'] ) );
26
27 $result = [];
28 foreach ( $source_rows as $source_row ) {
29 $row = [];
30 foreach ( $active_mapping as $rule ) {
31 $resolved = self::resolve_field( $source_row, $rule );
32 if ( null !== $resolved ) {
33 $field_key = $rule['repeater_field'];
34 $base_key = preg_replace( '/\[(id|url)\]$/', '', $field_key );
35 // Sub-key rows (image[id], image[url]) must stay as raw strings —
36 // their wrapping into ['id'=>N,'url'=>'...'] happens at the merge step.
37 $field_type = ( $field_key !== $base_key )
38 ? 'text'
39 : ( $field_types[ $field_key ] ?? 'text' );
40 $row[ $field_key ] = self::wrap_value( $resolved, $field_type );
41 }
42 }
43
44 // Merge image sub-keys: 'img[id]' + 'img[url]' → 'img' => ['id' => N, 'url' => '...']
45 $merged = [];
46 foreach ( $row as $key => $val ) {
47 if ( preg_match( '/^(.+)\[(id|url)\]$/', $key, $m ) ) {
48 $base = $m[1];
49 $sub = $m[2];
50 if ( ! isset( $merged[ $base ] ) ) {
51 $merged[ $base ] = [ 'id' => 0, 'url' => '' ];
52 }
53 $merged[ $base ][ $sub ] = $sub === 'id' ? intval( $val ) : (string) $val;
54 } else {
55 $merged[ $key ] = $val;
56 }
57 }
58
59 if ( ! empty( $merged ) ) {
60 $result[] = $merged;
61 }
62 }
63
64 return $result;
65 }
66
67 /**
68 * Wrap a resolved string value into the format Elementor expects
69 * for the given control type.
70 *
71 * @param string $value Resolved string value.
72 * @param string $field_type Elementor control type (url, media, image, text, …).
73 * @return string|array
74 */
75 private static function wrap_value( string $value, string $field_type ) {
76 switch ( $field_type ) {
77 case 'url':
78 return [
79 'url' => $value,
80 'is_external' => '',
81 'nofollow' => '',
82 'custom_attributes' => '',
83 ];
84
85 case 'media':
86 case 'image':
87 // Numeric value → treat as attachment ID and resolve the URL.
88 if ( is_numeric( $value ) && intval( $value ) > 0 ) {
89 $id = intval( $value );
90 $url = wp_get_attachment_url( $id ) ?: '';
91 return [ 'id' => $id, 'url' => $url ];
92 }
93 // URL string → use directly with id = 0.
94 return [ 'id' => 0, 'url' => $value ];
95
96 default:
97 return $value;
98 }
99 }
100
101 /**
102 * Resolve a single field from a source row.
103 * Returns null when the value is empty and skip_if_empty is true,
104 * signalling that the field should be omitted from the row entirely.
105 */
106 private static function resolve_field( array $source_row, array $rule ): ?string {
107 $source_field = $rule['source_field'] ?? '';
108 $before = $rule['before'] ?? '';
109 $after = $rule['after'] ?? '';
110 $fallback = $rule['fallback'] ?? '';
111 $skip_if_empty = (bool) ( $rule['skip_if_empty'] ?? false );
112
113 // Template mode: "{post_title} by {post_author}" — replace each {key}.
114 if ( strpos( $source_field, '{' ) !== false ) {
115 $value = preg_replace_callback(
116 '/\{([a-zA-Z0-9_]+)\}/',
117 function ( $m ) use ( $source_row ) {
118 $raw = $source_row[ $m[1] ] ?? '';
119 if ( is_array( $raw ) ) {
120 $raw = $raw['url'] ?? $raw['ID'] ?? implode( ', ', array_filter( $raw, 'is_scalar' ) );
121 }
122 return (string) $raw;
123 },
124 $source_field
125 );
126 $value = trim( $value );
127 } else {
128 // Simple field key.
129 $value = $source_row[ $source_field ] ?? '';
130 if ( is_array( $value ) ) {
131 $value = $value['url'] ?? $value['ID'] ?? implode( ', ', array_filter( $value, 'is_scalar' ) );
132 }
133 $value = trim( (string) $value );
134 }
135
136 if ( $value === '' ) {
137 if ( $fallback !== '' ) {
138 // Use fallback, but do NOT apply before/after around it —
139 // fallback is meant as a literal replacement, not a formatted value.
140 return $fallback;
141 }
142 if ( $skip_if_empty ) {
143 return null;
144 }
145 return '';
146 }
147
148 return $before . $value . $after;
149 }
150 }
151