Completed
Pull Request — master (#607)
by Tortue
01:40
created
src/Former/Framework/TwitterBootstrap5.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -352,7 +352,7 @@  discard block
 block discarded – undo
352 352
 	 * @param string $text
353 353
 	 * @param array  $attributes
354 354
 	 *
355
-	 * @return string
355
+	 * @return Element
356 356
 	 */
357 357
 	public function createValidationError($text, $attributes = array())
358 358
 	{
@@ -408,7 +408,7 @@  discard block
 block discarded – undo
408 408
 	/**
409 409
 	 * Wrap an item to be prepended or appended to the current field
410 410
 	 *
411
-	 * @return Element A wrapped item
411
+	 * @return string A wrapped item
412 412
 	 */
413 413
 	public function placeAround($item, $place = null)
414 414
 	{
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -139,7 +139,7 @@
 block discarded – undo
139 139
 		$classes = array_intersect($classes, $this->fields);
140 140
 
141 141
 		// Prepend field type
142
-		$classes = array_map(function ($class) {
142
+		$classes = array_map(function($class) {
143 143
 			return Str::startsWith($class, 'col') ? $class : 'input-'.$class;
144 144
 		}, $classes);
145 145
 
Please login to merge, or discard this patch.
Indentation   +480 added lines, -480 removed lines patch added patch discarded remove patch
@@ -13,484 +13,484 @@
 block discarded – undo
13 13
  */
14 14
 class TwitterBootstrap5 extends Framework implements FrameworkInterface
15 15
 {
16
-	/**
17
-	 * Form types that trigger special styling for this Framework
18
-	 *
19
-	 * @var array
20
-	 */
21
-	protected $availableTypes = array('horizontal', 'vertical', 'inline');
22
-
23
-	/**
24
-	 * The button types available
25
-	 *
26
-	 * @var array
27
-	 */
28
-	private $buttons = array(
29
-		'lg',
30
-		'sm',
31
-		'xs',
32
-		'block',
33
-		'link',
34
-		'primary',
35
-		'secondary',
36
-		'warning',
37
-		'danger',
38
-		'success',
39
-		'info',
40
-		'light',
41
-		'dark',
42
-	);
43
-
44
-	/**
45
-	 * The field sizes available
46
-	 *
47
-	 * @var array
48
-	 */
49
-	private $fields = array(
50
-		'lg',
51
-		'sm',
52
-		// 'col-xs-1', 'col-xs-2', 'col-xs-3', 'col-xs-4', 'col-xs-5', 'col-xs-6',
53
-		// 'col-xs-7', 'col-xs-8', 'col-xs-9', 'col-xs-10', 'col-xs-11', 'col-xs-12',
54
-		// 'col-sm-1', 'col-sm-2', 'col-sm-3', 'col-sm-4', 'col-sm-5', 'col-sm-6',
55
-		// 'col-sm-7', 'col-sm-8', 'col-sm-9', 'col-sm-10', 'col-sm-11', 'col-sm-12',
56
-		// 'col-md-1', 'col-md-2', 'col-md-3', 'col-md-4', 'col-md-5', 'col-md-6',
57
-		// 'col-md-7', 'col-md-8', 'col-md-9', 'col-md-10', 'col-md-11', 'col-md-12',
58
-		// 'col-lg-1', 'col-lg-2', 'col-lg-3', 'col-lg-4', 'col-lg-5', 'col-lg-6',
59
-		// 'col-lg-7', 'col-lg-8', 'col-lg-9', 'col-lg-10', 'col-lg-11', 'col-lg-12',
60
-	);
61
-
62
-	/**
63
-	 * The field states available
64
-	 *
65
-	 * @var array
66
-	 */
67
-	protected $states = array(
68
-		'is-invalid',
69
-	);
70
-
71
-	/**
72
-	 * The default HTML tag used for icons
73
-	 *
74
-	 * @var string
75
-	 */
76
-	protected $iconTag = 'i';
77
-
78
-	/**
79
-	 * The default set for icon fonts
80
-	 * By default Bootstrap 4 offers no fonts, but we'll add Font Awesome
81
-	 *
82
-	 * @var string
83
-	 */
84
-	protected $iconSet = 'fa';
85
-
86
-	/**
87
-	 * The default prefix icon names
88
-	 * Using Font Awesome 5, this can be 'fa' or 'fas' for solid, 'far' for regular
89
-	 *
90
-	 * @var string
91
-	 */
92
-	protected $iconPrefix = 'fa';
93
-
94
-	/**
95
-	 * Create a new TwitterBootstrap instance
96
-	 *
97
-	 * @param \Illuminate\Container\Container $app
98
-	 */
99
-	public function __construct(Container $app)
100
-	{
101
-		$this->app = $app;
102
-		$this->setFrameworkDefaults();
103
-	}
104
-
105
-	////////////////////////////////////////////////////////////////////
106
-	/////////////////////////// FILTER ARRAYS //////////////////////////
107
-	////////////////////////////////////////////////////////////////////
108
-
109
-	/**
110
-	 * Filter buttons classes
111
-	 *
112
-	 * @param  array $classes An array of classes
113
-	 *
114
-	 * @return string[] A filtered array
115
-	 */
116
-	public function filterButtonClasses($classes)
117
-	{
118
-		// Filter classes
119
-		// $classes = array_intersect($classes, $this->buttons);
120
-
121
-		// Prepend button type
122
-		$classes   = $this->prependWith($classes, 'btn-');
123
-		$classes[] = 'btn';
124
-
125
-		return $classes;
126
-	}
127
-
128
-	/**
129
-	 * Filter field classes
130
-	 *
131
-	 * @param  array $classes An array of classes
132
-	 *
133
-	 * @return array A filtered array
134
-	 */
135
-	public function filterFieldClasses($classes)
136
-	{
137
-		// Filter classes
138
-		$classes = array_intersect($classes, $this->fields);
139
-
140
-		// Prepend field type
141
-		$classes = array_map(function ($class) {
142
-			return Str::startsWith($class, 'col') ? $class : 'input-'.$class;
143
-		}, $classes);
144
-
145
-		return $classes;
146
-	}
147
-
148
-	////////////////////////////////////////////////////////////////////
149
-	///////////////////// EXPOSE FRAMEWORK SPECIFICS ///////////////////
150
-	////////////////////////////////////////////////////////////////////
151
-
152
-	/**
153
-	 * Framework error state
154
-	 *
155
-	 * @return string
156
-	 */
157
-	public function errorState()
158
-	{
159
-		return 'is-invalid';
160
-	}
161
-
162
-	/**
163
-	 * Returns corresponding inline class of a field
164
-	 *
165
-	 * @param Field $field
166
-	 *
167
-	 * @return string
168
-	 */
169
-	public function getInlineLabelClass($field)
170
-	{
171
-		$inlineClass = parent::getInlineLabelClass($field);
172
-		if ($field->isOfType('checkbox', 'checkboxes', 'radio', 'radios')) {
173
-			$inlineClass = 'form-check-label';
174
-		}
175
-
176
-		return $inlineClass;
177
-	}
178
-
179
-	/**
180
-	 * Set the fields width from a label width
181
-	 *
182
-	 * @param array $labelWidths
183
-	 */
184
-	protected function setFieldWidths($labelWidths)
185
-	{
186
-		$labelWidthClass = $fieldWidthClass = $fieldOffsetClass = '';
187
-
188
-		$viewports = $this->getFrameworkOption('viewports');
189
-		foreach ($labelWidths as $viewport => $columns) {
190
-			if ($viewport) {
191
-				$labelWidthClass .= " col-$viewports[$viewport]-$columns";
192
-				$fieldWidthClass .= " col-$viewports[$viewport]-".(12 - $columns);
193
-				$fieldOffsetClass .= " offset-$viewports[$viewport]-$columns";
194
-			}
195
-		}
196
-
197
-		$this->labelWidth  = ltrim($labelWidthClass);
198
-		$this->fieldWidth  = ltrim($fieldWidthClass);
199
-		$this->fieldOffset = ltrim($fieldOffsetClass);
200
-	}
201
-
202
-	////////////////////////////////////////////////////////////////////
203
-	///////////////////////////// ADD CLASSES //////////////////////////
204
-	////////////////////////////////////////////////////////////////////
205
-
206
-	/**
207
-	 * Add classes to a field
208
-	 *
209
-	 * @param Field $field
210
-	 * @param array $classes The possible classes to add
211
-	 *
212
-	 * @return Field
213
-	 */
214
-	public function getFieldClasses(Field $field, $classes)
215
-	{
216
-		// Add inline class for checkables
217
-		if ($field->isCheckable()) {
218
-			// Adds correct checkbox input class when is a checkbox (or radio)
219
-			$field->addClass('form-check-input');
220
-			$classes[] = 'form-check';
221
-
222
-			if (in_array('inline', $classes)) {
223
-				$field->inline();
224
-			}
225
-		}
226
-
227
-		// Filter classes according to field type
228
-		if ($field->isButton()) {
229
-			$classes = $this->filterButtonClasses($classes);
230
-		} else {
231
-			$classes = $this->filterFieldClasses($classes);
232
-		}
233
-
234
-		// Add form-control class for text-type, textarea and file fields
235
-		// As text-type is open-ended we instead exclude those that shouldn't receive the class
236
-		if (!$field->isCheckable() && !$field->isButton() && !in_array($field->getType(), [
237
-					'plaintext',
238
-					'select',
239
-				]) && !in_array('form-control', $classes)
240
-		) {
241
-			$classes[] = 'form-control';
242
-		}
243
-
244
-		// Add form-select class for select fields
245
-		if ($field->getType() === 'select' && !in_array('form-select', $classes)) {
246
-			$classes[] = 'form-select';
247
-		}
248
-
249
-		if ($this->app['former']->getErrors($field->getName())) {
250
-			$classes[] = $this->errorState();
251
-		}
252
-
253
-		return $this->addClassesToField($field, $classes);
254
-	}
255
-
256
-	/**
257
-	 * Add group classes
258
-	 *
259
-	 * @return string A list of group classes
260
-	 */
261
-	public function getGroupClasses()
262
-	{
263
-		if ($this->app['former.form']->isOfType('horizontal')) {
264
-			return 'mb-3 row';
265
-		} else {
266
-			return 'mb-3';
267
-		}
268
-	}
269
-
270
-	/**
271
-	 * Add label classes
272
-	 *
273
-	 * @return string[] An array of attributes with the label class
274
-	 */
275
-	public function getLabelClasses()
276
-	{
277
-		if ($this->app['former.form']->isOfType('horizontal')) {
278
-			return array('col-form-label', $this->labelWidth);
279
-		} elseif ($this->app['former.form']->isOfType('inline')) {
280
-			return array('visually-hidden');
281
-		} else {
282
-			return array('form-label');
283
-		}
284
-	}
285
-
286
-	/**
287
-	 * Add uneditable field classes
288
-	 *
289
-	 * @return string An array of attributes with the uneditable class
290
-	 */
291
-	public function getUneditableClasses()
292
-	{
293
-		return '';
294
-	}
295
-
296
-	/**
297
-	 * Add plain text field classes
298
-	 *
299
-	 * @return string An array of attributes with the plain text class
300
-	 */
301
-	public function getPlainTextClasses()
302
-	{
303
-		return 'form-control-plaintext';
304
-	}
305
-
306
-	/**
307
-	 * Add form class
308
-	 *
309
-	 * @param  string $type The type of form to add
310
-	 *
311
-	 * @return string|null
312
-	 */
313
-	public function getFormClasses($type)
314
-	{
315
-		return $type ? 'form-'.$type : null;
316
-	}
317
-
318
-	/**
319
-	 * Add actions block class
320
-	 *
321
-	 * @return string|null
322
-	 */
323
-	public function getActionClasses()
324
-	{
325
-		if ($this->app['former.form']->isOfType('horizontal') || $this->app['former.form']->isOfType('inline')) {
326
-			return 'mb-3 row';
327
-		}
328
-
329
-		return null;
330
-	}
331
-
332
-	/**
333
-	 * Add floating label class
334
-	 *
335
-	 * @return string Get the floating label class
336
-	 */
337
-	public function getFloatingLabelClass()
338
-	{
339
-		return 'form-floating';
340
-	}
341
-
342
-	////////////////////////////////////////////////////////////////////
343
-	//////////////////////////// RENDER BLOCKS /////////////////////////
344
-	////////////////////////////////////////////////////////////////////
345
-
346
-	/**
347
-	 * Render an help text
348
-	 *
349
-	 * @param string $text
350
-	 * @param array  $attributes
351
-	 *
352
-	 * @return Element
353
-	 */
354
-	public function createHelp($text, $attributes = array())
355
-	{
356
-		return Element::create('span', $text, $attributes)->addClass('form-text');
357
-	}
358
-
359
-	/**
360
-	 * Render an validation error text
361
-	 *
362
-	 * @param string $text
363
-	 * @param array  $attributes
364
-	 *
365
-	 * @return string
366
-	 */
367
-	public function createValidationError($text, $attributes = array())
368
-	{
369
-		return Element::create('div', $text, $attributes)->addClass('invalid-feedback');
370
-	}
371
-
372
-	/**
373
-	 * Render an help text
374
-	 *
375
-	 * @param string $text
376
-	 * @param array  $attributes
377
-	 *
378
-	 * @return Element
379
-	 */
380
-	public function createBlockHelp($text, $attributes = array())
381
-	{
382
-		return Element::create('div', $text, $attributes)->addClass('form-text');
383
-	}
384
-
385
-	/**
386
-	 * Render a disabled field
387
-	 *
388
-	 * @param Field $field
389
-	 *
390
-	 * @return Element
391
-	 */
392
-	public function createDisabledField(Field $field)
393
-	{
394
-		return Element::create('span', $field->getValue(), $field->getAttributes());
395
-	}
396
-
397
-	/**
398
-	 * Render a plain text field
399
-	 *
400
-	 * @param Field $field
401
-	 *
402
-	 * @return Element
403
-	 */
404
-	public function createPlainTextField(Field $field)
405
-	{
406
-		$label = $field->getLabel();
407
-		if ($label) {
408
-			$label->for('');
409
-		}
410
-
411
-		return Element::create('div', $field->getValue(), $field->getAttributes());
412
-	}
413
-
414
-	////////////////////////////////////////////////////////////////////
415
-	//////////////////////////// WRAP BLOCKS ///////////////////////////
416
-	////////////////////////////////////////////////////////////////////
417
-
418
-	/**
419
-	 * Wrap an item to be prepended or appended to the current field
420
-	 *
421
-	 * @return Element A wrapped item
422
-	 */
423
-	public function placeAround($item, $place = null)
424
-	{
425
-		// Render object
426
-		if (is_object($item) and method_exists($item, '__toString')) {
427
-			$item = $item->__toString();
428
-		}
429
-
430
-		$items = (array) $item;
431
-		$element = '';
432
-		foreach ($items as $item) {
433
-			$hasButtonTag = strpos(ltrim($item), '<button') === 0;
434
-
435
-			// Get class to use
436
-			$class = $hasButtonTag ? '' : 'input-group-text';
437
-
438
-			$element .= $hasButtonTag ? $item : Element::create('span', $item)->addClass($class);
439
-		}
440
-
441
-		return $element;
442
-	}
443
-
444
-	/**
445
-	 * Wrap a field with prepended and appended items
446
-	 *
447
-	 * @param  Field $field
448
-	 * @param  array $prepend
449
-	 * @param  array $append
450
-	 *
451
-	 * @return string A field concatented with prepended and/or appended items
452
-	 */
453
-	public function prependAppend($field, $prepend, $append)
454
-	{
455
-		$return = '<div class="input-group">';
456
-		$return .= join(null, $prepend);
457
-		$return .= $field->render();
458
-		$return .= join(null, $append);
459
-		$return .= '</div>';
460
-
461
-		return $return;
462
-	}
463
-
464
-	/**
465
-	 * Wrap a field with potential additional tags
466
-	 *
467
-	 * @param  Field $field
468
-	 *
469
-	 * @return Element A wrapped field
470
-	 */
471
-	public function wrapField($field)
472
-	{
473
-		if ($this->app['former.form']->isOfType('horizontal')) {
474
-			return Element::create('div', $field)->addClass($this->fieldWidth);
475
-		}
476
-
477
-		return $field;
478
-	}
479
-
480
-	/**
481
-	 * Wrap actions block with potential additional tags
482
-	 *
483
-	 * @param  Actions $actions
484
-	 *
485
-	 * @return string A wrapped actions block
486
-	 */
487
-	public function wrapActions($actions)
488
-	{
489
-		// For horizontal forms, we wrap the actions in a div
490
-		if ($this->app['former.form']->isOfType('horizontal')) {
491
-			return Element::create('div', $actions)->addClass(array($this->fieldOffset, $this->fieldWidth));
492
-		}
493
-
494
-		return $actions;
495
-	}
16
+    /**
17
+     * Form types that trigger special styling for this Framework
18
+     *
19
+     * @var array
20
+     */
21
+    protected $availableTypes = array('horizontal', 'vertical', 'inline');
22
+
23
+    /**
24
+     * The button types available
25
+     *
26
+     * @var array
27
+     */
28
+    private $buttons = array(
29
+        'lg',
30
+        'sm',
31
+        'xs',
32
+        'block',
33
+        'link',
34
+        'primary',
35
+        'secondary',
36
+        'warning',
37
+        'danger',
38
+        'success',
39
+        'info',
40
+        'light',
41
+        'dark',
42
+    );
43
+
44
+    /**
45
+     * The field sizes available
46
+     *
47
+     * @var array
48
+     */
49
+    private $fields = array(
50
+        'lg',
51
+        'sm',
52
+        // 'col-xs-1', 'col-xs-2', 'col-xs-3', 'col-xs-4', 'col-xs-5', 'col-xs-6',
53
+        // 'col-xs-7', 'col-xs-8', 'col-xs-9', 'col-xs-10', 'col-xs-11', 'col-xs-12',
54
+        // 'col-sm-1', 'col-sm-2', 'col-sm-3', 'col-sm-4', 'col-sm-5', 'col-sm-6',
55
+        // 'col-sm-7', 'col-sm-8', 'col-sm-9', 'col-sm-10', 'col-sm-11', 'col-sm-12',
56
+        // 'col-md-1', 'col-md-2', 'col-md-3', 'col-md-4', 'col-md-5', 'col-md-6',
57
+        // 'col-md-7', 'col-md-8', 'col-md-9', 'col-md-10', 'col-md-11', 'col-md-12',
58
+        // 'col-lg-1', 'col-lg-2', 'col-lg-3', 'col-lg-4', 'col-lg-5', 'col-lg-6',
59
+        // 'col-lg-7', 'col-lg-8', 'col-lg-9', 'col-lg-10', 'col-lg-11', 'col-lg-12',
60
+    );
61
+
62
+    /**
63
+     * The field states available
64
+     *
65
+     * @var array
66
+     */
67
+    protected $states = array(
68
+        'is-invalid',
69
+    );
70
+
71
+    /**
72
+     * The default HTML tag used for icons
73
+     *
74
+     * @var string
75
+     */
76
+    protected $iconTag = 'i';
77
+
78
+    /**
79
+     * The default set for icon fonts
80
+     * By default Bootstrap 4 offers no fonts, but we'll add Font Awesome
81
+     *
82
+     * @var string
83
+     */
84
+    protected $iconSet = 'fa';
85
+
86
+    /**
87
+     * The default prefix icon names
88
+     * Using Font Awesome 5, this can be 'fa' or 'fas' for solid, 'far' for regular
89
+     *
90
+     * @var string
91
+     */
92
+    protected $iconPrefix = 'fa';
93
+
94
+    /**
95
+     * Create a new TwitterBootstrap instance
96
+     *
97
+     * @param \Illuminate\Container\Container $app
98
+     */
99
+    public function __construct(Container $app)
100
+    {
101
+        $this->app = $app;
102
+        $this->setFrameworkDefaults();
103
+    }
104
+
105
+    ////////////////////////////////////////////////////////////////////
106
+    /////////////////////////// FILTER ARRAYS //////////////////////////
107
+    ////////////////////////////////////////////////////////////////////
108
+
109
+    /**
110
+     * Filter buttons classes
111
+     *
112
+     * @param  array $classes An array of classes
113
+     *
114
+     * @return string[] A filtered array
115
+     */
116
+    public function filterButtonClasses($classes)
117
+    {
118
+        // Filter classes
119
+        // $classes = array_intersect($classes, $this->buttons);
120
+
121
+        // Prepend button type
122
+        $classes   = $this->prependWith($classes, 'btn-');
123
+        $classes[] = 'btn';
124
+
125
+        return $classes;
126
+    }
127
+
128
+    /**
129
+     * Filter field classes
130
+     *
131
+     * @param  array $classes An array of classes
132
+     *
133
+     * @return array A filtered array
134
+     */
135
+    public function filterFieldClasses($classes)
136
+    {
137
+        // Filter classes
138
+        $classes = array_intersect($classes, $this->fields);
139
+
140
+        // Prepend field type
141
+        $classes = array_map(function ($class) {
142
+            return Str::startsWith($class, 'col') ? $class : 'input-'.$class;
143
+        }, $classes);
144
+
145
+        return $classes;
146
+    }
147
+
148
+    ////////////////////////////////////////////////////////////////////
149
+    ///////////////////// EXPOSE FRAMEWORK SPECIFICS ///////////////////
150
+    ////////////////////////////////////////////////////////////////////
151
+
152
+    /**
153
+     * Framework error state
154
+     *
155
+     * @return string
156
+     */
157
+    public function errorState()
158
+    {
159
+        return 'is-invalid';
160
+    }
161
+
162
+    /**
163
+     * Returns corresponding inline class of a field
164
+     *
165
+     * @param Field $field
166
+     *
167
+     * @return string
168
+     */
169
+    public function getInlineLabelClass($field)
170
+    {
171
+        $inlineClass = parent::getInlineLabelClass($field);
172
+        if ($field->isOfType('checkbox', 'checkboxes', 'radio', 'radios')) {
173
+            $inlineClass = 'form-check-label';
174
+        }
175
+
176
+        return $inlineClass;
177
+    }
178
+
179
+    /**
180
+     * Set the fields width from a label width
181
+     *
182
+     * @param array $labelWidths
183
+     */
184
+    protected function setFieldWidths($labelWidths)
185
+    {
186
+        $labelWidthClass = $fieldWidthClass = $fieldOffsetClass = '';
187
+
188
+        $viewports = $this->getFrameworkOption('viewports');
189
+        foreach ($labelWidths as $viewport => $columns) {
190
+            if ($viewport) {
191
+                $labelWidthClass .= " col-$viewports[$viewport]-$columns";
192
+                $fieldWidthClass .= " col-$viewports[$viewport]-".(12 - $columns);
193
+                $fieldOffsetClass .= " offset-$viewports[$viewport]-$columns";
194
+            }
195
+        }
196
+
197
+        $this->labelWidth  = ltrim($labelWidthClass);
198
+        $this->fieldWidth  = ltrim($fieldWidthClass);
199
+        $this->fieldOffset = ltrim($fieldOffsetClass);
200
+    }
201
+
202
+    ////////////////////////////////////////////////////////////////////
203
+    ///////////////////////////// ADD CLASSES //////////////////////////
204
+    ////////////////////////////////////////////////////////////////////
205
+
206
+    /**
207
+     * Add classes to a field
208
+     *
209
+     * @param Field $field
210
+     * @param array $classes The possible classes to add
211
+     *
212
+     * @return Field
213
+     */
214
+    public function getFieldClasses(Field $field, $classes)
215
+    {
216
+        // Add inline class for checkables
217
+        if ($field->isCheckable()) {
218
+            // Adds correct checkbox input class when is a checkbox (or radio)
219
+            $field->addClass('form-check-input');
220
+            $classes[] = 'form-check';
221
+
222
+            if (in_array('inline', $classes)) {
223
+                $field->inline();
224
+            }
225
+        }
226
+
227
+        // Filter classes according to field type
228
+        if ($field->isButton()) {
229
+            $classes = $this->filterButtonClasses($classes);
230
+        } else {
231
+            $classes = $this->filterFieldClasses($classes);
232
+        }
233
+
234
+        // Add form-control class for text-type, textarea and file fields
235
+        // As text-type is open-ended we instead exclude those that shouldn't receive the class
236
+        if (!$field->isCheckable() && !$field->isButton() && !in_array($field->getType(), [
237
+                    'plaintext',
238
+                    'select',
239
+                ]) && !in_array('form-control', $classes)
240
+        ) {
241
+            $classes[] = 'form-control';
242
+        }
243
+
244
+        // Add form-select class for select fields
245
+        if ($field->getType() === 'select' && !in_array('form-select', $classes)) {
246
+            $classes[] = 'form-select';
247
+        }
248
+
249
+        if ($this->app['former']->getErrors($field->getName())) {
250
+            $classes[] = $this->errorState();
251
+        }
252
+
253
+        return $this->addClassesToField($field, $classes);
254
+    }
255
+
256
+    /**
257
+     * Add group classes
258
+     *
259
+     * @return string A list of group classes
260
+     */
261
+    public function getGroupClasses()
262
+    {
263
+        if ($this->app['former.form']->isOfType('horizontal')) {
264
+            return 'mb-3 row';
265
+        } else {
266
+            return 'mb-3';
267
+        }
268
+    }
269
+
270
+    /**
271
+     * Add label classes
272
+     *
273
+     * @return string[] An array of attributes with the label class
274
+     */
275
+    public function getLabelClasses()
276
+    {
277
+        if ($this->app['former.form']->isOfType('horizontal')) {
278
+            return array('col-form-label', $this->labelWidth);
279
+        } elseif ($this->app['former.form']->isOfType('inline')) {
280
+            return array('visually-hidden');
281
+        } else {
282
+            return array('form-label');
283
+        }
284
+    }
285
+
286
+    /**
287
+     * Add uneditable field classes
288
+     *
289
+     * @return string An array of attributes with the uneditable class
290
+     */
291
+    public function getUneditableClasses()
292
+    {
293
+        return '';
294
+    }
295
+
296
+    /**
297
+     * Add plain text field classes
298
+     *
299
+     * @return string An array of attributes with the plain text class
300
+     */
301
+    public function getPlainTextClasses()
302
+    {
303
+        return 'form-control-plaintext';
304
+    }
305
+
306
+    /**
307
+     * Add form class
308
+     *
309
+     * @param  string $type The type of form to add
310
+     *
311
+     * @return string|null
312
+     */
313
+    public function getFormClasses($type)
314
+    {
315
+        return $type ? 'form-'.$type : null;
316
+    }
317
+
318
+    /**
319
+     * Add actions block class
320
+     *
321
+     * @return string|null
322
+     */
323
+    public function getActionClasses()
324
+    {
325
+        if ($this->app['former.form']->isOfType('horizontal') || $this->app['former.form']->isOfType('inline')) {
326
+            return 'mb-3 row';
327
+        }
328
+
329
+        return null;
330
+    }
331
+
332
+    /**
333
+     * Add floating label class
334
+     *
335
+     * @return string Get the floating label class
336
+     */
337
+    public function getFloatingLabelClass()
338
+    {
339
+        return 'form-floating';
340
+    }
341
+
342
+    ////////////////////////////////////////////////////////////////////
343
+    //////////////////////////// RENDER BLOCKS /////////////////////////
344
+    ////////////////////////////////////////////////////////////////////
345
+
346
+    /**
347
+     * Render an help text
348
+     *
349
+     * @param string $text
350
+     * @param array  $attributes
351
+     *
352
+     * @return Element
353
+     */
354
+    public function createHelp($text, $attributes = array())
355
+    {
356
+        return Element::create('span', $text, $attributes)->addClass('form-text');
357
+    }
358
+
359
+    /**
360
+     * Render an validation error text
361
+     *
362
+     * @param string $text
363
+     * @param array  $attributes
364
+     *
365
+     * @return string
366
+     */
367
+    public function createValidationError($text, $attributes = array())
368
+    {
369
+        return Element::create('div', $text, $attributes)->addClass('invalid-feedback');
370
+    }
371
+
372
+    /**
373
+     * Render an help text
374
+     *
375
+     * @param string $text
376
+     * @param array  $attributes
377
+     *
378
+     * @return Element
379
+     */
380
+    public function createBlockHelp($text, $attributes = array())
381
+    {
382
+        return Element::create('div', $text, $attributes)->addClass('form-text');
383
+    }
384
+
385
+    /**
386
+     * Render a disabled field
387
+     *
388
+     * @param Field $field
389
+     *
390
+     * @return Element
391
+     */
392
+    public function createDisabledField(Field $field)
393
+    {
394
+        return Element::create('span', $field->getValue(), $field->getAttributes());
395
+    }
396
+
397
+    /**
398
+     * Render a plain text field
399
+     *
400
+     * @param Field $field
401
+     *
402
+     * @return Element
403
+     */
404
+    public function createPlainTextField(Field $field)
405
+    {
406
+        $label = $field->getLabel();
407
+        if ($label) {
408
+            $label->for('');
409
+        }
410
+
411
+        return Element::create('div', $field->getValue(), $field->getAttributes());
412
+    }
413
+
414
+    ////////////////////////////////////////////////////////////////////
415
+    //////////////////////////// WRAP BLOCKS ///////////////////////////
416
+    ////////////////////////////////////////////////////////////////////
417
+
418
+    /**
419
+     * Wrap an item to be prepended or appended to the current field
420
+     *
421
+     * @return Element A wrapped item
422
+     */
423
+    public function placeAround($item, $place = null)
424
+    {
425
+        // Render object
426
+        if (is_object($item) and method_exists($item, '__toString')) {
427
+            $item = $item->__toString();
428
+        }
429
+
430
+        $items = (array) $item;
431
+        $element = '';
432
+        foreach ($items as $item) {
433
+            $hasButtonTag = strpos(ltrim($item), '<button') === 0;
434
+
435
+            // Get class to use
436
+            $class = $hasButtonTag ? '' : 'input-group-text';
437
+
438
+            $element .= $hasButtonTag ? $item : Element::create('span', $item)->addClass($class);
439
+        }
440
+
441
+        return $element;
442
+    }
443
+
444
+    /**
445
+     * Wrap a field with prepended and appended items
446
+     *
447
+     * @param  Field $field
448
+     * @param  array $prepend
449
+     * @param  array $append
450
+     *
451
+     * @return string A field concatented with prepended and/or appended items
452
+     */
453
+    public function prependAppend($field, $prepend, $append)
454
+    {
455
+        $return = '<div class="input-group">';
456
+        $return .= join(null, $prepend);
457
+        $return .= $field->render();
458
+        $return .= join(null, $append);
459
+        $return .= '</div>';
460
+
461
+        return $return;
462
+    }
463
+
464
+    /**
465
+     * Wrap a field with potential additional tags
466
+     *
467
+     * @param  Field $field
468
+     *
469
+     * @return Element A wrapped field
470
+     */
471
+    public function wrapField($field)
472
+    {
473
+        if ($this->app['former.form']->isOfType('horizontal')) {
474
+            return Element::create('div', $field)->addClass($this->fieldWidth);
475
+        }
476
+
477
+        return $field;
478
+    }
479
+
480
+    /**
481
+     * Wrap actions block with potential additional tags
482
+     *
483
+     * @param  Actions $actions
484
+     *
485
+     * @return string A wrapped actions block
486
+     */
487
+    public function wrapActions($actions)
488
+    {
489
+        // For horizontal forms, we wrap the actions in a div
490
+        if ($this->app['former.form']->isOfType('horizontal')) {
491
+            return Element::create('div', $actions)->addClass(array($this->fieldOffset, $this->fieldWidth));
492
+        }
493
+
494
+        return $actions;
495
+    }
496 496
 }
Please login to merge, or discard this patch.
src/config/former.php 1 patch
Indentation   +235 added lines, -235 removed lines patch added patch discarded remove patch
@@ -1,240 +1,240 @@
 block discarded – undo
1 1
 <?php return array(
2 2
 
3
-	// Markup
4
-	////////////////////////////////////////////////////////////////////
5
-
6
-	// Whether labels should be automatically computed from name
7
-	'automatic_label'         => true,
8
-
9
-	// The default form type
10
-	'default_form_type'       => 'horizontal',
11
-
12
-	// Whether Former should escape HTML tags of 'plaintext' fields value
13
-	// Enabled by default
14
-	//
15
-	// Instead of disabled this option, you should use the 'HtmlString' class:
16
-	//  Former::plaintext('text')
17
-	//      ->forceValue(
18
-	//          new Illuminate\Support\HtmlString('<b>your HTML data</b>')
19
-	//      )
20
-	'escape_plaintext_value'  => true,
21
-
22
-	// Validation
23
-	////////////////////////////////////////////////////////////////////
24
-
25
-	// Whether Former should fetch errors from Session
26
-	'fetch_errors'            => true,
27
-
28
-	// Whether Former should try to apply Validator rules as attributes
29
-	'live_validation'         => true,
30
-
31
-	// Whether Former should automatically fetch error messages and
32
-	// display them next to the matching fields
33
-	'error_messages'          => true,
34
-
35
-	// Checkables
36
-	////////////////////////////////////////////////////////////////////
37
-
38
-	// Whether checkboxes should always be present in the POST data,
39
-	// no matter if you checked them or not
40
-	'push_checkboxes'         => false,
41
-
42
-	// The value a checkbox will have in the POST array if unchecked
43
-	'unchecked_value'         => 0,
44
-
45
-	// Required fields
46
-	////////////////////////////////////////////////////////////////////
47
-
48
-	// The class to be added to required fields
49
-	'required_class'          => 'required',
50
-
51
-	// A facultative text to append to the labels of required fields
52
-	'required_text'           => '<sup>*</sup>',
53
-
54
-	// Translations
55
-	////////////////////////////////////////////////////////////////////
56
-
57
-	// Where Former should look for translations
58
-	'translate_from'          => 'validation.attributes',
59
-
60
-	// Whether text that comes out of the translated
61
-	// should be capitalized (ex: email => Email) automatically
62
-	'capitalize_translations' => true,
63
-
64
-	// An array of attributes to automatically translate
65
-	'translatable'            => array(
66
-		'help',
67
-		'inlineHelp',
68
-		'blockHelp',
69
-		'placeholder',
70
-		'data_placeholder',
71
-		'label',
72
-	),
73
-
74
-	// Framework
75
-	////////////////////////////////////////////////////////////////////
76
-
77
-	// The framework to be used by Former
78
-	'framework'               => 'TwitterBootstrap3',
79
-
80
-	'TwitterBootstrap5'       => array(
81
-
82
-		// Map Former-supported viewports to Bootstrap 5 equivalents
83
-		'viewports'   => array(
84
-			'large'  => 'lg',
85
-			'medium' => 'md',
86
-			'small'  => 'sm',
87
-			'mini'   => 'xs',
88
-		),
89
-		// Width of labels for horizontal forms expressed as viewport => grid columns
90
-		'labelWidths' => array(
91
-			'large' => 2,
92
-			'small' => 4,
93
-		),
94
-		// HTML markup and classes used by Bootstrap 5 for icons
95
-		'icon'        => array(
96
-			'tag'    => 'i',
97
-			'set'    => 'fa',
98
-			'prefix' => 'fa',
99
-		),
100
-
101
-	),
102
-
103
-	'TwitterBootstrap4'       => array(
104
-
105
-		// Map Former-supported viewports to Bootstrap 4 equivalents
106
-		'viewports'   => array(
107
-			'large'  => 'lg',
108
-			'medium' => 'md',
109
-			'small'  => 'sm',
110
-			'mini'   => 'xs',
111
-		),
112
-		// Width of labels for horizontal forms expressed as viewport => grid columns
113
-		'labelWidths' => array(
114
-			'large' => 2,
115
-			'small' => 4,
116
-		),
117
-		// HTML markup and classes used by Bootstrap 4 for icons
118
-		'icon'        => array(
119
-			'tag'    => 'i',
120
-			'set'    => 'fa',
121
-			'prefix' => 'fa',
122
-		),
123
-
124
-	),
125
-
126
-	'TwitterBootstrap3'       => array(
127
-
128
-		// Map Former-supported viewports to Bootstrap 3 equivalents
129
-		'viewports'   => array(
130
-			'large'  => 'lg',
131
-			'medium' => 'md',
132
-			'small'  => 'sm',
133
-			'mini'   => 'xs',
134
-		),
135
-		// Width of labels for horizontal forms expressed as viewport => grid columns
136
-		'labelWidths' => array(
137
-			'large' => 2,
138
-			'small' => 4,
139
-		),
140
-		// HTML markup and classes used by Bootstrap 3 for icons
141
-		'icon'        => array(
142
-			'tag'    => 'span',
143
-			'set'    => 'glyphicon',
144
-			'prefix' => 'glyphicon',
145
-		),
146
-
147
-	),
148
-
149
-	'Nude'                    => array(  // No-framework markup
150
-		'icon' => array(
151
-			'tag'    => 'i',
152
-			'set'    => null,
153
-			'prefix' => 'icon',
154
-		),
155
-	),
156
-
157
-	'TwitterBootstrap'        => array( // Twitter Bootstrap version 2
158
-		'icon' => array(
159
-			'tag'    => 'i',
160
-			'set'    => null,
161
-			'prefix' => 'icon',
162
-		),
163
-	),
164
-
165
-	'ZurbFoundation5'         => array(
166
-		// Map Former-supported viewports to Foundation 5 equivalents
167
-		'viewports'           => array(
168
-			'large'  => 'large',
169
-			'medium' => null,
170
-			'small'  => 'small',
171
-			'mini'   => null,
172
-		),
173
-		// Width of labels for horizontal forms expressed as viewport => grid columns
174
-		'labelWidths'         => array(
175
-			'small' => 3,
176
-		),
177
-		// Classes to be applied to wrapped labels in horizontal forms
178
-		'wrappedLabelClasses' => array('right', 'inline'),
179
-		// HTML markup and classes used by Foundation 5 for icons
180
-		'icon'                => array(
181
-			'tag'    => 'i',
182
-			'set'    => null,
183
-			'prefix' => 'fi',
184
-		),
185
-		// CSS for inline validation errors
186
-		'error_classes'       => array('class' => 'error'),
187
-	),
188
-
189
-	'ZurbFoundation4'         => array(
190
-		// Foundation 4 also has an experimental "medium" breakpoint
191
-		// explained at http://foundation.zurb.com/docs/components/grid.html
192
-		'viewports'           => array(
193
-			'large'  => 'large',
194
-			'medium' => null,
195
-			'small'  => 'small',
196
-			'mini'   => null,
197
-		),
198
-		// Width of labels for horizontal forms expressed as viewport => grid columns
199
-		'labelWidths'         => array(
200
-			'small' => 3,
201
-		),
202
-		// Classes to be applied to wrapped labels in horizontal forms
203
-		'wrappedLabelClasses' => array('right', 'inline'),
204
-		// HTML markup and classes used by Foundation 4 for icons
205
-		'icon'                => array(
206
-			'tag'    => 'i',
207
-			'set'    => 'general',
208
-			'prefix' => 'foundicon',
209
-		),
210
-		// CSS for inline validation errors
211
-		'error_classes'       => array('class' => 'alert-box radius warning'),
212
-	),
213
-
214
-	'ZurbFoundation'          => array( // Foundation 3
215
-		'viewports'           => array(
216
-			'large'  => '',
217
-			'medium' => null,
218
-			'small'  => 'mobile-',
219
-			'mini'   => null,
220
-		),
221
-		// Width of labels for horizontal forms expressed as viewport => grid columns
222
-		'labelWidths'         => array(
223
-			'large' => 2,
224
-			'small' => 4,
225
-		),
226
-		// Classes to be applied to wrapped labels in horizontal forms
227
-		'wrappedLabelClasses' => array('right', 'inline'),
228
-		// HTML markup and classes used by Foundation 3 for icons
229
-		'icon'                => array(
230
-			'tag'    => 'i',
231
-			'set'    => null,
232
-			'prefix' => 'fi',
233
-		),
234
-		// CSS for inline validation errors
235
-		// should work for Zurb 2 and 3
236
-		'error_classes'       => array('class' => 'alert-box alert error'),
237
-	),
3
+    // Markup
4
+    ////////////////////////////////////////////////////////////////////
5
+
6
+    // Whether labels should be automatically computed from name
7
+    'automatic_label'         => true,
8
+
9
+    // The default form type
10
+    'default_form_type'       => 'horizontal',
11
+
12
+    // Whether Former should escape HTML tags of 'plaintext' fields value
13
+    // Enabled by default
14
+    //
15
+    // Instead of disabled this option, you should use the 'HtmlString' class:
16
+    //  Former::plaintext('text')
17
+    //      ->forceValue(
18
+    //          new Illuminate\Support\HtmlString('<b>your HTML data</b>')
19
+    //      )
20
+    'escape_plaintext_value'  => true,
21
+
22
+    // Validation
23
+    ////////////////////////////////////////////////////////////////////
24
+
25
+    // Whether Former should fetch errors from Session
26
+    'fetch_errors'            => true,
27
+
28
+    // Whether Former should try to apply Validator rules as attributes
29
+    'live_validation'         => true,
30
+
31
+    // Whether Former should automatically fetch error messages and
32
+    // display them next to the matching fields
33
+    'error_messages'          => true,
34
+
35
+    // Checkables
36
+    ////////////////////////////////////////////////////////////////////
37
+
38
+    // Whether checkboxes should always be present in the POST data,
39
+    // no matter if you checked them or not
40
+    'push_checkboxes'         => false,
41
+
42
+    // The value a checkbox will have in the POST array if unchecked
43
+    'unchecked_value'         => 0,
44
+
45
+    // Required fields
46
+    ////////////////////////////////////////////////////////////////////
47
+
48
+    // The class to be added to required fields
49
+    'required_class'          => 'required',
50
+
51
+    // A facultative text to append to the labels of required fields
52
+    'required_text'           => '<sup>*</sup>',
53
+
54
+    // Translations
55
+    ////////////////////////////////////////////////////////////////////
56
+
57
+    // Where Former should look for translations
58
+    'translate_from'          => 'validation.attributes',
59
+
60
+    // Whether text that comes out of the translated
61
+    // should be capitalized (ex: email => Email) automatically
62
+    'capitalize_translations' => true,
63
+
64
+    // An array of attributes to automatically translate
65
+    'translatable'            => array(
66
+        'help',
67
+        'inlineHelp',
68
+        'blockHelp',
69
+        'placeholder',
70
+        'data_placeholder',
71
+        'label',
72
+    ),
73
+
74
+    // Framework
75
+    ////////////////////////////////////////////////////////////////////
76
+
77
+    // The framework to be used by Former
78
+    'framework'               => 'TwitterBootstrap3',
79
+
80
+    'TwitterBootstrap5'       => array(
81
+
82
+        // Map Former-supported viewports to Bootstrap 5 equivalents
83
+        'viewports'   => array(
84
+            'large'  => 'lg',
85
+            'medium' => 'md',
86
+            'small'  => 'sm',
87
+            'mini'   => 'xs',
88
+        ),
89
+        // Width of labels for horizontal forms expressed as viewport => grid columns
90
+        'labelWidths' => array(
91
+            'large' => 2,
92
+            'small' => 4,
93
+        ),
94
+        // HTML markup and classes used by Bootstrap 5 for icons
95
+        'icon'        => array(
96
+            'tag'    => 'i',
97
+            'set'    => 'fa',
98
+            'prefix' => 'fa',
99
+        ),
100
+
101
+    ),
102
+
103
+    'TwitterBootstrap4'       => array(
104
+
105
+        // Map Former-supported viewports to Bootstrap 4 equivalents
106
+        'viewports'   => array(
107
+            'large'  => 'lg',
108
+            'medium' => 'md',
109
+            'small'  => 'sm',
110
+            'mini'   => 'xs',
111
+        ),
112
+        // Width of labels for horizontal forms expressed as viewport => grid columns
113
+        'labelWidths' => array(
114
+            'large' => 2,
115
+            'small' => 4,
116
+        ),
117
+        // HTML markup and classes used by Bootstrap 4 for icons
118
+        'icon'        => array(
119
+            'tag'    => 'i',
120
+            'set'    => 'fa',
121
+            'prefix' => 'fa',
122
+        ),
123
+
124
+    ),
125
+
126
+    'TwitterBootstrap3'       => array(
127
+
128
+        // Map Former-supported viewports to Bootstrap 3 equivalents
129
+        'viewports'   => array(
130
+            'large'  => 'lg',
131
+            'medium' => 'md',
132
+            'small'  => 'sm',
133
+            'mini'   => 'xs',
134
+        ),
135
+        // Width of labels for horizontal forms expressed as viewport => grid columns
136
+        'labelWidths' => array(
137
+            'large' => 2,
138
+            'small' => 4,
139
+        ),
140
+        // HTML markup and classes used by Bootstrap 3 for icons
141
+        'icon'        => array(
142
+            'tag'    => 'span',
143
+            'set'    => 'glyphicon',
144
+            'prefix' => 'glyphicon',
145
+        ),
146
+
147
+    ),
148
+
149
+    'Nude'                    => array(  // No-framework markup
150
+        'icon' => array(
151
+            'tag'    => 'i',
152
+            'set'    => null,
153
+            'prefix' => 'icon',
154
+        ),
155
+    ),
156
+
157
+    'TwitterBootstrap'        => array( // Twitter Bootstrap version 2
158
+        'icon' => array(
159
+            'tag'    => 'i',
160
+            'set'    => null,
161
+            'prefix' => 'icon',
162
+        ),
163
+    ),
164
+
165
+    'ZurbFoundation5'         => array(
166
+        // Map Former-supported viewports to Foundation 5 equivalents
167
+        'viewports'           => array(
168
+            'large'  => 'large',
169
+            'medium' => null,
170
+            'small'  => 'small',
171
+            'mini'   => null,
172
+        ),
173
+        // Width of labels for horizontal forms expressed as viewport => grid columns
174
+        'labelWidths'         => array(
175
+            'small' => 3,
176
+        ),
177
+        // Classes to be applied to wrapped labels in horizontal forms
178
+        'wrappedLabelClasses' => array('right', 'inline'),
179
+        // HTML markup and classes used by Foundation 5 for icons
180
+        'icon'                => array(
181
+            'tag'    => 'i',
182
+            'set'    => null,
183
+            'prefix' => 'fi',
184
+        ),
185
+        // CSS for inline validation errors
186
+        'error_classes'       => array('class' => 'error'),
187
+    ),
188
+
189
+    'ZurbFoundation4'         => array(
190
+        // Foundation 4 also has an experimental "medium" breakpoint
191
+        // explained at http://foundation.zurb.com/docs/components/grid.html
192
+        'viewports'           => array(
193
+            'large'  => 'large',
194
+            'medium' => null,
195
+            'small'  => 'small',
196
+            'mini'   => null,
197
+        ),
198
+        // Width of labels for horizontal forms expressed as viewport => grid columns
199
+        'labelWidths'         => array(
200
+            'small' => 3,
201
+        ),
202
+        // Classes to be applied to wrapped labels in horizontal forms
203
+        'wrappedLabelClasses' => array('right', 'inline'),
204
+        // HTML markup and classes used by Foundation 4 for icons
205
+        'icon'                => array(
206
+            'tag'    => 'i',
207
+            'set'    => 'general',
208
+            'prefix' => 'foundicon',
209
+        ),
210
+        // CSS for inline validation errors
211
+        'error_classes'       => array('class' => 'alert-box radius warning'),
212
+    ),
213
+
214
+    'ZurbFoundation'          => array( // Foundation 3
215
+        'viewports'           => array(
216
+            'large'  => '',
217
+            'medium' => null,
218
+            'small'  => 'mobile-',
219
+            'mini'   => null,
220
+        ),
221
+        // Width of labels for horizontal forms expressed as viewport => grid columns
222
+        'labelWidths'         => array(
223
+            'large' => 2,
224
+            'small' => 4,
225
+        ),
226
+        // Classes to be applied to wrapped labels in horizontal forms
227
+        'wrappedLabelClasses' => array('right', 'inline'),
228
+        // HTML markup and classes used by Foundation 3 for icons
229
+        'icon'                => array(
230
+            'tag'    => 'i',
231
+            'set'    => null,
232
+            'prefix' => 'fi',
233
+        ),
234
+        // CSS for inline validation errors
235
+        // should work for Zurb 2 and 3
236
+        'error_classes'       => array('class' => 'alert-box alert error'),
237
+    ),
238 238
 
239 239
 
240 240
 );
Please login to merge, or discard this patch.
src/Former/Traits/Checkable.php 2 patches
Indentation   +570 added lines, -570 removed lines patch added patch discarded remove patch
@@ -14,574 +14,574 @@
 block discarded – undo
14 14
  */
15 15
 abstract class Checkable extends Field
16 16
 {
17
-	/**
18
-	 * Renders the checkables as inline
19
-	 *
20
-	 * @var boolean
21
-	 */
22
-	protected $inline = false;
23
-
24
-	/**
25
-	 * Add a text to a single element
26
-	 *
27
-	 * @var string
28
-	 */
29
-	protected $text = null;
30
-
31
-	/**
32
-	 * Renders the checkables as grouped
33
-	 *
34
-	 * @var boolean
35
-	 */
36
-	protected $grouped = false;
37
-
38
-	/**
39
-	 * The checkable items currently stored
40
-	 *
41
-	 * @var array
42
-	 */
43
-	protected $items = array();
44
-
45
-	/**
46
-	 * The type of checkable item
47
-	 *
48
-	 * @var string
49
-	 */
50
-	protected $checkable = null;
51
-
52
-	/**
53
-	 * An array of checked items
54
-	 *
55
-	 * @var array
56
-	 */
57
-	protected $checked = array();
58
-
59
-	/**
60
-	 * The checkable currently being focused on
61
-	 *
62
-	 * @var integer
63
-	 */
64
-	protected $focus = null;
65
-
66
-	/**
67
-	 * Whether this particular checkable is to be pushed
68
-	 *
69
-	 * @var boolean
70
-	 */
71
-	protected $isPushed = null;
72
-
73
-	////////////////////////////////////////////////////////////////////
74
-	//////////////////////////// CORE METHODS //////////////////////////
75
-	////////////////////////////////////////////////////////////////////
76
-
77
-	/**
78
-	 * Build a new checkable
79
-	 *
80
-	 * @param Container $app
81
-	 * @param string    $type
82
-	 * @param array     $name
83
-	 * @param           $label
84
-	 * @param           $value
85
-	 * @param           $attributes
86
-	 */
87
-	public function __construct(Container $app, $type, $name, $label, $value, $attributes)
88
-	{
89
-		// Unify auto and chained methods of grouping checkboxes
90
-		if (Str::endsWith($name, '[]')) {
91
-			$name = substr($name, 0, -2);
92
-			$this->grouped();
93
-		}
94
-		parent::__construct($app, $type, $name, $label, $value, $attributes);
95
-
96
-		if (is_array($this->value)) {
97
-			$this->items($this->value);
98
-		}
99
-	}
100
-
101
-	/**
102
-	 * Apply methods to focused checkable
103
-	 *
104
-	 * @param string $method
105
-	 * @param array  $parameters
106
-	 *
107
-	 * @return $this
108
-	 */
109
-	public function __call($method, $parameters)
110
-	{
111
-		$focused = $this->setOnFocused('attributes.'.$method, Arr::get($parameters, 0));
112
-		if ($focused) {
113
-			return $this;
114
-		}
115
-
116
-		return parent::__call($method, $parameters);
117
-	}
118
-
119
-	/**
120
-	 * Prints out the currently stored checkables
121
-	 */
122
-	public function render()
123
-	{
124
-		$html = null;
125
-
126
-		$this->setFieldClasses();
127
-
128
-		// Multiple items
129
-		if ($this->items) {
130
-			unset($this->app['former']->labels[array_search($this->name, $this->app['former']->labels)]);
131
-			foreach ($this->items as $key => $item) {
132
-				$value = $this->isCheckbox() && !$this->isGrouped() ? 1 : $key;
133
-				$html .= $this->createCheckable($item, $value);
134
-			}
135
-
136
-			return $html;
137
-		}
138
-
139
-		// Single item
140
-		return $this->createCheckable(array(
141
-			'name'  => $this->name,
142
-			'label' => $this->text,
143
-			'value' => $this->value,
144
-			'attributes' => $this->attributes,
145
-		));
146
-	}
147
-
148
-	////////////////////////////////////////////////////////////////////
149
-	////////////////////////// FIELD METHODS ///////////////////////////
150
-	////////////////////////////////////////////////////////////////////
151
-
152
-	/**
153
-	 * Focus on a particular checkable
154
-	 *
155
-	 * @param integer $on The checkable to focus on
156
-	 *
157
-	 * @return $this
158
-	 */
159
-	public function on($on)
160
-	{
161
-		if (!isset($this->items[$on])) {
162
-			return $this;
163
-		} else {
164
-			$this->focus = $on;
165
-		}
166
-
167
-		return $this;
168
-	}
169
-
170
-	/**
171
-	 * Set the checkables as inline
172
-	 */
173
-	public function inline($isInline = true)
174
-	{
175
-		$this->inline = $isInline;
176
-
177
-		return $this;
178
-	}
179
-
180
-	/**
181
-	 * Set the checkables as stacked
182
-	 */
183
-	public function stacked($isStacked = true)
184
-	{
185
-		$this->inline = !$isStacked;
186
-
187
-		return $this;
188
-	}
189
-
190
-	/**
191
-	 * Set the checkables as grouped
192
-	 */
193
-	public function grouped($isGrouped = true)
194
-	{
195
-		$this->grouped = $isGrouped;
196
-
197
-		return $this;
198
-	}
199
-
200
-	/**
201
-	 * Add text to a single checkable
202
-	 *
203
-	 * @param  string $text The checkable label
204
-	 *
205
-	 * @return $this
206
-	 */
207
-	public function text($text)
208
-	{
209
-		// Translate and format
210
-		$text = Helpers::translate($text);
211
-
212
-		// Apply on focused if any
213
-		$focused = $this->setOnFocused('label', $text);
214
-		if ($focused) {
215
-			return $this;
216
-		}
217
-
218
-		$this->text = $text;
219
-
220
-		return $this;
221
-	}
222
-
223
-	/**
224
-	 * Push this particular checkbox
225
-	 *
226
-	 * @param boolean $pushed
227
-	 *
228
-	 * @return $this
229
-	 */
230
-	public function push($pushed = true)
231
-	{
232
-		$this->isPushed = $pushed;
233
-
234
-		return $this;
235
-	}
236
-
237
-	/**
238
-	 * Check a specific item
239
-	 *
240
-	 * @param bool|string $checked The checkable to check, or an array of checked items
241
-	 *
242
-	 * @return $this
243
-	 */
244
-	public function check($checked = true)
245
-	{
246
-		// If we're setting all the checked items at once
247
-		if (is_array($checked)) {
248
-			$this->checked = $checked;
249
-			// Checking an item in particular
250
-		} elseif (is_string($checked) or is_int($checked)) {
251
-			$this->checked[$checked] = true;
252
-			// Only setting a single item
253
-		} else {
254
-			$this->checked[$this->name] = (bool) $checked;
255
-		}
256
-
257
-		return $this;
258
-	}
259
-
260
-
261
-	/**
262
-	 * Check if the checkables are inline
263
-	 *
264
-	 * @return boolean
265
-	 */
266
-	public function isInline()
267
-	{
268
-		return $this->inline;
269
-	}
270
-
271
-	////////////////////////////////////////////////////////////////////
272
-	////////////////////////// INTERNAL METHODS ////////////////////////
273
-	////////////////////////////////////////////////////////////////////
274
-
275
-	/**
276
-	 * Creates a series of checkable items
277
-	 *
278
-	 * @param array $_items Items to create
279
-	 */
280
-	protected function items($_items)
281
-	{
282
-		// If passing an array
283
-		if (sizeof($_items) == 1 and
284
-			isset($_items[0]) and
285
-			is_array($_items[0])
286
-		) {
287
-			$_items = $_items[0];
288
-		}
289
-
290
-		// Fetch models if that's what we were passed
291
-		if (isset($_items[0]) and is_object($_items[0])) {
292
-			$_items = Helpers::queryToArray($_items);
293
-			$_items = array_flip($_items);
294
-		}
295
-
296
-		// Iterate through items, assign a name and a label to each
297
-		$count = 0;
298
-		foreach ($_items as $label => $name) {
299
-
300
-			// Define a fallback name in case none is found
301
-			$fallback = $this->isCheckbox()
302
-				? $this->name.'_'.$count
303
-				: $this->name;
304
-
305
-			// Grouped fields
306
-			if ($this->isGrouped()) {
307
-				$attributes['id'] = str_replace('[]', null, $fallback);
308
-				$fallback         = str_replace('[]', null, $this->name).'[]';
309
-			}
310
-
311
-			// If we haven't any name defined for the checkable, try to compute some
312
-			if (!is_string($label) and !is_array($name)) {
313
-				$label = $name;
314
-				$name  = $fallback;
315
-			}
316
-
317
-			// If we gave custom information on the item, add them
318
-			if (is_array($name)) {
319
-				$attributes = $name;
320
-				$name       = Arr::get($attributes, 'name', $fallback);
321
-				unset($attributes['name']);
322
-			}
323
-
324
-			// Store all informations we have in an array
325
-			$item = array(
326
-				'name'  => $name,
327
-				'label' => Helpers::translate($label),
328
-				'count' => $count,
329
-			);
330
-			if (isset($attributes)) {
331
-				$item['attributes'] = $attributes;
332
-			}
333
-
334
-			$this->items[] = $item;
335
-			$count++;
336
-		}
337
-	}
338
-
339
-	/**
340
-	 * Renders a checkable
341
-	 *
342
-	 * @param string|array $item          A checkable item
343
-	 * @param integer      $fallbackValue A fallback value if none is set
344
-	 *
345
-	 * @return string
346
-	 */
347
-	protected function createCheckable($item, $fallbackValue = 1)
348
-	{
349
-		// Extract informations
350
-		extract($item);
351
-
352
-		// Set default values
353
-		if (!isset($attributes)) {
354
-			$attributes = array();
355
-		}
356
-		if (isset($attributes['value'])) {
357
-			$value = $attributes['value'];
358
-		}
359
-		if (!isset($value) or $value === $this->app['former']->getOption('unchecked_value')) {
360
-			$value = $fallbackValue;
361
-		}
362
-
363
-		// If inline items, add class
364
-		$isInline = $this->inline ? ' '.$this->app['former.framework']->getInlineLabelClass($this) : null;
365
-
366
-		// In Bootsrap 3 or 4 or 5, don't append the the checkable type (radio/checkbox) as a class if
367
-		// rendering inline.
368
-		$class =  in_array($this->app['former']->framework(), ['TwitterBootstrap3', 'TwitterBootstrap4', 'TwitterBootstrap5']) ? trim($isInline) : $this->checkable.$isInline;
369
-
370
-		// Merge custom attributes with global attributes
371
-		$attributes = array_merge($this->attributes, $attributes);
372
-		if (!isset($attributes['id'])) {
373
-			$attributes['id'] = $name.$this->unique($name);
374
-		}
375
-
376
-		// Create field
377
-		$field = Input::create($this->checkable, $name, Helpers::encode($value), $attributes);
378
-		if ($this->isChecked($item, $value)) {
379
-			$field->checked('checked');
380
-		}
381
-
382
-		// Add hidden checkbox if requested
383
-		if ($this->isOfType('checkbox', 'checkboxes', 'switch', 'switches')) {
384
-			if ($this->isPushed or ($this->app['former']->getOption('push_checkboxes') and $this->isPushed !== false)) {
385
-				$field = $this->app['former']->hidden($name)->forceValue($this->app['former']->getOption('unchecked_value')).$field->render();
386
-
387
-				// app['former.field'] was overwritten by Former::hidden() call in the line above, so here
388
-				// we reset it to $this to enable $this->app['former']->getErrors() to retrieve the correct object
389
-				$this->app->instance('former.field', $this);
390
-			}
391
-		}
392
-
393
-		// If no label to wrap, return plain checkable
394
-		if (!$label) {
395
-			$element = (is_object($field)) ? $field->render() : $field;
396
-		} elseif (in_array($this->app['former']->framework(), ['TwitterBootstrap4', 'TwitterBootstrap5'])) {
397
-			// Revised for Bootstrap 4, move the 'input' outside of the 'label'
398
-			$labelClass = 'form-check-label';
399
-			$element = $field . Element::create('label', $label)->for($attributes['id'])->class($labelClass)->render();
400
-
401
-			$wrapperClass = $this->inline ? 'form-check form-check-inline' : 'form-check';
402
-			if ($this->app['former']->framework() === 'TwitterBootstrap5' &&
403
-				$this->isOfType('switch', 'switches')
404
-			) {
405
-				$wrapperClass.= ' form-switch';
406
-			}
407
-			$element = Element::create('div', $element)->class($wrapperClass)->render();
408
-		} else {
409
-			// Original way is to add the 'input' inside the 'label'
410
-			$element = Element::create('label', $field.$label)->for($attributes['id'])->class($class)->render();
411
-		}
412
-
413
-		// If BS3, if checkables are stacked, wrap them in a div with the checkable type
414
-		if (!$isInline && $this->app['former']->framework() == 'TwitterBootstrap3') {
415
-			$wrapper = Element::create('div', $element)->class($this->checkable);
416
-			if ($this->getAttribute('disabled')) {
417
-				$wrapper->addClass('disabled');
418
-			}
419
-			$element = $wrapper->render();
420
-		}
421
-
422
-		// Return the field
423
-		return $element;
424
-	}
425
-
426
-	////////////////////////////////////////////////////////////////////
427
-	///////////////////////////// HELPERS //////////////////////////////
428
-	////////////////////////////////////////////////////////////////////
429
-
430
-	/**
431
-	 * Generate an unique ID for a field
432
-	 *
433
-	 * @param string $name The field's name
434
-	 *
435
-	 * @return string A field number to use
436
-	 */
437
-	protected function unique($name)
438
-	{
439
-		$this->app['former']->labels[] = $name;
440
-
441
-		// Count number of fields with the same ID
442
-		$where  = array_filter($this->app['former']->labels, function ($label) use ($name) {
443
-			return $label == $name;
444
-		});
445
-		$unique = sizeof($where);
446
-
447
-		// In case the field doesn't need to be numbered
448
-		if ($unique < 2 or empty($this->items)) {
449
-			return false;
450
-		}
451
-
452
-		return $unique;
453
-	}
454
-
455
-	/**
456
-	 * Set something on the currently focused checkable
457
-	 *
458
-	 * @param string $attribute The key to set
459
-	 * @param string $value     Its value
460
-	 *
461
-	 * @return $this|bool
462
-	 */
463
-	protected function setOnFocused($attribute, $value)
464
-	{
465
-		if (is_null($this->focus)) {
466
-			return false;
467
-		}
468
-
469
-		$this->items[$this->focus] = Arr::set($this->items[$this->focus], $attribute, $value);
470
-
471
-		return $this;
472
-	}
473
-
474
-	/**
475
-	 * Check if a checkable is checked
476
-	 *
477
-	 * @return boolean Checked or not
478
-	 */
479
-	protected function isChecked($item = null, $value = null)
480
-	{
481
-		if (isset($item['name'])) {
482
-			$name = $item['name'];
483
-		}
484
-		if (empty($name)) {
485
-			$name = $this->name;
486
-		}
487
-
488
-		// If it's a checkbox, see if we marqued that one as checked in the array
489
-		// Or if it's a single radio, simply see if we called check
490
-		if ($this->isCheckbox() or
491
-			!$this->isCheckbox() and !$this->items
492
-		) {
493
-			$checked = Arr::get($this->checked, $name, false);
494
-
495
-			// If there are multiple, search for the value
496
-			// as the name are the same between radios
497
-		} else {
498
-			$checked = Arr::get($this->checked, $value, false);
499
-		}
500
-
501
-		// Check the values and POST array
502
-		if ($this->isGrouped()) {
503
-			// The group index. (e.g. 'bar' if the item name is foo[bar], or the item index for foo[])
504
-			$groupIndex = self::getGroupIndexFromItem($item);
505
-
506
-			// Search using the bare name, not the individual item name
507
-			$post   = $this->app['former']->getPost($this->name);
508
-			$static = $this->app['former']->getValue($this->bind ?: $this->name);
509
-
510
-			if (isset($post[$groupIndex])) {
511
-				$post = $post[$groupIndex];
512
-			}
513
-
514
-			/**
515
-			 * Support for Laravel Collection repopulating for grouped checkboxes. Note that the groupIndex must
516
-			 * match the value in order for the checkbox to be considered checked, e.g.:
517
-			 *
518
-			 *  array(
519
-			 *    'name' = 'roles[foo]',
520
-			 *    'value' => 'foo',
521
-			 *  )
522
-			 */
523
-			if ($static instanceof Collection) {
524
-				// If the repopulate value is a collection, search for an item matching the $groupIndex
525
-				foreach ($static as $staticItem) {
526
-					$staticItemValue = method_exists($staticItem, 'getKey') ? $staticItem->getKey() : $staticItem;
527
-					if ($staticItemValue == $groupIndex) {
528
-						$static = $staticItemValue;
529
-						break;
530
-					}
531
-				}
532
-			} else if (isset($static[$groupIndex])) {
533
-				$static = $static[$groupIndex];
534
-			}
535
-		} else {
536
-			$post   = $this->app['former']->getPost($name);
537
-			$static = $this->app['former']->getValue($this->bind ?: $name);
538
-		}
539
-
540
-		if (!is_null($post) and $post !== $this->app['former']->getOption('unchecked_value')) {
541
-			$isChecked = ($post == $value);
542
-		} elseif (!is_null($static)) {
543
-			$isChecked = ($static == $value);
544
-		} else {
545
-			$isChecked = $checked;
546
-		}
547
-
548
-		return $isChecked ? true : false;
549
-	}
550
-
551
-	/**
552
-	 * Check if the current element is a checkbox
553
-	 *
554
-	 * @return boolean Checkbox or radio
555
-	 */
556
-	protected function isCheckbox()
557
-	{
558
-		return $this->checkable == 'checkbox';
559
-	}
560
-
561
-	/**
562
-	 * Check if the checkables are grouped or not
563
-	 *
564
-	 * @return boolean
565
-	 */
566
-	protected function isGrouped()
567
-	{
568
-		return
569
-			$this->grouped == true or
570
-			strpos($this->name, '[]') !== false;
571
-	}
572
-
573
-	/**
574
-	 * @param array $item The item array, containing at least name and count keys.
575
-	 *
576
-	 * @return mixed The group index. (e.g. returns bar if the item name is foo[bar], or the item count for foo[])
577
-	 */
578
-	public static function getGroupIndexFromItem($item)
579
-	{
580
-		$groupIndex = preg_replace('/^.*?\[(.*)\]$/', '$1', $item['name']);
581
-		if (empty($groupIndex) or $groupIndex == $item['name']) {
582
-			return $item['count'];
583
-		}
584
-
585
-		return $groupIndex;
586
-	}
17
+    /**
18
+     * Renders the checkables as inline
19
+     *
20
+     * @var boolean
21
+     */
22
+    protected $inline = false;
23
+
24
+    /**
25
+     * Add a text to a single element
26
+     *
27
+     * @var string
28
+     */
29
+    protected $text = null;
30
+
31
+    /**
32
+     * Renders the checkables as grouped
33
+     *
34
+     * @var boolean
35
+     */
36
+    protected $grouped = false;
37
+
38
+    /**
39
+     * The checkable items currently stored
40
+     *
41
+     * @var array
42
+     */
43
+    protected $items = array();
44
+
45
+    /**
46
+     * The type of checkable item
47
+     *
48
+     * @var string
49
+     */
50
+    protected $checkable = null;
51
+
52
+    /**
53
+     * An array of checked items
54
+     *
55
+     * @var array
56
+     */
57
+    protected $checked = array();
58
+
59
+    /**
60
+     * The checkable currently being focused on
61
+     *
62
+     * @var integer
63
+     */
64
+    protected $focus = null;
65
+
66
+    /**
67
+     * Whether this particular checkable is to be pushed
68
+     *
69
+     * @var boolean
70
+     */
71
+    protected $isPushed = null;
72
+
73
+    ////////////////////////////////////////////////////////////////////
74
+    //////////////////////////// CORE METHODS //////////////////////////
75
+    ////////////////////////////////////////////////////////////////////
76
+
77
+    /**
78
+     * Build a new checkable
79
+     *
80
+     * @param Container $app
81
+     * @param string    $type
82
+     * @param array     $name
83
+     * @param           $label
84
+     * @param           $value
85
+     * @param           $attributes
86
+     */
87
+    public function __construct(Container $app, $type, $name, $label, $value, $attributes)
88
+    {
89
+        // Unify auto and chained methods of grouping checkboxes
90
+        if (Str::endsWith($name, '[]')) {
91
+            $name = substr($name, 0, -2);
92
+            $this->grouped();
93
+        }
94
+        parent::__construct($app, $type, $name, $label, $value, $attributes);
95
+
96
+        if (is_array($this->value)) {
97
+            $this->items($this->value);
98
+        }
99
+    }
100
+
101
+    /**
102
+     * Apply methods to focused checkable
103
+     *
104
+     * @param string $method
105
+     * @param array  $parameters
106
+     *
107
+     * @return $this
108
+     */
109
+    public function __call($method, $parameters)
110
+    {
111
+        $focused = $this->setOnFocused('attributes.'.$method, Arr::get($parameters, 0));
112
+        if ($focused) {
113
+            return $this;
114
+        }
115
+
116
+        return parent::__call($method, $parameters);
117
+    }
118
+
119
+    /**
120
+     * Prints out the currently stored checkables
121
+     */
122
+    public function render()
123
+    {
124
+        $html = null;
125
+
126
+        $this->setFieldClasses();
127
+
128
+        // Multiple items
129
+        if ($this->items) {
130
+            unset($this->app['former']->labels[array_search($this->name, $this->app['former']->labels)]);
131
+            foreach ($this->items as $key => $item) {
132
+                $value = $this->isCheckbox() && !$this->isGrouped() ? 1 : $key;
133
+                $html .= $this->createCheckable($item, $value);
134
+            }
135
+
136
+            return $html;
137
+        }
138
+
139
+        // Single item
140
+        return $this->createCheckable(array(
141
+            'name'  => $this->name,
142
+            'label' => $this->text,
143
+            'value' => $this->value,
144
+            'attributes' => $this->attributes,
145
+        ));
146
+    }
147
+
148
+    ////////////////////////////////////////////////////////////////////
149
+    ////////////////////////// FIELD METHODS ///////////////////////////
150
+    ////////////////////////////////////////////////////////////////////
151
+
152
+    /**
153
+     * Focus on a particular checkable
154
+     *
155
+     * @param integer $on The checkable to focus on
156
+     *
157
+     * @return $this
158
+     */
159
+    public function on($on)
160
+    {
161
+        if (!isset($this->items[$on])) {
162
+            return $this;
163
+        } else {
164
+            $this->focus = $on;
165
+        }
166
+
167
+        return $this;
168
+    }
169
+
170
+    /**
171
+     * Set the checkables as inline
172
+     */
173
+    public function inline($isInline = true)
174
+    {
175
+        $this->inline = $isInline;
176
+
177
+        return $this;
178
+    }
179
+
180
+    /**
181
+     * Set the checkables as stacked
182
+     */
183
+    public function stacked($isStacked = true)
184
+    {
185
+        $this->inline = !$isStacked;
186
+
187
+        return $this;
188
+    }
189
+
190
+    /**
191
+     * Set the checkables as grouped
192
+     */
193
+    public function grouped($isGrouped = true)
194
+    {
195
+        $this->grouped = $isGrouped;
196
+
197
+        return $this;
198
+    }
199
+
200
+    /**
201
+     * Add text to a single checkable
202
+     *
203
+     * @param  string $text The checkable label
204
+     *
205
+     * @return $this
206
+     */
207
+    public function text($text)
208
+    {
209
+        // Translate and format
210
+        $text = Helpers::translate($text);
211
+
212
+        // Apply on focused if any
213
+        $focused = $this->setOnFocused('label', $text);
214
+        if ($focused) {
215
+            return $this;
216
+        }
217
+
218
+        $this->text = $text;
219
+
220
+        return $this;
221
+    }
222
+
223
+    /**
224
+     * Push this particular checkbox
225
+     *
226
+     * @param boolean $pushed
227
+     *
228
+     * @return $this
229
+     */
230
+    public function push($pushed = true)
231
+    {
232
+        $this->isPushed = $pushed;
233
+
234
+        return $this;
235
+    }
236
+
237
+    /**
238
+     * Check a specific item
239
+     *
240
+     * @param bool|string $checked The checkable to check, or an array of checked items
241
+     *
242
+     * @return $this
243
+     */
244
+    public function check($checked = true)
245
+    {
246
+        // If we're setting all the checked items at once
247
+        if (is_array($checked)) {
248
+            $this->checked = $checked;
249
+            // Checking an item in particular
250
+        } elseif (is_string($checked) or is_int($checked)) {
251
+            $this->checked[$checked] = true;
252
+            // Only setting a single item
253
+        } else {
254
+            $this->checked[$this->name] = (bool) $checked;
255
+        }
256
+
257
+        return $this;
258
+    }
259
+
260
+
261
+    /**
262
+     * Check if the checkables are inline
263
+     *
264
+     * @return boolean
265
+     */
266
+    public function isInline()
267
+    {
268
+        return $this->inline;
269
+    }
270
+
271
+    ////////////////////////////////////////////////////////////////////
272
+    ////////////////////////// INTERNAL METHODS ////////////////////////
273
+    ////////////////////////////////////////////////////////////////////
274
+
275
+    /**
276
+     * Creates a series of checkable items
277
+     *
278
+     * @param array $_items Items to create
279
+     */
280
+    protected function items($_items)
281
+    {
282
+        // If passing an array
283
+        if (sizeof($_items) == 1 and
284
+            isset($_items[0]) and
285
+            is_array($_items[0])
286
+        ) {
287
+            $_items = $_items[0];
288
+        }
289
+
290
+        // Fetch models if that's what we were passed
291
+        if (isset($_items[0]) and is_object($_items[0])) {
292
+            $_items = Helpers::queryToArray($_items);
293
+            $_items = array_flip($_items);
294
+        }
295
+
296
+        // Iterate through items, assign a name and a label to each
297
+        $count = 0;
298
+        foreach ($_items as $label => $name) {
299
+
300
+            // Define a fallback name in case none is found
301
+            $fallback = $this->isCheckbox()
302
+                ? $this->name.'_'.$count
303
+                : $this->name;
304
+
305
+            // Grouped fields
306
+            if ($this->isGrouped()) {
307
+                $attributes['id'] = str_replace('[]', null, $fallback);
308
+                $fallback         = str_replace('[]', null, $this->name).'[]';
309
+            }
310
+
311
+            // If we haven't any name defined for the checkable, try to compute some
312
+            if (!is_string($label) and !is_array($name)) {
313
+                $label = $name;
314
+                $name  = $fallback;
315
+            }
316
+
317
+            // If we gave custom information on the item, add them
318
+            if (is_array($name)) {
319
+                $attributes = $name;
320
+                $name       = Arr::get($attributes, 'name', $fallback);
321
+                unset($attributes['name']);
322
+            }
323
+
324
+            // Store all informations we have in an array
325
+            $item = array(
326
+                'name'  => $name,
327
+                'label' => Helpers::translate($label),
328
+                'count' => $count,
329
+            );
330
+            if (isset($attributes)) {
331
+                $item['attributes'] = $attributes;
332
+            }
333
+
334
+            $this->items[] = $item;
335
+            $count++;
336
+        }
337
+    }
338
+
339
+    /**
340
+     * Renders a checkable
341
+     *
342
+     * @param string|array $item          A checkable item
343
+     * @param integer      $fallbackValue A fallback value if none is set
344
+     *
345
+     * @return string
346
+     */
347
+    protected function createCheckable($item, $fallbackValue = 1)
348
+    {
349
+        // Extract informations
350
+        extract($item);
351
+
352
+        // Set default values
353
+        if (!isset($attributes)) {
354
+            $attributes = array();
355
+        }
356
+        if (isset($attributes['value'])) {
357
+            $value = $attributes['value'];
358
+        }
359
+        if (!isset($value) or $value === $this->app['former']->getOption('unchecked_value')) {
360
+            $value = $fallbackValue;
361
+        }
362
+
363
+        // If inline items, add class
364
+        $isInline = $this->inline ? ' '.$this->app['former.framework']->getInlineLabelClass($this) : null;
365
+
366
+        // In Bootsrap 3 or 4 or 5, don't append the the checkable type (radio/checkbox) as a class if
367
+        // rendering inline.
368
+        $class =  in_array($this->app['former']->framework(), ['TwitterBootstrap3', 'TwitterBootstrap4', 'TwitterBootstrap5']) ? trim($isInline) : $this->checkable.$isInline;
369
+
370
+        // Merge custom attributes with global attributes
371
+        $attributes = array_merge($this->attributes, $attributes);
372
+        if (!isset($attributes['id'])) {
373
+            $attributes['id'] = $name.$this->unique($name);
374
+        }
375
+
376
+        // Create field
377
+        $field = Input::create($this->checkable, $name, Helpers::encode($value), $attributes);
378
+        if ($this->isChecked($item, $value)) {
379
+            $field->checked('checked');
380
+        }
381
+
382
+        // Add hidden checkbox if requested
383
+        if ($this->isOfType('checkbox', 'checkboxes', 'switch', 'switches')) {
384
+            if ($this->isPushed or ($this->app['former']->getOption('push_checkboxes') and $this->isPushed !== false)) {
385
+                $field = $this->app['former']->hidden($name)->forceValue($this->app['former']->getOption('unchecked_value')).$field->render();
386
+
387
+                // app['former.field'] was overwritten by Former::hidden() call in the line above, so here
388
+                // we reset it to $this to enable $this->app['former']->getErrors() to retrieve the correct object
389
+                $this->app->instance('former.field', $this);
390
+            }
391
+        }
392
+
393
+        // If no label to wrap, return plain checkable
394
+        if (!$label) {
395
+            $element = (is_object($field)) ? $field->render() : $field;
396
+        } elseif (in_array($this->app['former']->framework(), ['TwitterBootstrap4', 'TwitterBootstrap5'])) {
397
+            // Revised for Bootstrap 4, move the 'input' outside of the 'label'
398
+            $labelClass = 'form-check-label';
399
+            $element = $field . Element::create('label', $label)->for($attributes['id'])->class($labelClass)->render();
400
+
401
+            $wrapperClass = $this->inline ? 'form-check form-check-inline' : 'form-check';
402
+            if ($this->app['former']->framework() === 'TwitterBootstrap5' &&
403
+                $this->isOfType('switch', 'switches')
404
+            ) {
405
+                $wrapperClass.= ' form-switch';
406
+            }
407
+            $element = Element::create('div', $element)->class($wrapperClass)->render();
408
+        } else {
409
+            // Original way is to add the 'input' inside the 'label'
410
+            $element = Element::create('label', $field.$label)->for($attributes['id'])->class($class)->render();
411
+        }
412
+
413
+        // If BS3, if checkables are stacked, wrap them in a div with the checkable type
414
+        if (!$isInline && $this->app['former']->framework() == 'TwitterBootstrap3') {
415
+            $wrapper = Element::create('div', $element)->class($this->checkable);
416
+            if ($this->getAttribute('disabled')) {
417
+                $wrapper->addClass('disabled');
418
+            }
419
+            $element = $wrapper->render();
420
+        }
421
+
422
+        // Return the field
423
+        return $element;
424
+    }
425
+
426
+    ////////////////////////////////////////////////////////////////////
427
+    ///////////////////////////// HELPERS //////////////////////////////
428
+    ////////////////////////////////////////////////////////////////////
429
+
430
+    /**
431
+     * Generate an unique ID for a field
432
+     *
433
+     * @param string $name The field's name
434
+     *
435
+     * @return string A field number to use
436
+     */
437
+    protected function unique($name)
438
+    {
439
+        $this->app['former']->labels[] = $name;
440
+
441
+        // Count number of fields with the same ID
442
+        $where  = array_filter($this->app['former']->labels, function ($label) use ($name) {
443
+            return $label == $name;
444
+        });
445
+        $unique = sizeof($where);
446
+
447
+        // In case the field doesn't need to be numbered
448
+        if ($unique < 2 or empty($this->items)) {
449
+            return false;
450
+        }
451
+
452
+        return $unique;
453
+    }
454
+
455
+    /**
456
+     * Set something on the currently focused checkable
457
+     *
458
+     * @param string $attribute The key to set
459
+     * @param string $value     Its value
460
+     *
461
+     * @return $this|bool
462
+     */
463
+    protected function setOnFocused($attribute, $value)
464
+    {
465
+        if (is_null($this->focus)) {
466
+            return false;
467
+        }
468
+
469
+        $this->items[$this->focus] = Arr::set($this->items[$this->focus], $attribute, $value);
470
+
471
+        return $this;
472
+    }
473
+
474
+    /**
475
+     * Check if a checkable is checked
476
+     *
477
+     * @return boolean Checked or not
478
+     */
479
+    protected function isChecked($item = null, $value = null)
480
+    {
481
+        if (isset($item['name'])) {
482
+            $name = $item['name'];
483
+        }
484
+        if (empty($name)) {
485
+            $name = $this->name;
486
+        }
487
+
488
+        // If it's a checkbox, see if we marqued that one as checked in the array
489
+        // Or if it's a single radio, simply see if we called check
490
+        if ($this->isCheckbox() or
491
+            !$this->isCheckbox() and !$this->items
492
+        ) {
493
+            $checked = Arr::get($this->checked, $name, false);
494
+
495
+            // If there are multiple, search for the value
496
+            // as the name are the same between radios
497
+        } else {
498
+            $checked = Arr::get($this->checked, $value, false);
499
+        }
500
+
501
+        // Check the values and POST array
502
+        if ($this->isGrouped()) {
503
+            // The group index. (e.g. 'bar' if the item name is foo[bar], or the item index for foo[])
504
+            $groupIndex = self::getGroupIndexFromItem($item);
505
+
506
+            // Search using the bare name, not the individual item name
507
+            $post   = $this->app['former']->getPost($this->name);
508
+            $static = $this->app['former']->getValue($this->bind ?: $this->name);
509
+
510
+            if (isset($post[$groupIndex])) {
511
+                $post = $post[$groupIndex];
512
+            }
513
+
514
+            /**
515
+             * Support for Laravel Collection repopulating for grouped checkboxes. Note that the groupIndex must
516
+             * match the value in order for the checkbox to be considered checked, e.g.:
517
+             *
518
+             *  array(
519
+             *    'name' = 'roles[foo]',
520
+             *    'value' => 'foo',
521
+             *  )
522
+             */
523
+            if ($static instanceof Collection) {
524
+                // If the repopulate value is a collection, search for an item matching the $groupIndex
525
+                foreach ($static as $staticItem) {
526
+                    $staticItemValue = method_exists($staticItem, 'getKey') ? $staticItem->getKey() : $staticItem;
527
+                    if ($staticItemValue == $groupIndex) {
528
+                        $static = $staticItemValue;
529
+                        break;
530
+                    }
531
+                }
532
+            } else if (isset($static[$groupIndex])) {
533
+                $static = $static[$groupIndex];
534
+            }
535
+        } else {
536
+            $post   = $this->app['former']->getPost($name);
537
+            $static = $this->app['former']->getValue($this->bind ?: $name);
538
+        }
539
+
540
+        if (!is_null($post) and $post !== $this->app['former']->getOption('unchecked_value')) {
541
+            $isChecked = ($post == $value);
542
+        } elseif (!is_null($static)) {
543
+            $isChecked = ($static == $value);
544
+        } else {
545
+            $isChecked = $checked;
546
+        }
547
+
548
+        return $isChecked ? true : false;
549
+    }
550
+
551
+    /**
552
+     * Check if the current element is a checkbox
553
+     *
554
+     * @return boolean Checkbox or radio
555
+     */
556
+    protected function isCheckbox()
557
+    {
558
+        return $this->checkable == 'checkbox';
559
+    }
560
+
561
+    /**
562
+     * Check if the checkables are grouped or not
563
+     *
564
+     * @return boolean
565
+     */
566
+    protected function isGrouped()
567
+    {
568
+        return
569
+            $this->grouped == true or
570
+            strpos($this->name, '[]') !== false;
571
+    }
572
+
573
+    /**
574
+     * @param array $item The item array, containing at least name and count keys.
575
+     *
576
+     * @return mixed The group index. (e.g. returns bar if the item name is foo[bar], or the item count for foo[])
577
+     */
578
+    public static function getGroupIndexFromItem($item)
579
+    {
580
+        $groupIndex = preg_replace('/^.*?\[(.*)\]$/', '$1', $item['name']);
581
+        if (empty($groupIndex) or $groupIndex == $item['name']) {
582
+            return $item['count'];
583
+        }
584
+
585
+        return $groupIndex;
586
+    }
587 587
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -365,7 +365,7 @@  discard block
 block discarded – undo
365 365
 
366 366
 		// In Bootsrap 3 or 4 or 5, don't append the the checkable type (radio/checkbox) as a class if
367 367
 		// rendering inline.
368
-		$class =  in_array($this->app['former']->framework(), ['TwitterBootstrap3', 'TwitterBootstrap4', 'TwitterBootstrap5']) ? trim($isInline) : $this->checkable.$isInline;
368
+		$class = in_array($this->app['former']->framework(), ['TwitterBootstrap3', 'TwitterBootstrap4', 'TwitterBootstrap5']) ? trim($isInline) : $this->checkable.$isInline;
369 369
 
370 370
 		// Merge custom attributes with global attributes
371 371
 		$attributes = array_merge($this->attributes, $attributes);
@@ -396,13 +396,13 @@  discard block
 block discarded – undo
396 396
 		} elseif (in_array($this->app['former']->framework(), ['TwitterBootstrap4', 'TwitterBootstrap5'])) {
397 397
 			// Revised for Bootstrap 4, move the 'input' outside of the 'label'
398 398
 			$labelClass = 'form-check-label';
399
-			$element = $field . Element::create('label', $label)->for($attributes['id'])->class($labelClass)->render();
399
+			$element = $field.Element::create('label', $label)->for($attributes['id'])->class($labelClass)->render();
400 400
 
401 401
 			$wrapperClass = $this->inline ? 'form-check form-check-inline' : 'form-check';
402 402
 			if ($this->app['former']->framework() === 'TwitterBootstrap5' &&
403 403
 				$this->isOfType('switch', 'switches')
404 404
 			) {
405
-				$wrapperClass.= ' form-switch';
405
+				$wrapperClass .= ' form-switch';
406 406
 			}
407 407
 			$element = Element::create('div', $element)->class($wrapperClass)->render();
408 408
 		} else {
@@ -439,7 +439,7 @@  discard block
 block discarded – undo
439 439
 		$this->app['former']->labels[] = $name;
440 440
 
441 441
 		// Count number of fields with the same ID
442
-		$where  = array_filter($this->app['former']->labels, function ($label) use ($name) {
442
+		$where  = array_filter($this->app['former']->labels, function($label) use ($name) {
443 443
 			return $label == $name;
444 444
 		});
445 445
 		$unique = sizeof($where);
Please login to merge, or discard this patch.
src/Former/Form/Fields/Switchbox.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -3,21 +3,21 @@
 block discarded – undo
3 3
 
4 4
 class Switchbox extends Checkbox
5 5
 {
6
-	////////////////////////////////////////////////////////////////////
7
-	////////////////////////// FIELD METHODS ///////////////////////////
8
-	////////////////////////////////////////////////////////////////////
6
+    ////////////////////////////////////////////////////////////////////
7
+    ////////////////////////// FIELD METHODS ///////////////////////////
8
+    ////////////////////////////////////////////////////////////////////
9 9
 
10
-	/**
11
-	 * Create a serie of switches
12
-	 */
13
-	public function switches()
14
-	{
15
-		if ($this->isGrouped()) {
16
-			// Remove any possible items added by the Populator.
17
-			$this->items = array();
18
-		}
19
-		$this->items(func_get_args());
10
+    /**
11
+     * Create a serie of switches
12
+     */
13
+    public function switches()
14
+    {
15
+        if ($this->isGrouped()) {
16
+            // Remove any possible items added by the Populator.
17
+            $this->items = array();
18
+        }
19
+        $this->items(func_get_args());
20 20
 
21
-		return $this;
22
-	}
21
+        return $this;
22
+    }
23 23
 }
Please login to merge, or discard this patch.
src/Former/MethodDispatcher.php 1 patch
Indentation   +239 added lines, -239 removed lines patch added patch discarded remove patch
@@ -12,243 +12,243 @@
 block discarded – undo
12 12
  */
13 13
 class MethodDispatcher
14 14
 {
15
-	/**
16
-	 * The IoC Container
17
-	 *
18
-	 * @var Container
19
-	 */
20
-	protected $app;
21
-
22
-	/**
23
-	 * An array of fields repositories
24
-	 *
25
-	 * @var array
26
-	 */
27
-	protected $repositories = array();
28
-
29
-	/**
30
-	 * Build a new Dispatcher
31
-	 *
32
-	 * @param Container $app
33
-	 * @param array     $repositories
34
-	 */
35
-	public function __construct(Container $app, $repositories)
36
-	{
37
-		$this->app          = $app;
38
-		$this->repositories = (array) $repositories;
39
-	}
40
-
41
-	////////////////////////////////////////////////////////////////////
42
-	///////////////////////////// REPOSITORIES /////////////////////////
43
-	////////////////////////////////////////////////////////////////////
44
-
45
-	/**
46
-	 * Add a fields repository
47
-	 *
48
-	 * @param string $repository
49
-	 *
50
-	 * @return $this
51
-	 */
52
-	public function addRepository($repository)
53
-	{
54
-		array_unshift($this->repositories, $repository);
55
-
56
-		return $this;
57
-	}
58
-
59
-	////////////////////////////////////////////////////////////////////
60
-	///////////////////////////// DISPATCHERS //////////////////////////
61
-	////////////////////////////////////////////////////////////////////
62
-
63
-	/**
64
-	 * Dispatch a call to a registered macro
65
-	 *
66
-	 * @param  string $method     The macro's name
67
-	 * @param  array  $parameters The macro's arguments
68
-	 *
69
-	 * @return mixed
70
-	 */
71
-	public function toMacros($method, $parameters)
72
-	{
73
-		if (!$this->app['former']->hasMacro($method)) {
74
-			return false;
75
-		}
76
-
77
-		// Get and format macro
78
-		$callback = $this->app['former']->getMacro($method);
79
-		if ($callback instanceof Closure) {
80
-			return call_user_func_array($callback, $parameters);
81
-		} // Cancel if the macro is invalid
82
-		elseif (!is_string($callback)) {
83
-			return false;
84
-		}
85
-
86
-		// Get class and method
87
-		list($class, $method) = explode('@', $callback);
88
-		$this->app->instance('Illuminate\Container\Container', $this->app);
89
-
90
-		return call_user_func_array(array($this->app->make($class), $method), $parameters);
91
-	}
92
-
93
-	/**
94
-	 * Dispatch a call over to Elements
95
-	 *
96
-	 * @param string $method     The method called
97
-	 * @param array  $parameters Its parameters
98
-	 *
99
-	 * @return string
100
-	 */
101
-	public function toElements($method, $parameters)
102
-	{
103
-		// Disregards if the method isn't an element
104
-		if (!method_exists($elements = new Form\Elements($this->app, $this->app['session']), $method)) {
105
-			return false;
106
-		}
107
-
108
-		return call_user_func_array(array($elements, $method), $parameters);
109
-	}
110
-
111
-	/**
112
-	 * Dispatch a call over to Form
113
-	 *
114
-	 * @param string $method     The method called
115
-	 * @param array  $parameters Its parameters
116
-	 *
117
-	 * @return Form
118
-	 */
119
-	public function toForm($method, $parameters)
120
-	{
121
-		// Disregards if the method doesn't contain 'open'
122
-		if (!Str::contains($method, 'open') and !Str::contains($method, 'Open')) {
123
-			return false;
124
-		}
125
-
126
-		$form = new Form\Form($this->app, $this->app['url'], $this->app['former.populator']);
127
-
128
-		return $form->openForm($method, $parameters);
129
-	}
130
-
131
-	/**
132
-	 * Dispatch a call over to Group
133
-	 *
134
-	 * @param string $method     The method called
135
-	 * @param array  $parameters Its parameters
136
-	 *
137
-	 * @return Group
138
-	 */
139
-	public function toGroup($method, $parameters)
140
-	{
141
-		// Disregards if the method isn't "group"
142
-		if ($method != 'group') {
143
-			return false;
144
-		}
145
-
146
-		// Create opener
147
-		$group = new Form\Group(
148
-			$this->app,
149
-			Arr::get($parameters, 0, null),
150
-			Arr::get($parameters, 1, null)
151
-		);
152
-
153
-		// Set custom group as true
154
-		Form\Group::$opened = true;
155
-
156
-		// Set custom group reference
157
-		Form\Group::$openGroup = $group;
158
-
159
-		return $group;
160
-	}
161
-
162
-	/**
163
-	 * Dispatch a call over to Actions
164
-	 *
165
-	 * @param string $method     The method called
166
-	 * @param array  $parameters Its parameters
167
-	 *
168
-	 * @return Actions
169
-	 */
170
-	public function toActions($method, $parameters)
171
-	{
172
-		if ($method != 'actions') {
173
-			return false;
174
-		}
175
-
176
-		return new Form\Actions($this->app, $parameters);
177
-	}
178
-
179
-	/**
180
-	 * Dispatch a call over to the Fields
181
-	 *
182
-	 * @param string $method     The method called
183
-	 * @param array  $parameters Its parameters
184
-	 *
185
-	 * @return Field
186
-	 */
187
-	public function toFields($method, $parameters)
188
-	{
189
-		// Listing parameters
190
-		$class = $this->getClassFromMethod($method);
191
-		$field = new $class(
192
-			$this->app,
193
-			$method,
194
-			Arr::get($parameters, 0),
195
-			Arr::get($parameters, 1),
196
-			Arr::get($parameters, 2),
197
-			Arr::get($parameters, 3),
198
-			Arr::get($parameters, 4),
199
-			Arr::get($parameters, 5)
200
-		);
201
-
202
-		return $field;
203
-	}
204
-
205
-	////////////////////////////////////////////////////////////////////
206
-	///////////////////////////// HELPERS //////////////////////////////
207
-	////////////////////////////////////////////////////////////////////
208
-
209
-	/**
210
-	 * Get the correct class to call according to the created field
211
-	 *
212
-	 * @param  string $method The field created
213
-	 *
214
-	 * @return string The correct class
215
-	 */
216
-	protected function getClassFromMethod($method)
217
-	{
218
-		// If the field's name directly match a class, call it
219
-		$class        = Str::singular(Str::title($method));
220
-		$studly_class = Str::singular(Str::studly($method));
221
-		foreach ($this->repositories as $repository) {
222
-			if (class_exists($repository.$studly_class)) {
223
-				return $repository.$studly_class;
224
-			} else {
225
-				if (class_exists($repository.$class)) {
226
-					return $repository.$class;
227
-				}
228
-			}
229
-		}
230
-
231
-		// Else convert known fields to their classes
232
-		switch ($method) {
233
-			case 'switch':
234
-			case 'switches':
235
-				$class = Former::FIELDSPACE.'Switchbox';
236
-				break;
237
-			case 'submit':
238
-			case 'link':
239
-			case 'reset':
240
-				$class = Former::FIELDSPACE.'Button';
241
-				break;
242
-
243
-			case 'multiselect':
244
-				$class = Former::FIELDSPACE.'Select';
245
-				break;
246
-
247
-			default:
248
-				$class = Former::FIELDSPACE.'Input';
249
-				break;
250
-		}
251
-
252
-		return $class;
253
-	}
15
+    /**
16
+     * The IoC Container
17
+     *
18
+     * @var Container
19
+     */
20
+    protected $app;
21
+
22
+    /**
23
+     * An array of fields repositories
24
+     *
25
+     * @var array
26
+     */
27
+    protected $repositories = array();
28
+
29
+    /**
30
+     * Build a new Dispatcher
31
+     *
32
+     * @param Container $app
33
+     * @param array     $repositories
34
+     */
35
+    public function __construct(Container $app, $repositories)
36
+    {
37
+        $this->app          = $app;
38
+        $this->repositories = (array) $repositories;
39
+    }
40
+
41
+    ////////////////////////////////////////////////////////////////////
42
+    ///////////////////////////// REPOSITORIES /////////////////////////
43
+    ////////////////////////////////////////////////////////////////////
44
+
45
+    /**
46
+     * Add a fields repository
47
+     *
48
+     * @param string $repository
49
+     *
50
+     * @return $this
51
+     */
52
+    public function addRepository($repository)
53
+    {
54
+        array_unshift($this->repositories, $repository);
55
+
56
+        return $this;
57
+    }
58
+
59
+    ////////////////////////////////////////////////////////////////////
60
+    ///////////////////////////// DISPATCHERS //////////////////////////
61
+    ////////////////////////////////////////////////////////////////////
62
+
63
+    /**
64
+     * Dispatch a call to a registered macro
65
+     *
66
+     * @param  string $method     The macro's name
67
+     * @param  array  $parameters The macro's arguments
68
+     *
69
+     * @return mixed
70
+     */
71
+    public function toMacros($method, $parameters)
72
+    {
73
+        if (!$this->app['former']->hasMacro($method)) {
74
+            return false;
75
+        }
76
+
77
+        // Get and format macro
78
+        $callback = $this->app['former']->getMacro($method);
79
+        if ($callback instanceof Closure) {
80
+            return call_user_func_array($callback, $parameters);
81
+        } // Cancel if the macro is invalid
82
+        elseif (!is_string($callback)) {
83
+            return false;
84
+        }
85
+
86
+        // Get class and method
87
+        list($class, $method) = explode('@', $callback);
88
+        $this->app->instance('Illuminate\Container\Container', $this->app);
89
+
90
+        return call_user_func_array(array($this->app->make($class), $method), $parameters);
91
+    }
92
+
93
+    /**
94
+     * Dispatch a call over to Elements
95
+     *
96
+     * @param string $method     The method called
97
+     * @param array  $parameters Its parameters
98
+     *
99
+     * @return string
100
+     */
101
+    public function toElements($method, $parameters)
102
+    {
103
+        // Disregards if the method isn't an element
104
+        if (!method_exists($elements = new Form\Elements($this->app, $this->app['session']), $method)) {
105
+            return false;
106
+        }
107
+
108
+        return call_user_func_array(array($elements, $method), $parameters);
109
+    }
110
+
111
+    /**
112
+     * Dispatch a call over to Form
113
+     *
114
+     * @param string $method     The method called
115
+     * @param array  $parameters Its parameters
116
+     *
117
+     * @return Form
118
+     */
119
+    public function toForm($method, $parameters)
120
+    {
121
+        // Disregards if the method doesn't contain 'open'
122
+        if (!Str::contains($method, 'open') and !Str::contains($method, 'Open')) {
123
+            return false;
124
+        }
125
+
126
+        $form = new Form\Form($this->app, $this->app['url'], $this->app['former.populator']);
127
+
128
+        return $form->openForm($method, $parameters);
129
+    }
130
+
131
+    /**
132
+     * Dispatch a call over to Group
133
+     *
134
+     * @param string $method     The method called
135
+     * @param array  $parameters Its parameters
136
+     *
137
+     * @return Group
138
+     */
139
+    public function toGroup($method, $parameters)
140
+    {
141
+        // Disregards if the method isn't "group"
142
+        if ($method != 'group') {
143
+            return false;
144
+        }
145
+
146
+        // Create opener
147
+        $group = new Form\Group(
148
+            $this->app,
149
+            Arr::get($parameters, 0, null),
150
+            Arr::get($parameters, 1, null)
151
+        );
152
+
153
+        // Set custom group as true
154
+        Form\Group::$opened = true;
155
+
156
+        // Set custom group reference
157
+        Form\Group::$openGroup = $group;
158
+
159
+        return $group;
160
+    }
161
+
162
+    /**
163
+     * Dispatch a call over to Actions
164
+     *
165
+     * @param string $method     The method called
166
+     * @param array  $parameters Its parameters
167
+     *
168
+     * @return Actions
169
+     */
170
+    public function toActions($method, $parameters)
171
+    {
172
+        if ($method != 'actions') {
173
+            return false;
174
+        }
175
+
176
+        return new Form\Actions($this->app, $parameters);
177
+    }
178
+
179
+    /**
180
+     * Dispatch a call over to the Fields
181
+     *
182
+     * @param string $method     The method called
183
+     * @param array  $parameters Its parameters
184
+     *
185
+     * @return Field
186
+     */
187
+    public function toFields($method, $parameters)
188
+    {
189
+        // Listing parameters
190
+        $class = $this->getClassFromMethod($method);
191
+        $field = new $class(
192
+            $this->app,
193
+            $method,
194
+            Arr::get($parameters, 0),
195
+            Arr::get($parameters, 1),
196
+            Arr::get($parameters, 2),
197
+            Arr::get($parameters, 3),
198
+            Arr::get($parameters, 4),
199
+            Arr::get($parameters, 5)
200
+        );
201
+
202
+        return $field;
203
+    }
204
+
205
+    ////////////////////////////////////////////////////////////////////
206
+    ///////////////////////////// HELPERS //////////////////////////////
207
+    ////////////////////////////////////////////////////////////////////
208
+
209
+    /**
210
+     * Get the correct class to call according to the created field
211
+     *
212
+     * @param  string $method The field created
213
+     *
214
+     * @return string The correct class
215
+     */
216
+    protected function getClassFromMethod($method)
217
+    {
218
+        // If the field's name directly match a class, call it
219
+        $class        = Str::singular(Str::title($method));
220
+        $studly_class = Str::singular(Str::studly($method));
221
+        foreach ($this->repositories as $repository) {
222
+            if (class_exists($repository.$studly_class)) {
223
+                return $repository.$studly_class;
224
+            } else {
225
+                if (class_exists($repository.$class)) {
226
+                    return $repository.$class;
227
+                }
228
+            }
229
+        }
230
+
231
+        // Else convert known fields to their classes
232
+        switch ($method) {
233
+            case 'switch':
234
+            case 'switches':
235
+                $class = Former::FIELDSPACE.'Switchbox';
236
+                break;
237
+            case 'submit':
238
+            case 'link':
239
+            case 'reset':
240
+                $class = Former::FIELDSPACE.'Button';
241
+                break;
242
+
243
+            case 'multiselect':
244
+                $class = Former::FIELDSPACE.'Select';
245
+                break;
246
+
247
+            default:
248
+                $class = Former::FIELDSPACE.'Input';
249
+                break;
250
+        }
251
+
252
+        return $class;
253
+    }
254 254
 }
Please login to merge, or discard this patch.
src/Former/Form/Group.php 1 patch
Indentation   +548 added lines, -548 removed lines patch added patch discarded remove patch
@@ -13,552 +13,552 @@
 block discarded – undo
13 13
  */
14 14
 class Group extends Tag
15 15
 {
16
-	/**
17
-	 * The Container
18
-	 *
19
-	 * @var Container
20
-	 */
21
-	protected $app;
22
-
23
-	/**
24
-	 * The current state of the group
25
-	 *
26
-	 * @var string
27
-	 */
28
-	protected $state = null;
29
-
30
-	/**
31
-	 * Whether the field should be displayed raw or not
32
-	 *
33
-	 * @var boolean
34
-	 */
35
-	protected $raw = false;
36
-
37
-	/**
38
-	 * The group label
39
-	 *
40
-	 * @var Element
41
-	 */
42
-	protected $label;
43
-
44
-	/**
45
-	 * The group help
46
-	 *
47
-	 * @var array
48
-	 */
49
-	protected $help = array();
50
-
51
-	/**
52
-	 * An array of elements to preprend the field
53
-	 *
54
-	 * @var array
55
-	 */
56
-	protected $prepend = array();
57
-
58
-	/**
59
-	 * An array of elements to append the field
60
-	 *
61
-	 * @var array
62
-	 */
63
-	protected $append = array();
64
-
65
-	/**
66
-	 * The field validations to be checked for errors
67
-	 *
68
-	 * @var array
69
-	 */
70
-	protected $validations = array();
71
-
72
-	/**
73
-	 * The group's element
74
-	 *
75
-	 * @var string
76
-	 */
77
-	protected $element = 'div';
78
-
79
-	/**
80
-	 * Whether a custom group is opened or not
81
-	 *
82
-	 * @var boolean
83
-	 */
84
-	public static $opened = false;
85
-
86
-	/**
87
-	 * The custom group that is open
88
-	 *
89
-	 * @var Former\Form\Group
90
-	 */
91
-	public static $openGroup = null;
92
-
93
-	////////////////////////////////////////////////////////////////////
94
-	/////////////////////////// CORE METHODS ///////////////////////////
95
-	////////////////////////////////////////////////////////////////////
96
-
97
-	/**
98
-	 * Creates a group
99
-	 *
100
-	 * @param string $label Its label
101
-	 */
102
-	public function __construct(Container $app, $label, $validations = null)
103
-	{
104
-		// Get special classes
105
-		$this->app = $app;
106
-		$this->addClass($this->app['former.framework']->getGroupClasses());
107
-
108
-		// Invisible if Nude
109
-		if ($this->app['former.framework']->is('Nude')) {
110
-			$this->element = '';
111
-		}
112
-
113
-		// Set group label
114
-		if ($label) {
115
-			$this->setLabel($label);
116
-		}
117
-
118
-		// Set validations used to override groups own conclusions
119
-		$this->validations = (array) $validations;
120
-	}
121
-
122
-	/**
123
-	 * Prints out the opening of the Control Group
124
-	 *
125
-	 * @return string A control group opening tag
126
-	 */
127
-	public function __toString()
128
-	{
129
-		return $this->open().$this->getFormattedLabel();
130
-	}
131
-
132
-	/**
133
-	 * Opens a group
134
-	 *
135
-	 * @return string Opening tag
136
-	 */
137
-	public function open()
138
-	{
139
-		if ($this->getErrors()) {
140
-			$this->state($this->app['former.framework']->errorState());
141
-		}
142
-
143
-		// Retrieve state and append it to classes
144
-		if ($this->state) {
145
-			$this->addClass($this->state);
146
-		}
147
-
148
-		// Required state
149
-		if ($this->app->bound('former.field') and $this->app['former.field']->isRequired()) {
150
-			$this->addClass($this->app['former']->getOption('required_class'));
151
-		}
152
-
153
-		return parent::open();
154
-	}
155
-
156
-	/**
157
-	 * Set the contents of the current group
158
-	 *
159
-	 * @param string $contents The group contents
160
-	 *
161
-	 * @return string A group
162
-	 */
163
-	public function contents($contents)
164
-	{
165
-		return $this->wrap($contents, $this->getFormattedLabel());
166
-	}
167
-
168
-	/**
169
-	 * Wrap a Field with the current group
170
-	 *
171
-	 * @param  \Former\Traits\Field $field A Field instance
172
-	 *
173
-	 * @return string        A group
174
-	 */
175
-	public function wrapField($field)
176
-	{
177
-		$label = $this->getLabel($field);
178
-		$help = $this->getHelp();
179
-		if ($field->isCheckable() &&
180
-			$this->app['former']->framework() === 'TwitterBootstrap4'
181
-		) {
182
-			$wrapperClass = $field->isInline() ? 'form-check form-check-inline' : 'form-check';
183
-			if ($this->app['former']->getErrors($field->getName())) {
184
-				$hiddenInput = Element::create('input', null, ['type' => 'hidden'])->class('form-check-input is-invalid');
185
-				$help = $hiddenInput.$help;
186
-			}
187
-			$help = Element::create('div', $help)->class($wrapperClass);
188
-		}
189
-		$withFloatingLabel = $field->withFloatingLabel();
190
-
191
-		$field = $this->prependAppend($field);
192
-		$field .= $help;
193
-
194
-		if ($withFloatingLabel &&
195
-			$this->app['former']->framework() === 'TwitterBootstrap5'
196
-		) {
197
-			return $this->wrapWithFloatingLabel($field, $label);
198
-		}
199
-
200
-		return $this->wrap($field, $label);
201
-	}
202
-
203
-	////////////////////////////////////////////////////////////////////
204
-	//////////////////////////// FIELD METHODS /////////////////////////
205
-	////////////////////////////////////////////////////////////////////
206
-
207
-	/**
208
-	 * Set the state of the group
209
-	 *
210
-	 * @param  string $state A Bootstrap state class
211
-	 */
212
-	public function state($state)
213
-	{
214
-		// Filter state
215
-		$state = $this->app['former.framework']->filterState($state);
216
-
217
-		$this->state = $state;
218
-	}
219
-
220
-	/**
221
-	 * Set a class on the Group
222
-	 *
223
-	 * @param string $class The class(es) to add on the Group
224
-	 */
225
-	public function addGroupClass($class)
226
-	{
227
-		$this->addClass($class);
228
-	}
229
-
230
-	/**
231
-	 * Remove one or more classes on the Group
232
-	 *
233
-	 * @param string $class The class(es) to remove on the Group
234
-	 */
235
-	public function removeGroupClass($class)
236
-	{
237
-		$this->removeClass($class);
238
-	}
239
-
240
-	/**
241
-	 * Set a class on the Label
242
-	 *
243
-	 * @param string $class The class(es) to add on the Label
244
-	 */
245
-	public function addLabelClass($class)
246
-	{
247
-		// Don't add a label class if it isn't an Element instance
248
-		if (!$this->label instanceof Element) {
249
-			return $this;
250
-		}
251
-
252
-		$this->label->addClass($class);
253
-
254
-		return $this;
255
-	}
256
-
257
-	/**
258
-	 * Remove one or more classes on the Label
259
-	 *
260
-	 * @param string $class The class(es) to remove on the Label
261
-	 */
262
-	public function removeLabelClass($class)
263
-	{
264
-		// Don't remove a label class if it isn't an Element instance
265
-		if (!$this->label instanceof Element) {
266
-			return $this;
267
-		}
268
-
269
-		$this->label->removeClass($class);
270
-
271
-		return $this;
272
-	}
273
-
274
-	/**
275
-	 * Adds a label to the group
276
-	 *
277
-	 * @param  string $label A label
278
-	 */
279
-	public function setLabel($label)
280
-	{
281
-		if (!$label instanceof Element) {
282
-			$label = Helpers::translate($label);
283
-			$label = Element::create('label', $label)->for($label);
284
-		}
285
-
286
-		$this->label = $label;
287
-	}
288
-
289
-	/**
290
-	 * Get the formatted group label
291
-	 *
292
-	 * @return string|null
293
-	 */
294
-	public function getFormattedLabel()
295
-	{
296
-		if (!$this->label) {
297
-			return false;
298
-		}
299
-
300
-		return $this->label->addClass($this->app['former.framework']->getLabelClasses());
301
-	}
302
-
303
-	/**
304
-	 * Disables the control group for the current field
305
-	 */
306
-	public function raw()
307
-	{
308
-		$this->raw = true;
309
-	}
310
-
311
-	/**
312
-	 * Check if the current group is to be displayed or not
313
-	 *
314
-	 * @return boolean
315
-	 */
316
-	public function isRaw()
317
-	{
318
-		return (bool) $this->raw;
319
-	}
320
-
321
-	////////////////////////////////////////////////////////////////////
322
-	///////////////////////////// HELP BLOCKS //////////////////////////
323
-	////////////////////////////////////////////////////////////////////
324
-
325
-	/**
326
-	 * Alias for inlineHelp
327
-	 *
328
-	 * @param  string $help       The help text
329
-	 * @param  array  $attributes Facultative attributes
330
-	 */
331
-	public function help($help, $attributes = array())
332
-	{
333
-		return $this->inlineHelp($help, $attributes);
334
-	}
335
-
336
-	/**
337
-	 * Add an inline help
338
-	 *
339
-	 * @param  string $help       The help text
340
-	 * @param  array  $attributes Facultative attributes
341
-	 */
342
-	public function inlineHelp($help, $attributes = array())
343
-	{
344
-		// If no help text, do nothing
345
-		if (!$help) {
346
-			return false;
347
-		}
348
-
349
-		$this->help['inline'] = $this->app['former.framework']->createHelp($help, $attributes);
350
-	}
351
-
352
-	/**
353
-	 * Add an block help
354
-	 *
355
-	 * @param  string $help       The help text
356
-	 * @param  array  $attributes Facultative attributes
357
-	 */
358
-	public function blockHelp($help, $attributes = array())
359
-	{
360
-		// Reserved method
361
-		if ($this->app['former.framework']->isnt('TwitterBootstrap') &&
362
-			$this->app['former.framework']->isnt('TwitterBootstrap3') &&
363
-			$this->app['former.framework']->isnt('TwitterBootstrap4') &&
364
-			$this->app['former.framework']->isnt('TwitterBootstrap5')
365
-		) {
366
-			throw new BadMethodCallException('This method is only available on the Bootstrap framework');
367
-		}
368
-
369
-		// If no help text, do nothing
370
-		if (!$help) {
371
-			return false;
372
-		}
373
-
374
-		$this->help['block'] = $this->app['former.framework']->createBlockHelp($help, $attributes);
375
-	}
376
-
377
-	////////////////////////////////////////////////////////////////////
378
-	///////////////////////// PREPEND/APPEND METHODS ///////////////////
379
-	////////////////////////////////////////////////////////////////////
380
-
381
-	/**
382
-	 * Prepend elements to the field
383
-	 */
384
-	public function prepend()
385
-	{
386
-		$this->placeAround(func_get_args(), 'prepend');
387
-	}
388
-
389
-	/**
390
-	 * Append elements to the field
391
-	 */
392
-	public function append()
393
-	{
394
-		$this->placeAround(func_get_args(), 'append');
395
-	}
396
-
397
-	/**
398
-	 * Prepends an icon to a field
399
-	 *
400
-	 * @param string $icon       The icon to prepend
401
-	 * @param array  $attributes Its attributes
402
-	 */
403
-	public function prependIcon($icon, $attributes = array(), $iconSettings = array())
404
-	{
405
-		$icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
406
-
407
-		$this->prepend($icon);
408
-	}
409
-
410
-	/**
411
-	 * Append an icon to a field
412
-	 *
413
-	 * @param string $icon       The icon to prepend
414
-	 * @param array  $attributes Its attributes
415
-	 */
416
-	public function appendIcon($icon, $attributes = array(), $iconSettings = array())
417
-	{
418
-		$icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
419
-
420
-		$this->append($icon);
421
-	}
422
-
423
-	////////////////////////////////////////////////////////////////////
424
-	//////////////////////////////// HELPERS ///////////////////////////
425
-	////////////////////////////////////////////////////////////////////
426
-
427
-	/**
428
-	 * Get the errors for the group
429
-	 *
430
-	 * @return string
431
-	 */
432
-	public function getErrors()
433
-	{
434
-		$errors = '';
435
-
436
-		if (!self::$opened) {
437
-
438
-			// for non-custom groups, normal error handling applies
439
-			$errors = $this->app['former']->getErrors();
440
-		} elseif (!empty($this->validations)) {
441
-
442
-			// error handling only when validations specified for custom groups
443
-			foreach ($this->validations as $validation) {
444
-				$errors .= $this->app['former']->getErrors($validation);
445
-			}
446
-		}
447
-
448
-		return $errors;
449
-	}
450
-
451
-	/**
452
-	 * Wraps content in a group
453
-	 *
454
-	 * @param string $contents The content
455
-	 * @param string $label    The label to add
456
-	 *
457
-	 * @return string A group
458
-	 */
459
-	public function wrap($contents, $label = null)
460
-	{
461
-		$group = $this->open();
462
-		$group .= $label;
463
-		$group .= $this->app['former.framework']->wrapField($contents);
464
-		$group .= $this->close();
465
-
466
-		return $group;
467
-	}
468
-
469
-	/**
470
-	 * Wraps content in a group with floating label
471
-	 *
472
-	 * @param string $contents The content
473
-	 * @param string $label    The label to add
474
-	 *
475
-	 * @return string A group
476
-	 */
477
-	public function wrapWithFloatingLabel($contents, $label = null)
478
-	{
479
-		$floatingLabelClass = $this->app['former.framework']->getFloatingLabelClass();
480
-		if ($floatingLabelClass) {
481
-			$this->addClass($floatingLabelClass);
482
-		}
483
-		return $this->wrap($label, $contents);
484
-	}
485
-
486
-	/**
487
-	 * Prints out the current label
488
-	 *
489
-	 * @param  string $field The field to create a label for
490
-	 *
491
-	 * @return string        A <label> tag
492
-	 */
493
-	 protected function getLabel($field = null)
494
- 	{
495
- 		// Don't create a label if none exist
496
- 		if (!$field or !$this->label) {
497
- 			return null;
498
- 		}
499
-
500
- 		// Wrap label in framework classes
501
- 		$labelClasses = $this->app['former.framework']->getLabelClasses();
502
- 		if ($field->isCheckable() &&
503
- 			in_array($this->app['former']->framework(), ['TwitterBootstrap4', 'TwitterBootstrap5']) &&
504
- 			$this->app['former.form']->isOfType('horizontal')
505
- 		) {
506
- 			$labelClasses = array_merge($labelClasses, array('pt-0'));
507
- 		}
508
- 		$this->label->addClass($labelClasses);
509
- 		$this->label = $this->app['former.framework']->createLabelOf($field, $this->label);
510
- 		$this->label = $this->app['former.framework']->wrapLabel($this->label);
511
-
512
- 		return $this->label;
513
- 	}
514
-
515
-	/**
516
-	 * Prints out the current help
517
-	 *
518
-	 * @return string A .help-block or .help-inline
519
-	 */
520
-	protected function getHelp()
521
-	{
522
-		$inline = Arr::get($this->help, 'inline');
523
-		$block  = Arr::get($this->help, 'block');
524
-
525
-		// Replace help text with error if any found
526
-		$errors = $this->app['former']->getErrors();
527
-		if ($errors and $this->app['former']->getOption('error_messages')) {
528
-			$inline = $this->app['former.framework']->createValidationError($errors);
529
-		}
530
-
531
-		return join(null, array($inline, $block));
532
-	}
533
-
534
-	/**
535
-	 * Format the field with prepended/appended elements
536
-	 *
537
-	 * @param  Field $field The field to format
538
-	 *
539
-	 * @return string        Field plus supplementary elements
540
-	 */
541
-	protected function prependAppend($field)
542
-	{
543
-		if (!$this->prepend and !$this->append) {
544
-			return $field->render();
545
-		}
546
-
547
-		return $this->app['former.framework']->prependAppend($field, $this->prepend, $this->append);
548
-	}
549
-
550
-	/**
551
-	 * Place elements around the field
552
-	 *
553
-	 * @param  array  $items An array of items to place
554
-	 * @param  string $place Where they should end up (prepend|append)
555
-	 */
556
-	protected function placeAround($items, $place)
557
-	{
558
-		// Iterate over the items and place them where they should
559
-		foreach ((array) $items as $item) {
560
-			$item             = $this->app['former.framework']->placeAround($item, $place);
561
-			$this->{$place}[] = $item;
562
-		}
563
-	}
16
+    /**
17
+     * The Container
18
+     *
19
+     * @var Container
20
+     */
21
+    protected $app;
22
+
23
+    /**
24
+     * The current state of the group
25
+     *
26
+     * @var string
27
+     */
28
+    protected $state = null;
29
+
30
+    /**
31
+     * Whether the field should be displayed raw or not
32
+     *
33
+     * @var boolean
34
+     */
35
+    protected $raw = false;
36
+
37
+    /**
38
+     * The group label
39
+     *
40
+     * @var Element
41
+     */
42
+    protected $label;
43
+
44
+    /**
45
+     * The group help
46
+     *
47
+     * @var array
48
+     */
49
+    protected $help = array();
50
+
51
+    /**
52
+     * An array of elements to preprend the field
53
+     *
54
+     * @var array
55
+     */
56
+    protected $prepend = array();
57
+
58
+    /**
59
+     * An array of elements to append the field
60
+     *
61
+     * @var array
62
+     */
63
+    protected $append = array();
64
+
65
+    /**
66
+     * The field validations to be checked for errors
67
+     *
68
+     * @var array
69
+     */
70
+    protected $validations = array();
71
+
72
+    /**
73
+     * The group's element
74
+     *
75
+     * @var string
76
+     */
77
+    protected $element = 'div';
78
+
79
+    /**
80
+     * Whether a custom group is opened or not
81
+     *
82
+     * @var boolean
83
+     */
84
+    public static $opened = false;
85
+
86
+    /**
87
+     * The custom group that is open
88
+     *
89
+     * @var Former\Form\Group
90
+     */
91
+    public static $openGroup = null;
92
+
93
+    ////////////////////////////////////////////////////////////////////
94
+    /////////////////////////// CORE METHODS ///////////////////////////
95
+    ////////////////////////////////////////////////////////////////////
96
+
97
+    /**
98
+     * Creates a group
99
+     *
100
+     * @param string $label Its label
101
+     */
102
+    public function __construct(Container $app, $label, $validations = null)
103
+    {
104
+        // Get special classes
105
+        $this->app = $app;
106
+        $this->addClass($this->app['former.framework']->getGroupClasses());
107
+
108
+        // Invisible if Nude
109
+        if ($this->app['former.framework']->is('Nude')) {
110
+            $this->element = '';
111
+        }
112
+
113
+        // Set group label
114
+        if ($label) {
115
+            $this->setLabel($label);
116
+        }
117
+
118
+        // Set validations used to override groups own conclusions
119
+        $this->validations = (array) $validations;
120
+    }
121
+
122
+    /**
123
+     * Prints out the opening of the Control Group
124
+     *
125
+     * @return string A control group opening tag
126
+     */
127
+    public function __toString()
128
+    {
129
+        return $this->open().$this->getFormattedLabel();
130
+    }
131
+
132
+    /**
133
+     * Opens a group
134
+     *
135
+     * @return string Opening tag
136
+     */
137
+    public function open()
138
+    {
139
+        if ($this->getErrors()) {
140
+            $this->state($this->app['former.framework']->errorState());
141
+        }
142
+
143
+        // Retrieve state and append it to classes
144
+        if ($this->state) {
145
+            $this->addClass($this->state);
146
+        }
147
+
148
+        // Required state
149
+        if ($this->app->bound('former.field') and $this->app['former.field']->isRequired()) {
150
+            $this->addClass($this->app['former']->getOption('required_class'));
151
+        }
152
+
153
+        return parent::open();
154
+    }
155
+
156
+    /**
157
+     * Set the contents of the current group
158
+     *
159
+     * @param string $contents The group contents
160
+     *
161
+     * @return string A group
162
+     */
163
+    public function contents($contents)
164
+    {
165
+        return $this->wrap($contents, $this->getFormattedLabel());
166
+    }
167
+
168
+    /**
169
+     * Wrap a Field with the current group
170
+     *
171
+     * @param  \Former\Traits\Field $field A Field instance
172
+     *
173
+     * @return string        A group
174
+     */
175
+    public function wrapField($field)
176
+    {
177
+        $label = $this->getLabel($field);
178
+        $help = $this->getHelp();
179
+        if ($field->isCheckable() &&
180
+            $this->app['former']->framework() === 'TwitterBootstrap4'
181
+        ) {
182
+            $wrapperClass = $field->isInline() ? 'form-check form-check-inline' : 'form-check';
183
+            if ($this->app['former']->getErrors($field->getName())) {
184
+                $hiddenInput = Element::create('input', null, ['type' => 'hidden'])->class('form-check-input is-invalid');
185
+                $help = $hiddenInput.$help;
186
+            }
187
+            $help = Element::create('div', $help)->class($wrapperClass);
188
+        }
189
+        $withFloatingLabel = $field->withFloatingLabel();
190
+
191
+        $field = $this->prependAppend($field);
192
+        $field .= $help;
193
+
194
+        if ($withFloatingLabel &&
195
+            $this->app['former']->framework() === 'TwitterBootstrap5'
196
+        ) {
197
+            return $this->wrapWithFloatingLabel($field, $label);
198
+        }
199
+
200
+        return $this->wrap($field, $label);
201
+    }
202
+
203
+    ////////////////////////////////////////////////////////////////////
204
+    //////////////////////////// FIELD METHODS /////////////////////////
205
+    ////////////////////////////////////////////////////////////////////
206
+
207
+    /**
208
+     * Set the state of the group
209
+     *
210
+     * @param  string $state A Bootstrap state class
211
+     */
212
+    public function state($state)
213
+    {
214
+        // Filter state
215
+        $state = $this->app['former.framework']->filterState($state);
216
+
217
+        $this->state = $state;
218
+    }
219
+
220
+    /**
221
+     * Set a class on the Group
222
+     *
223
+     * @param string $class The class(es) to add on the Group
224
+     */
225
+    public function addGroupClass($class)
226
+    {
227
+        $this->addClass($class);
228
+    }
229
+
230
+    /**
231
+     * Remove one or more classes on the Group
232
+     *
233
+     * @param string $class The class(es) to remove on the Group
234
+     */
235
+    public function removeGroupClass($class)
236
+    {
237
+        $this->removeClass($class);
238
+    }
239
+
240
+    /**
241
+     * Set a class on the Label
242
+     *
243
+     * @param string $class The class(es) to add on the Label
244
+     */
245
+    public function addLabelClass($class)
246
+    {
247
+        // Don't add a label class if it isn't an Element instance
248
+        if (!$this->label instanceof Element) {
249
+            return $this;
250
+        }
251
+
252
+        $this->label->addClass($class);
253
+
254
+        return $this;
255
+    }
256
+
257
+    /**
258
+     * Remove one or more classes on the Label
259
+     *
260
+     * @param string $class The class(es) to remove on the Label
261
+     */
262
+    public function removeLabelClass($class)
263
+    {
264
+        // Don't remove a label class if it isn't an Element instance
265
+        if (!$this->label instanceof Element) {
266
+            return $this;
267
+        }
268
+
269
+        $this->label->removeClass($class);
270
+
271
+        return $this;
272
+    }
273
+
274
+    /**
275
+     * Adds a label to the group
276
+     *
277
+     * @param  string $label A label
278
+     */
279
+    public function setLabel($label)
280
+    {
281
+        if (!$label instanceof Element) {
282
+            $label = Helpers::translate($label);
283
+            $label = Element::create('label', $label)->for($label);
284
+        }
285
+
286
+        $this->label = $label;
287
+    }
288
+
289
+    /**
290
+     * Get the formatted group label
291
+     *
292
+     * @return string|null
293
+     */
294
+    public function getFormattedLabel()
295
+    {
296
+        if (!$this->label) {
297
+            return false;
298
+        }
299
+
300
+        return $this->label->addClass($this->app['former.framework']->getLabelClasses());
301
+    }
302
+
303
+    /**
304
+     * Disables the control group for the current field
305
+     */
306
+    public function raw()
307
+    {
308
+        $this->raw = true;
309
+    }
310
+
311
+    /**
312
+     * Check if the current group is to be displayed or not
313
+     *
314
+     * @return boolean
315
+     */
316
+    public function isRaw()
317
+    {
318
+        return (bool) $this->raw;
319
+    }
320
+
321
+    ////////////////////////////////////////////////////////////////////
322
+    ///////////////////////////// HELP BLOCKS //////////////////////////
323
+    ////////////////////////////////////////////////////////////////////
324
+
325
+    /**
326
+     * Alias for inlineHelp
327
+     *
328
+     * @param  string $help       The help text
329
+     * @param  array  $attributes Facultative attributes
330
+     */
331
+    public function help($help, $attributes = array())
332
+    {
333
+        return $this->inlineHelp($help, $attributes);
334
+    }
335
+
336
+    /**
337
+     * Add an inline help
338
+     *
339
+     * @param  string $help       The help text
340
+     * @param  array  $attributes Facultative attributes
341
+     */
342
+    public function inlineHelp($help, $attributes = array())
343
+    {
344
+        // If no help text, do nothing
345
+        if (!$help) {
346
+            return false;
347
+        }
348
+
349
+        $this->help['inline'] = $this->app['former.framework']->createHelp($help, $attributes);
350
+    }
351
+
352
+    /**
353
+     * Add an block help
354
+     *
355
+     * @param  string $help       The help text
356
+     * @param  array  $attributes Facultative attributes
357
+     */
358
+    public function blockHelp($help, $attributes = array())
359
+    {
360
+        // Reserved method
361
+        if ($this->app['former.framework']->isnt('TwitterBootstrap') &&
362
+            $this->app['former.framework']->isnt('TwitterBootstrap3') &&
363
+            $this->app['former.framework']->isnt('TwitterBootstrap4') &&
364
+            $this->app['former.framework']->isnt('TwitterBootstrap5')
365
+        ) {
366
+            throw new BadMethodCallException('This method is only available on the Bootstrap framework');
367
+        }
368
+
369
+        // If no help text, do nothing
370
+        if (!$help) {
371
+            return false;
372
+        }
373
+
374
+        $this->help['block'] = $this->app['former.framework']->createBlockHelp($help, $attributes);
375
+    }
376
+
377
+    ////////////////////////////////////////////////////////////////////
378
+    ///////////////////////// PREPEND/APPEND METHODS ///////////////////
379
+    ////////////////////////////////////////////////////////////////////
380
+
381
+    /**
382
+     * Prepend elements to the field
383
+     */
384
+    public function prepend()
385
+    {
386
+        $this->placeAround(func_get_args(), 'prepend');
387
+    }
388
+
389
+    /**
390
+     * Append elements to the field
391
+     */
392
+    public function append()
393
+    {
394
+        $this->placeAround(func_get_args(), 'append');
395
+    }
396
+
397
+    /**
398
+     * Prepends an icon to a field
399
+     *
400
+     * @param string $icon       The icon to prepend
401
+     * @param array  $attributes Its attributes
402
+     */
403
+    public function prependIcon($icon, $attributes = array(), $iconSettings = array())
404
+    {
405
+        $icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
406
+
407
+        $this->prepend($icon);
408
+    }
409
+
410
+    /**
411
+     * Append an icon to a field
412
+     *
413
+     * @param string $icon       The icon to prepend
414
+     * @param array  $attributes Its attributes
415
+     */
416
+    public function appendIcon($icon, $attributes = array(), $iconSettings = array())
417
+    {
418
+        $icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
419
+
420
+        $this->append($icon);
421
+    }
422
+
423
+    ////////////////////////////////////////////////////////////////////
424
+    //////////////////////////////// HELPERS ///////////////////////////
425
+    ////////////////////////////////////////////////////////////////////
426
+
427
+    /**
428
+     * Get the errors for the group
429
+     *
430
+     * @return string
431
+     */
432
+    public function getErrors()
433
+    {
434
+        $errors = '';
435
+
436
+        if (!self::$opened) {
437
+
438
+            // for non-custom groups, normal error handling applies
439
+            $errors = $this->app['former']->getErrors();
440
+        } elseif (!empty($this->validations)) {
441
+
442
+            // error handling only when validations specified for custom groups
443
+            foreach ($this->validations as $validation) {
444
+                $errors .= $this->app['former']->getErrors($validation);
445
+            }
446
+        }
447
+
448
+        return $errors;
449
+    }
450
+
451
+    /**
452
+     * Wraps content in a group
453
+     *
454
+     * @param string $contents The content
455
+     * @param string $label    The label to add
456
+     *
457
+     * @return string A group
458
+     */
459
+    public function wrap($contents, $label = null)
460
+    {
461
+        $group = $this->open();
462
+        $group .= $label;
463
+        $group .= $this->app['former.framework']->wrapField($contents);
464
+        $group .= $this->close();
465
+
466
+        return $group;
467
+    }
468
+
469
+    /**
470
+     * Wraps content in a group with floating label
471
+     *
472
+     * @param string $contents The content
473
+     * @param string $label    The label to add
474
+     *
475
+     * @return string A group
476
+     */
477
+    public function wrapWithFloatingLabel($contents, $label = null)
478
+    {
479
+        $floatingLabelClass = $this->app['former.framework']->getFloatingLabelClass();
480
+        if ($floatingLabelClass) {
481
+            $this->addClass($floatingLabelClass);
482
+        }
483
+        return $this->wrap($label, $contents);
484
+    }
485
+
486
+    /**
487
+     * Prints out the current label
488
+     *
489
+     * @param  string $field The field to create a label for
490
+     *
491
+     * @return string        A <label> tag
492
+     */
493
+        protected function getLabel($field = null)
494
+        {
495
+            // Don't create a label if none exist
496
+            if (!$field or !$this->label) {
497
+                return null;
498
+            }
499
+
500
+            // Wrap label in framework classes
501
+            $labelClasses = $this->app['former.framework']->getLabelClasses();
502
+            if ($field->isCheckable() &&
503
+             in_array($this->app['former']->framework(), ['TwitterBootstrap4', 'TwitterBootstrap5']) &&
504
+             $this->app['former.form']->isOfType('horizontal')
505
+            ) {
506
+                $labelClasses = array_merge($labelClasses, array('pt-0'));
507
+            }
508
+            $this->label->addClass($labelClasses);
509
+            $this->label = $this->app['former.framework']->createLabelOf($field, $this->label);
510
+            $this->label = $this->app['former.framework']->wrapLabel($this->label);
511
+
512
+            return $this->label;
513
+        }
514
+
515
+    /**
516
+     * Prints out the current help
517
+     *
518
+     * @return string A .help-block or .help-inline
519
+     */
520
+    protected function getHelp()
521
+    {
522
+        $inline = Arr::get($this->help, 'inline');
523
+        $block  = Arr::get($this->help, 'block');
524
+
525
+        // Replace help text with error if any found
526
+        $errors = $this->app['former']->getErrors();
527
+        if ($errors and $this->app['former']->getOption('error_messages')) {
528
+            $inline = $this->app['former.framework']->createValidationError($errors);
529
+        }
530
+
531
+        return join(null, array($inline, $block));
532
+    }
533
+
534
+    /**
535
+     * Format the field with prepended/appended elements
536
+     *
537
+     * @param  Field $field The field to format
538
+     *
539
+     * @return string        Field plus supplementary elements
540
+     */
541
+    protected function prependAppend($field)
542
+    {
543
+        if (!$this->prepend and !$this->append) {
544
+            return $field->render();
545
+        }
546
+
547
+        return $this->app['former.framework']->prependAppend($field, $this->prepend, $this->append);
548
+    }
549
+
550
+    /**
551
+     * Place elements around the field
552
+     *
553
+     * @param  array  $items An array of items to place
554
+     * @param  string $place Where they should end up (prepend|append)
555
+     */
556
+    protected function placeAround($items, $place)
557
+    {
558
+        // Iterate over the items and place them where they should
559
+        foreach ((array) $items as $item) {
560
+            $item             = $this->app['former.framework']->placeAround($item, $place);
561
+            $this->{$place}[] = $item;
562
+        }
563
+    }
564 564
 }
Please login to merge, or discard this patch.
src/Former/Traits/Field.php 1 patch
Indentation   +433 added lines, -433 removed lines patch added patch discarded remove patch
@@ -16,281 +16,281 @@  discard block
 block discarded – undo
16 16
  */
17 17
 abstract class Field extends FormerObject implements FieldInterface
18 18
 {
19
-	/**
20
-	 * The IoC Container
21
-	 *
22
-	 * @var Container
23
-	 */
24
-	protected $app;
25
-
26
-	/**
27
-	 * The Form instance
28
-	 *
29
-	 * @var Former\Form
30
-	 */
31
-	protected $form;
32
-
33
-	/**
34
-	 * A label for the field (if not using Bootstrap)
35
-	 *
36
-	 * @var string
37
-	 */
38
-	protected $label;
39
-
40
-	/**
41
-	 * The field's group
42
-	 *
43
-	 * @var Group
44
-	 */
45
-	protected $group;
46
-
47
-	/**
48
-	 * The field's default element
49
-	 *
50
-	 * @var string
51
-	 */
52
-	protected $element = 'input';
53
-
54
-	/**
55
-	 * Whether the Field is self-closing or not
56
-	 *
57
-	 * @var boolean
58
-	 */
59
-	protected $isSelfClosing = true;
60
-
61
-	/**
62
-	 * The field's bind destination
63
-	 *
64
-	 * @var string
65
-	 */
66
-	protected $bind;
67
-
68
-	/**
69
-	 * Renders with floating label
70
-	 *
71
-	 * @var boolean
72
-	 */
73
-	protected $floatingLabel = false;
74
-
75
-	/**
76
-	 * Get the current framework instance
77
-	 *
78
-	 * @return Framework
79
-	 */
80
-	protected function currentFramework()
81
-	{
82
-		if ($this->app->bound('former.form.framework')) {
83
-			return $this->app['former.form.framework'];
84
-		}
85
-
86
-		return $this->app['former.framework'];
87
-	}
88
-
89
-	////////////////////////////////////////////////////////////////////
90
-	///////////////////////////// INTERFACE ////////////////////////////
91
-	////////////////////////////////////////////////////////////////////
92
-
93
-	/**
94
-	 * Set up a Field instance
95
-	 *
96
-	 * @param string $type A field type
97
-	 */
98
-	public function __construct(Container $app, $type, $name, $label, $value, $attributes)
99
-	{
100
-		// Set base parameters
101
-		$this->app   = $app;
102
-		$this->type  = $type;
103
-		$this->value = $value;
104
-		$this->setAttributes($attributes);
105
-		$this->form = $this->app->bound('former.form') ? $this->app['former.form'] : null;
106
-
107
-		// Compute and translate label
108
-		$this->automaticLabels($name, $label);
109
-
110
-		// Repopulate field
111
-		if ($type != 'password' && $name !== '_token') {
112
-			$this->value = $this->repopulate();
113
-		}
114
-
115
-		// Apply Live validation rules
116
-		if ($this->app['former']->getOption('live_validation')) {
117
-			$rules = new LiveValidation($this);
118
-			$rules->apply($this->getRules());
119
-		}
120
-
121
-		// Bind the Group class
122
-		$groupClass = $this->isCheckable() ? 'CheckableGroup' : 'Group';
123
-		$groupClass = Former::FORMSPACE.$groupClass;
124
-
125
-		$this->group = new $groupClass($this->app, $this->label);
126
-	}
127
-
128
-	/**
129
-	 * Redirect calls to the group if necessary
130
-	 *
131
-	 * @param string $method
132
-	 */
133
-	public function __call($method, $parameters)
134
-	{
135
-		// Translate attributes
136
-		$translatable = $this->app['former']->getOption('translatable', array());
137
-		if (in_array($method, $translatable) and isset($parameters[0])) {
138
-			$parameters[0] = Helpers::translate($parameters[0]);
139
-		}
140
-
141
-		// Redirect calls to the Control Group
142
-		if ((!empty($this->group) && method_exists($this->group, $method)) or Str::startsWith($method, 'onGroup')) {
143
-			$method = str_replace('onGroup', '', $method);
144
-			$method = lcfirst($method);
145
-
146
-			call_user_func_array(array($this->group, $method), $parameters);
147
-
148
-			return $this;
149
-		}
150
-
151
-		return parent::__call($method, $parameters);
152
-	}
153
-
154
-	/**
155
-	 * Prints out the field, wrapped in its group
156
-	 *
157
-	 * @return string
158
-	 */
159
-	public function wrapAndRender()
160
-	{
161
-		// Dry syntax (hidden fields, plain fields)
162
-		if ($this->isUnwrappable()) {
163
-			$html = $this->render();
164
-			// Control group syntax
165
-		} elseif (Form::hasInstanceOpened()) {
166
-			$html = $this->group->wrapField($this);
167
-			// Classic syntax
168
-		} else {
169
-			$html = $this->currentFramework()->createLabelOf($this);
170
-			$html .= $this->render();
171
-		}
172
-
173
-		return $html;
174
-	}
175
-
176
-	/**
177
-	 * Prints out the field
178
-	 *
179
-	 * @return string
180
-	 */
181
-	public function __toString()
182
-	{
183
-		return $this->wrapAndRender();
184
-	}
185
-
186
-	////////////////////////////////////////////////////////////////////
187
-	////////////////////////// PUBLIC INTERFACE ////////////////////////
188
-	////////////////////////////////////////////////////////////////////
189
-
190
-	/**
191
-	 * Whether the current field is required or not
192
-	 *
193
-	 * @return boolean
194
-	 */
195
-	public function isRequired()
196
-	{
197
-		return isset($this->attributes['required']);
198
-	}
199
-
200
-	/**
201
-	 * Set required live validation attribute
202
-	 *
203
-	 * @param boolean $isRequired
204
-	 * @return $this
205
-	 */
206
-	public function required($isRequired=true)
207
-	{
208
-		if ($isRequired) {
209
-			$this->attributes['required'] = true;
210
-		} else {
211
-			unset($this->attributes['required']);
212
-		}
213
-
214
-		return $this;
215
-	}
216
-
217
-	/**
218
-	 * Check if a field is unwrappable (no label)
219
-	 *
220
-	 * @return boolean
221
-	 */
222
-	public function isUnwrappable()
223
-	{
224
-		return
225
-			($this->form and $this->currentFramework()->is('Nude')) or
226
-			($this->form and $this->isOfType('inline')) or
227
-			$this->isButton() or
228
-			$this->isOfType('hidden') or
229
-			\Former\Form\Group::$opened or
230
-			$this->group and $this->group->isRaw();
231
-	}
232
-
233
-	/**
234
-	 * Check if field is a checkbox or a radio
235
-	 *
236
-	 * @return boolean
237
-	 */
238
-	public function isCheckable()
239
-	{
240
-		return $this->isOfType('checkbox', 'checkboxes', 'radio', 'radios', 'switch', 'switches');
241
-	}
242
-
243
-	/**
244
-	 * Check if the field is a button
245
-	 *
246
-	 * @return boolean
247
-	 */
248
-	public function isButton()
249
-	{
250
-		return false;
251
-	}
252
-
253
-	/**
254
-	 * Check if the field get a floating label
255
-	 *
256
-	 * @return boolean
257
-	 */
258
-	public function withFloatingLabel()
259
-	{
260
-		return $this->floatingLabel;
261
-	}
262
-
263
-	/**
264
-	 * Get the rules applied to the current field
265
-	 *
266
-	 * @return array An array of rules
267
-	 */
268
-	public function getRules()
269
-	{
270
-		return $this->app['former']->getRules($this->name);
271
-	}
272
-
273
-	////////////////////////////////////////////////////////////////////
274
-	//////////////////////// SETTERS AND GETTERS ///////////////////////
275
-	////////////////////////////////////////////////////////////////////
276
-
277
-	/**
278
-	 * Apply a Live Validation rule by chaining
279
-	 *
280
-	 * @param string $rule The rule
281
-	 */
282
-	public function rule($rule)
283
-	{
284
-		$parameters = func_get_args();
285
-		array_shift($parameters);
286
-
287
-		$live = new LiveValidation($this);
288
-		$live->apply(array(
289
-			$rule => $parameters,
290
-		));
291
-
292
-		return $this;
293
-	}
19
+    /**
20
+     * The IoC Container
21
+     *
22
+     * @var Container
23
+     */
24
+    protected $app;
25
+
26
+    /**
27
+     * The Form instance
28
+     *
29
+     * @var Former\Form
30
+     */
31
+    protected $form;
32
+
33
+    /**
34
+     * A label for the field (if not using Bootstrap)
35
+     *
36
+     * @var string
37
+     */
38
+    protected $label;
39
+
40
+    /**
41
+     * The field's group
42
+     *
43
+     * @var Group
44
+     */
45
+    protected $group;
46
+
47
+    /**
48
+     * The field's default element
49
+     *
50
+     * @var string
51
+     */
52
+    protected $element = 'input';
53
+
54
+    /**
55
+     * Whether the Field is self-closing or not
56
+     *
57
+     * @var boolean
58
+     */
59
+    protected $isSelfClosing = true;
60
+
61
+    /**
62
+     * The field's bind destination
63
+     *
64
+     * @var string
65
+     */
66
+    protected $bind;
67
+
68
+    /**
69
+     * Renders with floating label
70
+     *
71
+     * @var boolean
72
+     */
73
+    protected $floatingLabel = false;
74
+
75
+    /**
76
+     * Get the current framework instance
77
+     *
78
+     * @return Framework
79
+     */
80
+    protected function currentFramework()
81
+    {
82
+        if ($this->app->bound('former.form.framework')) {
83
+            return $this->app['former.form.framework'];
84
+        }
85
+
86
+        return $this->app['former.framework'];
87
+    }
88
+
89
+    ////////////////////////////////////////////////////////////////////
90
+    ///////////////////////////// INTERFACE ////////////////////////////
91
+    ////////////////////////////////////////////////////////////////////
92
+
93
+    /**
94
+     * Set up a Field instance
95
+     *
96
+     * @param string $type A field type
97
+     */
98
+    public function __construct(Container $app, $type, $name, $label, $value, $attributes)
99
+    {
100
+        // Set base parameters
101
+        $this->app   = $app;
102
+        $this->type  = $type;
103
+        $this->value = $value;
104
+        $this->setAttributes($attributes);
105
+        $this->form = $this->app->bound('former.form') ? $this->app['former.form'] : null;
106
+
107
+        // Compute and translate label
108
+        $this->automaticLabels($name, $label);
109
+
110
+        // Repopulate field
111
+        if ($type != 'password' && $name !== '_token') {
112
+            $this->value = $this->repopulate();
113
+        }
114
+
115
+        // Apply Live validation rules
116
+        if ($this->app['former']->getOption('live_validation')) {
117
+            $rules = new LiveValidation($this);
118
+            $rules->apply($this->getRules());
119
+        }
120
+
121
+        // Bind the Group class
122
+        $groupClass = $this->isCheckable() ? 'CheckableGroup' : 'Group';
123
+        $groupClass = Former::FORMSPACE.$groupClass;
124
+
125
+        $this->group = new $groupClass($this->app, $this->label);
126
+    }
127
+
128
+    /**
129
+     * Redirect calls to the group if necessary
130
+     *
131
+     * @param string $method
132
+     */
133
+    public function __call($method, $parameters)
134
+    {
135
+        // Translate attributes
136
+        $translatable = $this->app['former']->getOption('translatable', array());
137
+        if (in_array($method, $translatable) and isset($parameters[0])) {
138
+            $parameters[0] = Helpers::translate($parameters[0]);
139
+        }
140
+
141
+        // Redirect calls to the Control Group
142
+        if ((!empty($this->group) && method_exists($this->group, $method)) or Str::startsWith($method, 'onGroup')) {
143
+            $method = str_replace('onGroup', '', $method);
144
+            $method = lcfirst($method);
145
+
146
+            call_user_func_array(array($this->group, $method), $parameters);
147
+
148
+            return $this;
149
+        }
150
+
151
+        return parent::__call($method, $parameters);
152
+    }
153
+
154
+    /**
155
+     * Prints out the field, wrapped in its group
156
+     *
157
+     * @return string
158
+     */
159
+    public function wrapAndRender()
160
+    {
161
+        // Dry syntax (hidden fields, plain fields)
162
+        if ($this->isUnwrappable()) {
163
+            $html = $this->render();
164
+            // Control group syntax
165
+        } elseif (Form::hasInstanceOpened()) {
166
+            $html = $this->group->wrapField($this);
167
+            // Classic syntax
168
+        } else {
169
+            $html = $this->currentFramework()->createLabelOf($this);
170
+            $html .= $this->render();
171
+        }
172
+
173
+        return $html;
174
+    }
175
+
176
+    /**
177
+     * Prints out the field
178
+     *
179
+     * @return string
180
+     */
181
+    public function __toString()
182
+    {
183
+        return $this->wrapAndRender();
184
+    }
185
+
186
+    ////////////////////////////////////////////////////////////////////
187
+    ////////////////////////// PUBLIC INTERFACE ////////////////////////
188
+    ////////////////////////////////////////////////////////////////////
189
+
190
+    /**
191
+     * Whether the current field is required or not
192
+     *
193
+     * @return boolean
194
+     */
195
+    public function isRequired()
196
+    {
197
+        return isset($this->attributes['required']);
198
+    }
199
+
200
+    /**
201
+     * Set required live validation attribute
202
+     *
203
+     * @param boolean $isRequired
204
+     * @return $this
205
+     */
206
+    public function required($isRequired=true)
207
+    {
208
+        if ($isRequired) {
209
+            $this->attributes['required'] = true;
210
+        } else {
211
+            unset($this->attributes['required']);
212
+        }
213
+
214
+        return $this;
215
+    }
216
+
217
+    /**
218
+     * Check if a field is unwrappable (no label)
219
+     *
220
+     * @return boolean
221
+     */
222
+    public function isUnwrappable()
223
+    {
224
+        return
225
+            ($this->form and $this->currentFramework()->is('Nude')) or
226
+            ($this->form and $this->isOfType('inline')) or
227
+            $this->isButton() or
228
+            $this->isOfType('hidden') or
229
+            \Former\Form\Group::$opened or
230
+            $this->group and $this->group->isRaw();
231
+    }
232
+
233
+    /**
234
+     * Check if field is a checkbox or a radio
235
+     *
236
+     * @return boolean
237
+     */
238
+    public function isCheckable()
239
+    {
240
+        return $this->isOfType('checkbox', 'checkboxes', 'radio', 'radios', 'switch', 'switches');
241
+    }
242
+
243
+    /**
244
+     * Check if the field is a button
245
+     *
246
+     * @return boolean
247
+     */
248
+    public function isButton()
249
+    {
250
+        return false;
251
+    }
252
+
253
+    /**
254
+     * Check if the field get a floating label
255
+     *
256
+     * @return boolean
257
+     */
258
+    public function withFloatingLabel()
259
+    {
260
+        return $this->floatingLabel;
261
+    }
262
+
263
+    /**
264
+     * Get the rules applied to the current field
265
+     *
266
+     * @return array An array of rules
267
+     */
268
+    public function getRules()
269
+    {
270
+        return $this->app['former']->getRules($this->name);
271
+    }
272
+
273
+    ////////////////////////////////////////////////////////////////////
274
+    //////////////////////// SETTERS AND GETTERS ///////////////////////
275
+    ////////////////////////////////////////////////////////////////////
276
+
277
+    /**
278
+     * Apply a Live Validation rule by chaining
279
+     *
280
+     * @param string $rule The rule
281
+     */
282
+    public function rule($rule)
283
+    {
284
+        $parameters = func_get_args();
285
+        array_shift($parameters);
286
+
287
+        $live = new LiveValidation($this);
288
+        $live->apply(array(
289
+            $rule => $parameters,
290
+        ));
291
+
292
+        return $this;
293
+    }
294 294
 
295 295
     /**
296 296
      * Apply multiple rules passed as a string.
@@ -312,9 +312,9 @@  discard block
 block discarded – undo
312 312
                 $rulename = substr($rule, 0, $colon) ;
313 313
 
314 314
                 /**
315
-                * Regular expressions may contain commas and should not be divided by str_getcsv.
316
-                * For regular expressions we are just using the complete expression as a parameter.
317
-                */
315
+                 * Regular expressions may contain commas and should not be divided by str_getcsv.
316
+                 * For regular expressions we are just using the complete expression as a parameter.
317
+                 */
318 318
                 if ($rulename !== 'regex') {
319 319
                     $parameters = str_getcsv(substr($rule, $colon + 1));
320 320
                 } else {
@@ -336,159 +336,159 @@  discard block
 block discarded – undo
336 336
         return $this;
337 337
     }
338 338
 
339
-	/**
340
-	 * Adds a label to the group/field
341
-	 *
342
-	 * @param  string $text       A label
343
-	 * @param  array  $attributes The label's attributes
344
-	 *
345
-	 * @return Field              A field
346
-	 */
347
-	public function label($text, $attributes = array())
348
-	{
349
-		// Create the Label element
350
-		$for   = $this->id ?: $this->name;
351
-		$label = $this->app['former']->label($text, $for, $attributes);
352
-
353
-		// Set label
354
-		$this->label = $label;
355
-		if ($this->group) {
356
-			$this->group->setLabel($label);
357
-		}
358
-
359
-		return $this;
360
-	}
361
-
362
-	/**
363
-	 * Set the Field value no matter what
364
-	 *
365
-	 * @param string $value A new value
366
-	 */
367
-	public function forceValue($value)
368
-	{
369
-		$this->value = $value;
370
-
371
-		return $this;
372
-	}
373
-
374
-	/**
375
-	 * Classic setting of attribute, won't overwrite any populate() attempt
376
-	 *
377
-	 * @param  string $value A new value
378
-	 */
379
-	public function value($value)
380
-	{
381
-		// Check if we already have a value stored for this field or in POST data
382
-		$already = $this->repopulate();
383
-
384
-		if (!$already) {
385
-			$this->value = $value;
386
-		}
387
-
388
-		return $this;
389
-	}
390
-
391
-	/**
392
-	 * Change the field's name
393
-	 *
394
-	 * @param  string $name The new name
395
-	 */
396
-	public function name($name)
397
-	{
398
-		$this->name = $name;
399
-
400
-		// Also relink the label to the new name
401
-		$this->label($name);
402
-
403
-		return $this;
404
-	}
405
-
406
-	/**
407
-	 * Set the field as floating label
408
-	 */
409
-	public function floatingLabel($isFloatingLabel = true)
410
-	{
411
-		$this->floatingLabel = $isFloatingLabel;
412
-
413
-		return $this;
414
-	}
415
-
416
-	/**
417
-	 * Get the field's labels
418
-	 *
419
-	 * @return string
420
-	 */
421
-	public function getLabel()
422
-	{
423
-		return $this->label;
424
-	}
425
-
426
-	/**
427
-	 * Change the field's bind destination
428
-	 *
429
-	 * @param $destination
430
-	 */
431
-	public function bind($destination) {
432
-		$this->bind = $destination;
433
-		if ($this->type != 'password') {
434
-			$this->value = $this->repopulate();
435
-		}
436
-
437
-		return $this;
438
-	}
439
-
440
-	////////////////////////////////////////////////////////////////////
441
-	//////////////////////////////// HELPERS ///////////////////////////
442
-	////////////////////////////////////////////////////////////////////
443
-
444
-	/**
445
-	 * Use values stored in Former to populate the current field
446
-	 */
447
-	private function repopulate($fallback = null)
448
-	{
449
-		// Get values from POST, populated, and manually set value
450
-		$post      = $this->app['former']->getPost($this->name);
451
-		$populator = $this->form ? $this->form->getPopulator() : $this->app['former.populator'];
452
-		$populate  = $populator->get($this->bind ?: $this->name);
453
-
454
-		// Assign a priority to each
455
-		if (!is_null($post)) {
456
-			return $post;
457
-		}
458
-		if (!is_null($populate)) {
459
-			return $populate;
460
-		}
461
-
462
-		return $fallback ?: $this->value;
463
-	}
464
-
465
-	/**
466
-	 * Ponders a label and a field name, and tries to get the best out of it
467
-	 *
468
-	 * @param  string $label A label
469
-	 * @param  string $name  A field name
470
-	 *
471
-	 * @return false|null         A label and a field name
472
-	 */
473
-	private function automaticLabels($name, $label)
474
-	{
475
-		// Disabled automatic labels
476
-		if (!$this->app['former']->getOption('automatic_label')) {
477
-			$this->name = $name;
478
-			$this->label($label);
479
-
480
-			return false;
481
-		}
482
-
483
-		// Check for the two possibilities
484
-		if ($label and is_null($name)) {
485
-			$name = Str::slug($label);
486
-		} elseif (is_null($label) and $name) {
487
-			$label = preg_replace('/\[\]$/', '', $name);
488
-		}
489
-
490
-		// Save values
491
-		$this->name = $name;
492
-		$this->label($label);
493
-	}
339
+    /**
340
+     * Adds a label to the group/field
341
+     *
342
+     * @param  string $text       A label
343
+     * @param  array  $attributes The label's attributes
344
+     *
345
+     * @return Field              A field
346
+     */
347
+    public function label($text, $attributes = array())
348
+    {
349
+        // Create the Label element
350
+        $for   = $this->id ?: $this->name;
351
+        $label = $this->app['former']->label($text, $for, $attributes);
352
+
353
+        // Set label
354
+        $this->label = $label;
355
+        if ($this->group) {
356
+            $this->group->setLabel($label);
357
+        }
358
+
359
+        return $this;
360
+    }
361
+
362
+    /**
363
+     * Set the Field value no matter what
364
+     *
365
+     * @param string $value A new value
366
+     */
367
+    public function forceValue($value)
368
+    {
369
+        $this->value = $value;
370
+
371
+        return $this;
372
+    }
373
+
374
+    /**
375
+     * Classic setting of attribute, won't overwrite any populate() attempt
376
+     *
377
+     * @param  string $value A new value
378
+     */
379
+    public function value($value)
380
+    {
381
+        // Check if we already have a value stored for this field or in POST data
382
+        $already = $this->repopulate();
383
+
384
+        if (!$already) {
385
+            $this->value = $value;
386
+        }
387
+
388
+        return $this;
389
+    }
390
+
391
+    /**
392
+     * Change the field's name
393
+     *
394
+     * @param  string $name The new name
395
+     */
396
+    public function name($name)
397
+    {
398
+        $this->name = $name;
399
+
400
+        // Also relink the label to the new name
401
+        $this->label($name);
402
+
403
+        return $this;
404
+    }
405
+
406
+    /**
407
+     * Set the field as floating label
408
+     */
409
+    public function floatingLabel($isFloatingLabel = true)
410
+    {
411
+        $this->floatingLabel = $isFloatingLabel;
412
+
413
+        return $this;
414
+    }
415
+
416
+    /**
417
+     * Get the field's labels
418
+     *
419
+     * @return string
420
+     */
421
+    public function getLabel()
422
+    {
423
+        return $this->label;
424
+    }
425
+
426
+    /**
427
+     * Change the field's bind destination
428
+     *
429
+     * @param $destination
430
+     */
431
+    public function bind($destination) {
432
+        $this->bind = $destination;
433
+        if ($this->type != 'password') {
434
+            $this->value = $this->repopulate();
435
+        }
436
+
437
+        return $this;
438
+    }
439
+
440
+    ////////////////////////////////////////////////////////////////////
441
+    //////////////////////////////// HELPERS ///////////////////////////
442
+    ////////////////////////////////////////////////////////////////////
443
+
444
+    /**
445
+     * Use values stored in Former to populate the current field
446
+     */
447
+    private function repopulate($fallback = null)
448
+    {
449
+        // Get values from POST, populated, and manually set value
450
+        $post      = $this->app['former']->getPost($this->name);
451
+        $populator = $this->form ? $this->form->getPopulator() : $this->app['former.populator'];
452
+        $populate  = $populator->get($this->bind ?: $this->name);
453
+
454
+        // Assign a priority to each
455
+        if (!is_null($post)) {
456
+            return $post;
457
+        }
458
+        if (!is_null($populate)) {
459
+            return $populate;
460
+        }
461
+
462
+        return $fallback ?: $this->value;
463
+    }
464
+
465
+    /**
466
+     * Ponders a label and a field name, and tries to get the best out of it
467
+     *
468
+     * @param  string $label A label
469
+     * @param  string $name  A field name
470
+     *
471
+     * @return false|null         A label and a field name
472
+     */
473
+    private function automaticLabels($name, $label)
474
+    {
475
+        // Disabled automatic labels
476
+        if (!$this->app['former']->getOption('automatic_label')) {
477
+            $this->name = $name;
478
+            $this->label($label);
479
+
480
+            return false;
481
+        }
482
+
483
+        // Check for the two possibilities
484
+        if ($label and is_null($name)) {
485
+            $name = Str::slug($label);
486
+        } elseif (is_null($label) and $name) {
487
+            $label = preg_replace('/\[\]$/', '', $name);
488
+        }
489
+
490
+        // Save values
491
+        $this->name = $name;
492
+        $this->label($label);
493
+    }
494 494
 }
Please login to merge, or discard this patch.