Completed
Pull Request — master (#604)
by Tortue
01:40
created

Group::wrapField()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.3888
c 0
b 0
f 0
cc 5
nc 5
nop 1
1
<?php
2
namespace Former\Form;
3
4
use BadMethodCallException;
5
use Former\Helpers;
6
use HtmlObject\Element;
7
use HtmlObject\Traits\Tag;
8
use Illuminate\Container\Container;
9
use Illuminate\Support\Arr;
10
11
/**
12
 * Helper class to build groups
13
 */
14
class Group extends Tag
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());
0 ignored issues
show
Documentation introduced by
$this->getFormattedLabel() is of type false|object<HtmlObject\Element>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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() && $this->app['former']->framework() == 'TwitterBootstrap4') {
180
			$wrapperClass = $field->isInline() ? 'form-check form-check-inline' : 'form-check';
0 ignored issues
show
Documentation Bug introduced by
The method isInline does not exist on object<Former\Traits\Field>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
181
			if ($this->app['former']->getErrors($field->getName())) {
182
				$hiddenInput = Element::create('input', null, ['type' => 'hidden'])->class('form-check-input is-invalid');
183
				$help = $hiddenInput.$help;
184
			}
185
			$help = Element::create('div', $help)->class($wrapperClass);
186
		}
187
		$field = $this->prependAppend($field);
0 ignored issues
show
Documentation introduced by
$field is of type object<Former\Traits\Field>, but the function expects a object<Former\Form\Field>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
188
		$field .= $help;
189
190
		return $this->wrap($field, $label);
191
	}
192
193
	////////////////////////////////////////////////////////////////////
194
	//////////////////////////// FIELD METHODS /////////////////////////
195
	////////////////////////////////////////////////////////////////////
196
197
	/**
198
	 * Set the state of the group
199
	 *
200
	 * @param  string $state A Bootstrap state class
201
	 */
202
	public function state($state)
203
	{
204
		// Filter state
205
		$state = $this->app['former.framework']->filterState($state);
206
207
		$this->state = $state;
208
	}
209
210
	/**
211
	 * Set a class on the Group
212
	 *
213
	 * @param string $class The class to add
214
	 */
215
	public function addGroupClass($class)
216
	{
217
		$this->addClass($class);
218
	}
219
220
	/**
221
	 * Set a class on the Label
222
	 *
223
	 * @param string $class The class to add on the Label
224
	 */
225
	public function addLabelClass($class)
226
	{
227
		// Don't add a label class if it isn't an Element instance
228
		if (!$this->label instanceof Element) {
229
			return $this;
230
		}
231
232
		$this->label->addClass($class);
233
234
		return $this;
235
	}
236
237
	/**
238
	 * Adds a label to the group
239
	 *
240
	 * @param  string $label A label
241
	 */
242
	public function setLabel($label)
243
	{
244
		if (!$label instanceof Element) {
245
			$label = Helpers::translate($label);
246
			$label = Element::create('label', $label)->for($label);
247
		}
248
249
		$this->label = $label;
250
	}
251
252
	/**
253
	 * Get the formatted group label
254
	 *
255
	 * @return string|null
256
	 */
257
	public function getFormattedLabel()
258
	{
259
		if (!$this->label) {
260
			return false;
261
		}
262
263
		return $this->label->addClass($this->app['former.framework']->getLabelClasses());
264
	}
265
266
	/**
267
	 * Disables the control group for the current field
268
	 */
269
	public function raw()
270
	{
271
		$this->raw = true;
272
	}
273
274
	/**
275
	 * Check if the current group is to be displayed or not
276
	 *
277
	 * @return boolean
278
	 */
279
	public function isRaw()
280
	{
281
		return (bool) $this->raw;
282
	}
283
284
	////////////////////////////////////////////////////////////////////
285
	///////////////////////////// HELP BLOCKS //////////////////////////
286
	////////////////////////////////////////////////////////////////////
287
288
	/**
289
	 * Alias for inlineHelp
290
	 *
291
	 * @param  string $help       The help text
292
	 * @param  array  $attributes Facultative attributes
293
	 */
294
	public function help($help, $attributes = array())
295
	{
296
		return $this->inlineHelp($help, $attributes);
297
	}
298
299
	/**
300
	 * Add an inline help
301
	 *
302
	 * @param  string $help       The help text
303
	 * @param  array  $attributes Facultative attributes
304
	 */
305
	public function inlineHelp($help, $attributes = array())
306
	{
307
		// If no help text, do nothing
308
		if (!$help) {
309
			return false;
310
		}
311
312
		$this->help['inline'] = $this->app['former.framework']->createHelp($help, $attributes);
313
	}
314
315
	/**
316
	 * Add an block help
317
	 *
318
	 * @param  string $help       The help text
319
	 * @param  array  $attributes Facultative attributes
320
	 */
321
	public function blockHelp($help, $attributes = array())
