|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Charcoal\Admin\Property; |
|
4
|
|
|
|
|
5
|
|
|
use \InvalidArgumentException; |
|
6
|
|
|
use \Exception; |
|
7
|
|
|
|
|
8
|
|
|
// PSR-3 logger dependencies |
|
9
|
|
|
use \Psr\Log\LoggerAwareInterface; |
|
10
|
|
|
use \Psr\Log\LoggerAwareTrait; |
|
11
|
|
|
use \Psr\Log\NullLogger; |
|
12
|
|
|
|
|
13
|
|
|
// Module `charcoal-property` dependencies |
|
14
|
|
|
use \Charcoal\Property\PropertyInterface; |
|
15
|
|
|
|
|
16
|
|
|
// Module `charcoal-translation` dependencies |
|
17
|
|
|
use \Charcoal\Translation\TranslationConfig; |
|
18
|
|
|
|
|
19
|
|
|
// Intra-module (`charcoal-admin`) dependencies |
|
20
|
|
|
use \Charcoal\Admin\Property\PropertyInputInterface; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* |
|
24
|
|
|
*/ |
|
25
|
|
|
abstract class AbstractPropertyInput implements |
|
26
|
|
|
PropertyInputInterface, |
|
27
|
|
|
LoggerAwareInterface |
|
28
|
|
|
{ |
|
29
|
|
|
use LoggerAwareTrait; |
|
30
|
|
|
|
|
31
|
|
|
/** |
|
32
|
|
|
* @var string $lang |
|
33
|
|
|
*/ |
|
34
|
|
|
private $lang; |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* @var string $ident |
|
38
|
|
|
*/ |
|
39
|
|
|
private $ident; |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* @var boolean $readOnly |
|
43
|
|
|
*/ |
|
44
|
|
|
private $readOnly; |
|
45
|
|
|
/** |
|
46
|
|
|
* @var boolean $required |
|
47
|
|
|
*/ |
|
48
|
|
|
private $required; |
|
49
|
|
|
/** |
|
50
|
|
|
* @var boolean $disabled |
|
51
|
|
|
*/ |
|
52
|
|
|
private $disabled; |
|
53
|
|
|
/** |
|
54
|
|
|
* @var boolean $multiple |
|
55
|
|
|
*/ |
|
56
|
|
|
private $multiple; |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* @var string $type |
|
60
|
|
|
*/ |
|
61
|
|
|
protected $type; |
|
62
|
|
|
/** |
|
63
|
|
|
* @var string $inputType |
|
64
|
|
|
*/ |
|
65
|
|
|
protected $inputType; |
|
66
|
|
|
|
|
67
|
|
|
/** |
|
68
|
|
|
* @var string $inputId |
|
69
|
|
|
*/ |
|
70
|
|
|
protected $inputId; |
|
71
|
|
|
/** |
|
72
|
|
|
* @var string $inputClass |
|
73
|
|
|
*/ |
|
74
|
|
|
protected $inputClass = ''; |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* @var array $propertyData |
|
78
|
|
|
*/ |
|
79
|
|
|
private $propertyData = []; |
|
80
|
|
|
|
|
81
|
|
|
private $propertyVal; |
|
82
|
|
|
|
|
83
|
|
|
/** |
|
84
|
|
|
* @var PropertyInterface $property |
|
85
|
|
|
*/ |
|
86
|
|
|
private $property; |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* @param array|\ArrayAccess $data Constructor data. |
|
90
|
|
|
*/ |
|
91
|
|
View Code Duplication |
public function __construct($data = null) |
|
|
|
|
|
|
92
|
|
|
{ |
|
93
|
|
|
if (!isset($data['logger'])) { |
|
94
|
|
|
$data['logger'] = new NullLogger(); |
|
95
|
|
|
} |
|
96
|
|
|
$this->setLogger($data['logger']); |
|
97
|
|
|
} |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* This function takes an array and fill the model object with its value. |
|
101
|
|
|
* |
|
102
|
|
|
* This method either calls a setter for each key (`set_{$key}()`) or sets a public member. |
|
103
|
|
|
* |
|
104
|
|
|
* For example, calling with `setData(['properties'=>$properties])` would call |
|
105
|
|
|
* `setProperties($properties)`, becasue `setProperties()` exists. |
|
106
|
|
|
* |
|
107
|
|
|
* But calling with `setData(['foobar'=>$foo])` would set the `$foobar` member |
|
108
|
|
|
* on the metadata object, because the method `set_foobar()` does not exist. |
|
109
|
|
|
* |
|
110
|
|
|
* @param array $data The input data. |
|
111
|
|
|
* @return Input Chainable |
|
112
|
|
|
*/ |
|
113
|
|
View Code Duplication |
public function setData(array $data) |
|
|
|
|
|
|
114
|
|
|
{ |
|
115
|
|
|
foreach ($data as $prop => $val) { |
|
116
|
|
|
$func = [$this, $this->setter($prop)]; |
|
117
|
|
|
if (is_callable($func)) { |
|
118
|
|
|
call_user_func($func, $val); |
|
119
|
|
|
unset($data[$prop]); |
|
120
|
|
|
} else { |
|
121
|
|
|
$this->{$prop} = $val; |
|
122
|
|
|
} |
|
123
|
|
|
} |
|
124
|
|
|
|
|
125
|
|
|
$this->propertyData = $data; |
|
126
|
|
|
|
|
127
|
|
|
return $this; |
|
|
|
|
|
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
public function setPropertyVal($val) |
|
131
|
|
|
{ |
|
132
|
|
|
$this->propertyVal = $val; |
|
133
|
|
|
return $this; |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
public function propertyVal() |
|
137
|
|
|
{ |
|
138
|
|
|
return $this->propertyVal; |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
|
|
/** |
|
142
|
|
|
* @ |
|
143
|
|
|
*/ |
|
144
|
|
|
public function setLang($lang) |
|
145
|
|
|
{ |
|
146
|
|
|
$this->lang = $lang; |
|
147
|
|
|
return $this; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
public function lang() |
|
151
|
|
|
{ |
|
152
|
|
|
if ($this->lang === null) { |
|
153
|
|
|
return TranslationConfig::instance()->currentLanguage(); |
|
154
|
|
|
} |
|
155
|
|
|
return $this->lang; |
|
156
|
|
|
} |
|
157
|
|
|
|
|
158
|
|
|
/** |
|
159
|
|
|
* @param string $ident Input identifier. |
|
160
|
|
|
* @throws InvalidArgumentException If the ident is not a string. |
|
161
|
|
|
* @return Widget Chainable |
|
162
|
|
|
*/ |
|
163
|
|
View Code Duplication |
public function setIdent($ident) |
|
|
|
|
|
|
164
|
|
|
{ |
|
165
|
|
|
if (!is_string($ident)) { |
|
166
|
|
|
throw new InvalidArgumentException( |
|
167
|
|
|
__CLASS__.'::'.__FUNCTION__.'() - Ident must be a string.' |
|
168
|
|
|
); |
|
169
|
|
|
} |
|
170
|
|
|
$this->ident = $ident; |
|
171
|
|
|
return $this; |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
/** |
|
175
|
|
|
* @return string |
|
176
|
|
|
*/ |
|
177
|
|
|
public function ident() |
|
178
|
|
|
{ |
|
179
|
|
|
return $this->ident; |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* @param boolean $readOnly The read-only flag. |
|
184
|
|
|
* @return Widget (Chainable) |
|
185
|
|
|
*/ |
|
186
|
|
|
public function setReadOnly($readOnly) |
|
187
|
|
|
{ |
|
188
|
|
|
$this->readOnly = !!$readOnly; |
|
189
|
|
|
return $this; |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
/** |
|
193
|
|
|
* @return boolean |
|
194
|
|
|
*/ |
|
195
|
|
|
public function readOnly() |
|
196
|
|
|
{ |
|
197
|
|
|
return $this->readOnly; |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
/** |
|
201
|
|
|
* @param boolean $required Required flag. |
|
202
|
|
|
* @return Widget (Chainable) |
|
203
|
|
|
*/ |
|
204
|
|
|
public function setRequired($required) |
|
205
|
|
|
{ |
|
206
|
|
|
$this->required = !!$required; |
|
207
|
|
|
return $this; |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* @return boolean |
|
212
|
|
|
*/ |
|
213
|
|
|
public function required() |
|
214
|
|
|
{ |
|
215
|
|
|
return $this->required; |
|
216
|
|
|
} |
|
217
|
|
|
|
|
218
|
|
|
|
|
219
|
|
|
/** |
|
220
|
|
|
* @param boolean $disabled Disabled flag. |
|
221
|
|
|
* @return Widget (Chainable) |
|
222
|
|
|
*/ |
|
223
|
|
|
public function setDisabled($disabled) |
|
224
|
|
|
{ |
|
225
|
|
|
$this->disabled = !!$disabled; |
|
226
|
|
|
return $this; |
|
227
|
|
|
} |
|
228
|
|
|
|
|
229
|
|
|
/** |
|
230
|
|
|
* @return boolean |
|
231
|
|
|
*/ |
|
232
|
|
|
public function disabled() |
|
233
|
|
|
{ |
|
234
|
|
|
return $this->disabled; |
|
235
|
|
|
} |
|
236
|
|
|
|
|
237
|
|
|
/** |
|
238
|
|
|
* @param boolean $multiple Multiple flag. |
|
239
|
|
|
* @return Widget (Chainable) |
|
240
|
|
|
*/ |
|
241
|
|
|
public function setMultiple($multiple) |
|
242
|
|
|
{ |
|
243
|
|
|
$this->multiple = !!$multiple; |
|
244
|
|
|
return $this; |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
/** |
|
248
|
|
|
* @return boolean |
|
249
|
|
|
*/ |
|
250
|
|
|
public function multiple() |
|
251
|
|
|
{ |
|
252
|
|
|
return $this->multiple; |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
/** |
|
256
|
|
|
* @param string $inputId HTML input id attribute. |
|
257
|
|
|
* @return Input Chainable |
|
258
|
|
|
*/ |
|
259
|
|
|
public function setInputId($inputId) |
|
260
|
|
|
{ |
|
261
|
|
|
$this->inputId = $inputId; |
|
262
|
|
|
return $this; |
|
|
|
|
|
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
/** |
|
266
|
|
|
* Get the input ID. |
|
267
|
|
|
* |
|
268
|
|
|
* If none was previously set, than a unique random one will be generated. |
|
269
|
|
|
* |
|
270
|
|
|
* @return string |
|
271
|
|
|
*/ |
|
272
|
|
|
public function inputId() |
|
273
|
|
|
{ |
|
274
|
|
|
if (!$this->inputId) { |
|
275
|
|
|
$this->inputId = 'input_'.uniqid(); |
|
276
|
|
|
} |
|
277
|
|
|
return $this->inputId; |
|
278
|
|
|
} |
|
279
|
|
|
|
|
280
|
|
|
/** |
|
281
|
|
|
* @param string $inputClass The input class attribute. |
|
282
|
|
|
* @throws InvalidArgumentException If the class is not a string. |
|
283
|
|
|
* @return AbstractPropertyInput Chainable |
|
284
|
|
|
*/ |
|
285
|
|
|
public function setInputClass($inputClass) |
|
286
|
|
|
{ |
|
287
|
|
|
if (!is_string($inputClass)) { |
|
288
|
|
|
throw new InvalidArgumentException( |
|
289
|
|
|
'Input class must be a string' |
|
290
|
|
|
); |
|
291
|
|
|
} |
|
292
|
|
|
$this->inputClass = $inputClass; |
|
293
|
|
|
return $this; |
|
294
|
|
|
} |
|
295
|
|
|
|
|
296
|
|
|
/** |
|
297
|
|
|
* @return string |
|
298
|
|
|
*/ |
|
299
|
|
|
public function inputClass() |
|
300
|
|
|
{ |
|
301
|
|
|
return $this->inputClass; |
|
302
|
|
|
} |
|
303
|
|
|
|
|
304
|
|
|
/** |
|
305
|
|
|
* The input name should always be the property's ident. |
|
306
|
|
|
* |
|
307
|
|
|
* @return string |
|
308
|
|
|
*/ |
|
309
|
|
View Code Duplication |
public function inputName() |
|
|
|
|
|
|
310
|
|
|
{ |
|
311
|
|
|
$name = $this->p()->ident(); |
|
312
|
|
|
if ($this->p()->l10n()) { |
|
313
|
|
|
$name .= '['.$this->lang().']'; |
|
314
|
|
|
} |
|
315
|
|
|
if ($this->multiple()) { |
|
316
|
|
|
$name .= '[]'; |
|
317
|
|
|
} |
|
318
|
|
|
return $name; |
|
319
|
|
|
} |
|
320
|
|
|
|
|
321
|
|
|
/** |
|
322
|
|
|
* @uses AbstractProperty::inputVal() Must handle string sanitization of value. |
|
323
|
|
|
* @throws Exception If the value is invalid. |
|
324
|
|
|
* @return string |
|
325
|
|
|
*/ |
|
326
|
|
|
public function inputVal() |
|
327
|
|
|
{ |
|
328
|
|
|
$prop = $this->p(); |
|
329
|
|
|
$val = $prop->inputVal($this->propertyVal(), ['lang'=>$this->lang()]); |
|
|
|
|
|
|
330
|
|
|
|
|
331
|
|
|
if ($val === null) { |
|
332
|
|
|
return ''; |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
View Code Duplication |
if (!is_scalar($val)) { |
|
|
|
|
|
|
336
|
|
|
throw new Exception( |
|
337
|
|
|
sprintf( |
|
338
|
|
|
'Input value must be a string, received %s', |
|
339
|
|
|
(is_object($val) ? get_class($val) : gettype($val)) |
|
340
|
|
|
) |
|
341
|
|
|
); |
|
342
|
|
|
} |
|
343
|
|
|
|
|
344
|
|
|
return $val; |
|
|
|
|
|
|
345
|
|
|
} |
|
346
|
|
|
|
|
347
|
|
|
/** |
|
348
|
|
|
* @param string $inputType The input type. |
|
349
|
|
|
* @throws InvalidArgumentException If provided argument is not of type 'string'. |
|
350
|
|
|
* @return AbstractPropertyInput Chainable |
|
351
|
|
|
*/ |
|
352
|
|
|
public function setInputType($inputType) |
|
353
|
|
|
{ |
|
354
|
|
|
if (!is_string($inputType)) { |
|
355
|
|
|
throw new InvalidArgumentException( |
|
356
|
|
|
'Input type must be a string.' |
|
357
|
|
|
); |
|
358
|
|
|
} |
|
359
|
|
|
$this->inputType = $inputType; |
|
360
|
|
|
return $this; |
|
361
|
|
|
} |
|
362
|
|
|
|
|
363
|
|
|
/** |
|
364
|
|
|
* @return string |
|
365
|
|
|
*/ |
|
366
|
|
|
public function inputType() |
|
367
|
|
|
{ |
|
368
|
|
|
if ($this->inputType === null) { |
|
369
|
|
|
$this->inputType = 'charcoal/admin/property/input/text'; |
|
370
|
|
|
} |
|
371
|
|
|
return $this->inputType; |
|
372
|
|
|
} |
|
373
|
|
|
|
|
374
|
|
|
/** |
|
375
|
|
|
* @param PropertyInterface $p The property. |
|
376
|
|
|
* @return AbstractPropertyInput Chainable |
|
377
|
|
|
*/ |
|
378
|
|
|
public function setProperty(PropertyInterface $p) |
|
379
|
|
|
{ |
|
380
|
|
|
$this->property = $p; |
|
381
|
|
|
return $this; |
|
382
|
|
|
} |
|
383
|
|
|
|
|
384
|
|
|
/** |
|
385
|
|
|
* @return PropertyInterface |
|
386
|
|
|
*/ |
|
387
|
|
|
public function property() |
|
388
|
|
|
{ |
|
389
|
|
|
return $this->property; |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
|
|
/** |
|
393
|
|
|
* Alias of the `property` method. |
|
394
|
|
|
* |
|
395
|
|
|
* @return PropertyInterface |
|
396
|
|
|
*/ |
|
397
|
|
|
public function p() |
|
398
|
|
|
{ |
|
399
|
|
|
return $this->property(); |
|
400
|
|
|
} |
|
401
|
|
|
|
|
402
|
|
|
/** |
|
403
|
|
|
* Allow an object to define how the key getter are called. |
|
404
|
|
|
* |
|
405
|
|
|
* @param string $key The key to get the getter from. |
|
406
|
|
|
* @return string The getter method name, for a given key. |
|
407
|
|
|
*/ |
|
408
|
|
|
protected function getter($key) |
|
409
|
|
|
{ |
|
410
|
|
|
$getter = $key; |
|
411
|
|
|
return $this->camelize($getter); |
|
412
|
|
|
} |
|
413
|
|
|
|
|
414
|
|
|
/** |
|
415
|
|
|
* Allow an object to define how the key setter are called. |
|
416
|
|
|
* |
|
417
|
|
|
* @param string $key The key to get the setter from. |
|
418
|
|
|
* @return string The setter method name, for a given key. |
|
419
|
|
|
*/ |
|
420
|
|
|
protected function setter($key) |
|
421
|
|
|
{ |
|
422
|
|
|
$setter = 'set_'.$key; |
|
423
|
|
|
return $this->camelize($setter); |
|
424
|
|
|
|
|
425
|
|
|
} |
|
426
|
|
|
|
|
427
|
|
|
/** |
|
428
|
|
|
* Transform a snake_case string to camelCase. |
|
429
|
|
|
* |
|
430
|
|
|
* @param string $str The snake_case string to camelize. |
|
431
|
|
|
* @return string The camelCase string. |
|
432
|
|
|
*/ |
|
433
|
|
|
private function camelize($str) |
|
434
|
|
|
{ |
|
435
|
|
|
return lcfirst(implode('', array_map('ucfirst', explode('_', $str)))); |
|
436
|
|
|
} |
|
437
|
|
|
} |
|
438
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.