1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace GeminiLabs\Castor\Forms\Fields; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use GeminiLabs\Castor\Services\Normalizer; |
7
|
|
|
|
8
|
|
|
abstract class Base |
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* @var array |
12
|
|
|
*/ |
13
|
|
|
protected $args; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @var array |
17
|
|
|
*/ |
18
|
|
|
protected $dependencies = []; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Whether the field has multiple values |
22
|
|
|
* |
23
|
|
|
* @var bool |
24
|
|
|
*/ |
25
|
|
|
protected $multi = false; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Whether the field is rendered outside of the form table |
29
|
|
|
* |
30
|
|
|
* @var bool |
31
|
|
|
*/ |
32
|
|
|
protected $outside = false; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* The field element tag (i.e. "input") |
36
|
|
|
* |
37
|
|
|
* @var string |
38
|
|
|
*/ |
39
|
|
|
protected $element; |
40
|
|
|
|
41
|
|
|
public function __construct( array $args = [] ) |
42
|
|
|
{ |
43
|
|
|
$this->args = $args; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @param string $property |
48
|
|
|
* |
49
|
|
|
* @return mixed |
50
|
|
|
* @throws Exception |
51
|
|
|
*/ |
52
|
|
|
public function __get( $property ) |
53
|
|
|
{ |
54
|
|
|
switch( $property ) { |
55
|
|
|
case 'args'; |
|
|
|
|
56
|
|
|
case 'dependencies'; |
|
|
|
|
57
|
|
|
case 'element'; |
|
|
|
|
58
|
|
|
case 'multi'; |
|
|
|
|
59
|
|
|
case 'outside'; |
|
|
|
|
60
|
|
|
return $this->$property; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $property ); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Generate the field description |
68
|
|
|
* |
69
|
|
|
* @param bool $paragraph |
70
|
|
|
* |
71
|
|
|
* @return null|string |
72
|
|
|
*/ |
73
|
|
|
public function generateDescription( $paragraph = true ) |
74
|
|
|
{ |
75
|
|
|
if( !isset( $this->args['desc'] ) || !$this->args['desc'] )return; |
76
|
|
|
|
77
|
|
|
$tag = ( !!$paragraph || $paragraph == 'p' ) ? 'p' : 'span'; |
78
|
|
|
|
79
|
|
|
return sprintf( '<%1$s class="description">%2$s</%1$s>', $tag, $this->args['desc'] ); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Generate the field label |
84
|
|
|
* |
85
|
|
|
* @return null|string |
86
|
|
|
*/ |
87
|
|
|
public function generateLabel() |
88
|
|
|
{ |
89
|
|
|
if( empty( $this->args['label'] ))return; |
90
|
|
|
|
91
|
|
|
$for = !!$this->args['id'] |
92
|
|
|
? " for=\"{$this->args['id']}\"" |
93
|
|
|
: ''; |
94
|
|
|
|
95
|
|
|
return sprintf( '<label%s>%s</label>', $for, $this->args['label'] ); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Render this field type |
100
|
|
|
* |
101
|
|
|
* @return string |
102
|
|
|
*/ |
103
|
|
|
abstract public function render(); |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Convert a value to camel case. |
107
|
|
|
* |
108
|
|
|
* @param string $value |
109
|
|
|
* |
110
|
|
|
* @return string |
111
|
|
|
*/ |
112
|
|
|
protected function camelCase( $value ) |
113
|
|
|
{ |
114
|
|
|
$value = ucwords( str_replace( ['-', '_'], ' ', $value )); |
115
|
|
|
|
116
|
|
|
return lcfirst( str_replace( ' ', '', $value )); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Implode the field attributes |
121
|
|
|
* |
122
|
|
|
* @return array |
123
|
|
|
*/ |
124
|
|
|
protected function implodeAttributes( $defaults = [] ) |
125
|
|
|
{ |
126
|
|
|
return $this->normalize( $defaults, 'implode' ); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Implode multi-field items |
131
|
|
|
* |
132
|
|
|
* @return null|string |
133
|
|
|
*/ |
134
|
|
|
protected function implodeOptions( $method = 'select_option', $default = null ) |
135
|
|
|
{ |
136
|
|
|
$this->args['default'] ?: $this->args['default'] = $default; |
137
|
|
|
|
138
|
|
|
$method = $this->camelCase( $method ); |
139
|
|
|
|
140
|
|
|
$method = method_exists( $this, $method ) |
141
|
|
|
? $method |
142
|
|
|
: 'selectOption'; |
143
|
|
|
|
144
|
|
|
$i = 0; |
145
|
|
|
|
146
|
|
|
if( $method === 'singleInput' ) { |
147
|
|
|
|
148
|
|
|
if( !isset( $this->args['options'] ) || empty( $this->args['options'] ))return; |
149
|
|
|
|
150
|
|
|
// hack to make sure unset single checkbox values start at 1 instead of 0 |
151
|
|
|
if( key( $this->args['options'] ) === 0 ) { |
152
|
|
|
$options = ['1' => $this->args['options'][0]]; |
153
|
|
|
$this->args['options'] = $options; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return $this->singleInput(); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
return array_reduce( array_keys( $this->args['options'] ), function( $carry, $key ) use ( &$i, $method ) { |
160
|
|
|
return $carry .= $this->$method( $key, $i++ ); |
161
|
|
|
}); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Normalize attributes for this specific field type |
166
|
|
|
* |
167
|
|
|
* @param bool|string $implode |
168
|
|
|
* |
169
|
|
|
* @return array |
170
|
|
|
*/ |
171
|
|
|
protected function normalize( array $defaults = [], $implode = false ) |
172
|
|
|
{ |
173
|
|
|
$args = $this->mergeAttributesWith( $defaults ); |
174
|
|
|
|
175
|
|
|
$normalize = new Normalize; |
176
|
|
|
|
177
|
|
|
return ( $this->element && method_exists( $normalize, $this->element )) |
178
|
|
|
? $normalize->{$this->element}( $args, $implode ) |
179
|
|
|
: ( !!$implode ? '' : [] ); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Merge and overwrite empty $this->args values with $defaults |
184
|
|
|
* |
185
|
|
|
* @return array |
186
|
|
|
*/ |
187
|
|
|
protected function mergeAttributesWith( array $defaults ) |
188
|
|
|
{ |
189
|
|
|
// similar to array_merge except overwrite empty values |
190
|
|
|
foreach( $defaults as $key => $value ) { |
191
|
|
|
if( isset( $this->args[ $key ] ) && !empty( $this->args[ $key ] ))continue; |
192
|
|
|
$this->args[ $key ] = $value; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$attributes = $this->args['attributes']; |
196
|
|
|
|
197
|
|
|
// prioritize $attributes over $this->args, don't worry about duplicates |
198
|
|
|
return array_merge( $this->args, $attributes ); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Generate checkboxes and radios |
203
|
|
|
* |
204
|
|
|
* @param string $optionKey |
205
|
|
|
* @param string $number |
206
|
|
|
* @param string $type |
207
|
|
|
* |
208
|
|
|
* @return null|string |
209
|
|
|
*/ |
210
|
|
|
protected function multiInput( $optionKey, $number, $type = 'radio' ) |
211
|
|
|
{ |
212
|
|
|
$args = $this->multiInputArgs( $type, $optionKey, $number ); |
213
|
|
|
|
214
|
|
|
if( !$args )return; |
215
|
|
|
|
216
|
|
|
$attributes = ''; |
217
|
|
|
|
218
|
|
|
foreach( $args['attributes'] as $key => $val ) { |
219
|
|
|
$attributes .= sprintf( '%s="%s" ', $key, $val ); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
return sprintf( '<li><label for="%s"><input %s%s/> %s</label></li>', |
223
|
|
|
$args['attributes']['id'], |
224
|
|
|
$attributes, |
225
|
|
|
checked( $args['value'], $args['attributes']['value'], false ), |
226
|
|
|
$args['label'] |
227
|
|
|
); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Build the checkbox/radio args |
232
|
|
|
* |
233
|
|
|
* @param string $type |
234
|
|
|
* @param string $optionName |
235
|
|
|
* @param string $number |
236
|
|
|
* |
237
|
|
|
* @return array|null |
238
|
|
|
*/ |
239
|
|
|
protected function multiInputArgs( $type, $optionName, $number ) |
240
|
|
|
{ |
241
|
|
|
$defaults = [ |
242
|
|
|
'class' => '', |
243
|
|
|
'name' => '', |
244
|
|
|
'type' => $type, |
245
|
|
|
'value' => '', |
246
|
|
|
]; |
247
|
|
|
|
248
|
|
|
$args = []; |
249
|
|
|
|
250
|
|
|
$value = $this->args['options'][ $optionName ]; |
251
|
|
|
|
252
|
|
|
if( is_array( $value )) { |
253
|
|
|
$args = $value; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
if( is_string( $value )) { |
257
|
|
|
$label = $value; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
isset( $args['name'] ) ?: $args['name'] = $optionName; |
261
|
|
|
isset( $args['value'] ) ?: $args['value'] = $optionName; |
262
|
|
|
|
263
|
|
|
$args = wp_parse_args( $args, $defaults ); |
264
|
|
|
|
265
|
|
|
if( !isset( $label ) || $args['name'] === '' )return; |
266
|
|
|
|
267
|
|
|
$args['id'] = $this->args['id'] . "-{$number}"; |
268
|
|
|
$args['name'] = $this->args['name'] . ( $type === 'checkbox' && $this->multi ? '[]' : '' ); |
269
|
|
|
|
270
|
|
|
$args = array_filter( $args, function( $value ) { |
271
|
|
|
return $value !== ''; |
272
|
|
|
}); |
273
|
|
|
|
274
|
|
|
if( is_array( $this->args['value'] )) { |
275
|
|
|
if( in_array( $args['value'], $this->args['value'] )) { |
276
|
|
|
$this->args['default'] = $args['value']; |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
else if( $this->args['value'] ) { |
280
|
|
|
$this->args['default'] = $this->args['value']; |
281
|
|
|
} |
282
|
|
|
else if( $type == 'radio' && !$this->args['default'] ) { |
283
|
|
|
$this->args['default'] = 0; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
return [ |
287
|
|
|
'attributes' => $args, |
288
|
|
|
'label' => $label, |
289
|
|
|
'value' => $this->args['default'], |
290
|
|
|
]; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Generate checkboxes |
295
|
|
|
* |
296
|
|
|
* @param string $optionKey |
297
|
|
|
* @param string $number |
298
|
|
|
* |
299
|
|
|
* @return null|string |
300
|
|
|
*/ |
301
|
|
|
protected function multiInputCheckbox( $optionKey, $number ) |
302
|
|
|
{ |
303
|
|
|
return $this->multiInput( $optionKey, $number, 'checkbox' ); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Generate select options |
308
|
|
|
* |
309
|
|
|
* @param string $optionKey |
310
|
|
|
* |
311
|
|
|
* @return string |
312
|
|
|
*/ |
313
|
|
|
protected function selectOption( $optionKey ) |
314
|
|
|
{ |
315
|
|
|
return sprintf( '<option value="%s"%s>%s</option>', |
316
|
|
|
$optionKey, |
317
|
|
|
selected( $this->args['value'], $optionKey, false ), |
318
|
|
|
$this->args['options'][ $optionKey ] |
319
|
|
|
); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Generate a single checkbox |
324
|
|
|
* |
325
|
|
|
* @param string $type |
326
|
|
|
* |
327
|
|
|
* @return null|string |
328
|
|
|
*/ |
329
|
|
|
protected function singleInput( $type = 'checkbox' ) |
330
|
|
|
{ |
331
|
|
|
$optionKey = key( $this->args['options'] ); |
332
|
|
|
|
333
|
|
|
$args = $this->multiInputArgs( $type, $optionKey, 1 ); |
334
|
|
|
|
335
|
|
|
if( !$args )return; |
336
|
|
|
|
337
|
|
|
$atts = $this->normalize(); |
338
|
|
|
$atts = wp_parse_args( $args['attributes'], $atts ); |
339
|
|
|
|
340
|
|
|
$attributes = ''; |
341
|
|
|
|
342
|
|
|
foreach( $atts as $key => $val ) { |
343
|
|
|
$attributes .= sprintf( '%s="%s" ', $key, $val ); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
return sprintf( '<label for="%s"><input %s%s/> %s</label>', |
347
|
|
|
$atts['id'], |
348
|
|
|
$attributes, |
349
|
|
|
checked( $args['value'], $atts['value'], false ), |
350
|
|
|
$args['label'] |
351
|
|
|
); |
352
|
|
|
} |
353
|
|
|
} |
354
|
|
|
|
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break
.There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.