322
	{
323
		// Reserved method
324
		if ($this->app['former.framework']->isnt('TwitterBootstrap') &&
325
		    $this->app['former.framework']->isnt('TwitterBootstrap3') &&
326
		    $this->app['former.framework']->isnt('TwitterBootstrap4')
327
		) {
328
			throw new BadMethodCallException('This method is only available on the Bootstrap framework');
329
		}
330
331
		// If no help text, do nothing
332
		if (!$help) {
333
			return false;
334
		}
335
336
		$this->help['block'] = $this->app['former.framework']->createBlockHelp($help, $attributes);
337
	}
338
339
	////////////////////////////////////////////////////////////////////
340
	///////////////////////// PREPEND/APPEND METHODS ///////////////////
341
	////////////////////////////////////////////////////////////////////
342
343
	/**
344
	 * Prepend elements to the field
345
	 */
346
	public function prepend()
347
	{
348
		$this->placeAround(func_get_args(), 'prepend');
349
	}
350
351
	/**
352
	 * Append elements to the field
353
	 */
354
	public function append()
355
	{
356
		$this->placeAround(func_get_args(), 'append');
357
	}
358
359
	/**
360
	 * Prepends an icon to a field
361
	 *
362
	 * @param string $icon       The icon to prepend
363
	 * @param array  $attributes Its attributes
364
	 */
365
	public function prependIcon($icon, $attributes = array(), $iconSettings = array())
366
	{
367
		$icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
368
369
		$this->prepend($icon);
370
	}
371
372
	/**
373
	 * Append an icon to a field
374
	 *
375
	 * @param string $icon       The icon to prepend
376
	 * @param array  $attributes Its attributes
377
	 */
378
	public function appendIcon($icon, $attributes = array(), $iconSettings = array())
379
	{
380
		$icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
381
382
		$this->append($icon);
383
	}
384
385
	////////////////////////////////////////////////////////////////////
386
	//////////////////////////////// HELPERS ///////////////////////////
387
	////////////////////////////////////////////////////////////////////
388
389
	/**
390
	 * Get the errors for the group
391
	 *
392
	 * @return string
393
	 */
394
	public function getErrors()
395
	{
396
		$errors = '';
397
398
		if (!self::$opened) {
399
400
			// for non-custom groups, normal error handling applies
401
			$errors = $this->app['former']->getErrors();
402
		} elseif (!empty($this->validations)) {
403
404
			// error handling only when validations specified for custom groups
405
			foreach ($this->validations as $validation) {
406
				$errors .= $this->app['former']->getErrors($validation);
407
			}
408
		}
409
410
		return $errors;
411
	}
412
413
	/**
414
	 * Wraps content in a group
415
	 *
416
	 * @param string $contents The content
417
	 * @param string $label    The label to add
418
	 *
419
	 * @return string A group
420
	 */
421
	public function wrap($contents, $label = null)
422
	{
423
		$group = $this->open();
424
		$group .= $label;
425
		$group .= $this->app['former.framework']->wrapField($contents);
426
		$group .= $this->close();
427
428
		return $group;
429
	}
430
431
	/**
432
	 * Prints out the current label
433
	 *
434
	 * @param  string $field The field to create a label for
435
	 *
436
	 * @return string        A <label> tag
437
	 */
438
	protected function getLabel($field = null)
439
	{
440
		// Don't create a label if none exist
441
		if (!$field or !$this->label) {
442
			return null;
443
		}
444
445
		// Wrap label in framework classes
446
		$this->label->addClass($this->app['former.framework']->getLabelClasses());
447
		$this->label = $this->app['former.framework']->createLabelOf($field, $this->label);
448
		$this->label = $this->app['former.framework']->wrapLabel($this->label);
449
450
		return $this->label;
451
	}
452
453
	/**
454
	 * Prints out the current help
455
	 *
456
	 * @return string A .help-block or .help-inline
457
	 */
458
	protected function getHelp()
459
	{
460
		$inline = Arr::get($this->help, 'inline');
461
		$block  = Arr::get($this->help, 'block');
462
463
		// Replace help text with error if any found
464
		$errors = $this->app['former']->getErrors();
465
		if ($errors and $this->app['former']->getOption('error_messages')) {
466
			$inline = $this->app['former.framework']->createValidationError($errors);
467
		}
468
469
		return join(null, array($inline, $block));
470
	}
471
472
	/**
473
	 * Format the field with prepended/appended elements
474
	 *
475
	 * @param  Field $field The field to format
476
	 *
477
	 * @return string        Field plus supplementary elements
478
	 */
479
	protected function prependAppend($field)
480
	{
481
		if (!$this->prepend and !$this->append) {
482
			return $field->render();
483
		}
484
485
		return $this->app['former.framework']->prependAppend($field, $this->prepend, $this->append);
486
	}
487
488
	/**
489
	 * Place elements around the field
490
	 *
491
	 * @param  array  $items An array of items to place
492
	 * @param  string $place Where they should end up (prepend|append)
493
	 */
494
	protected function placeAround($items, $place)
495
	{
496
		// Iterate over the items and place them where they should
497
		foreach ((array) $items as $item) {
498
			$item             = $this->app['former.framework']->placeAround($item, $place);
499
			$this->{$place}[] = $item;
500
		}
501
	}
502
}
503