Completed
Pull Request — master (#603)
by Tortue
03:23 queued 01:37
created

Group::getLabel()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 8.9617
c 0
b 0
f 0
cc 6
nc 3
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
	 * Adds a label to the group
222
	 *
223
	 * @param  string $label A label
224
	 */
225
	public function setLabel($label)
226
	{
227
		if (!$label instanceof Element) {
228
			$label = Helpers::translate($label);
229
			$label = Element::create('label', $label)->for($label);
230
		}
231
232
		$this->label = $label;
233
	}
234
235
	/**
236
	 * Get the formatted group label
237
	 *
238
	 * @return string|null
239
	 */
240
	public function getFormattedLabel()
241
	{
242
		if (!$this->label) {
243
			return false;
244
		}
245
246
		return $this->label->addClass($this->app['former.framework']->getLabelClasses());
247
	}
248
249
	/**
250
	 * Disables the control group for the current field
251
	 */
252
	public function raw()
253
	{
254
		$this->raw = true;
255
	}
256
257
	/**
258
	 * Check if the current group is to be displayed or not
259
	 *
260
	 * @return boolean
261
	 */
262
	public function isRaw()
263
	{
264
		return (bool) $this->raw;
265
	}
266
267
	////////////////////////////////////////////////////////////////////
268
	///////////////////////////// HELP BLOCKS //////////////////////////
269
	////////////////////////////////////////////////////////////////////
270
271
	/**
272
	 * Alias for inlineHelp
273
	 *
274
	 * @param  string $help       The help text
275
	 * @param  array  $attributes Facultative attributes
276
	 */
277
	public function help($help, $attributes = array())
278
	{
279
		return $this->inlineHelp($help, $attributes);
280
	}
281
282
	/**
283
	 * Add an inline help
284
	 *
285
	 * @param  string $help       The help text
286
	 * @param  array  $attributes Facultative attributes
287
	 */
288
	public function inlineHelp($help, $attributes = array())
289
	{
290
		// If no help text, do nothing
291
		if (!$help) {
292
			return false;
293
		}
294
295
		$this->help['inline'] = $this->app['former.framework']->createHelp($help, $attributes);
296
	}
297
298
	/**
299
	 * Add an block help
300
	 *
301
	 * @param  string $help       The help text
302
	 * @param  array  $attributes Facultative attributes
303
	 */
304
	public function blockHelp($help, $attributes = array())
305
	{
306
		// Reserved method
307
		if ($this->app['former.framework']->isnt('TwitterBootstrap') &&
308
		    $this->app['former.framework']->isnt('TwitterBootstrap3') &&
309
		    $this->app['former.framework']->isnt('TwitterBootstrap4')
310
		) {
311
			throw new BadMethodCallException('This method is only available on the Bootstrap framework');
312
		}
313
314
		// If no help text, do nothing
315
		if (!$help) {
316
			return false;
317
		}
318
319
		$this->help['block'] = $this->app['former.framework']->createBlockHelp($help, $attributes);
320
	}
321
322
	////////////////////////////////////////////////////////////////////
323
	///////////////////////// PREPEND/APPEND METHODS ///////////////////
324
	////////////////////////////////////////////////////////////////////
325
326
	/**
327
	 * Prepend elements to the field
328
	 */
329
	public function prepend()
330
	{
331
		$this->placeAround(func_get_args(), 'prepend');
332
	}
333
334
	/**
335
	 * Append elements to the field
336
	 */
337
	public function append()
338
	{
339
		$this->placeAround(func_get_args(), 'append');
340
	}
341
342
	/**
343
	 * Prepends an icon to a field
344
	 *
345
	 * @param string $icon       The icon to prepend
346
	 * @param array  $attributes Its attributes
347
	 */
348
	public function prependIcon($icon, $attributes = array(), $iconSettings = array())
349
	{
350
		$icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
351
352
		$this->prepend($icon);
353
	}
354
355
	/**
356
	 * Append an icon to a field
357
	 *
358
	 * @param string $icon       The icon to prepend
359
	 * @param array  $attributes Its attributes
360
	 */
361
	public function appendIcon($icon, $attributes = array(), $iconSettings = array())
362
	{
363
		$icon = $this->app['former.framework']->createIcon($icon, $attributes, $iconSettings);
364
365
		$this->append($icon);
366
	}
367
368
	////////////////////////////////////////////////////////////////////
369
	//////////////////////////////// HELPERS ///////////////////////////
370
	////////////////////////////////////////////////////////////////////
371
372
	/**
373
	 * Get the errors for the group
374
	 *
375
	 * @return string
376
	 */
377
	public function getErrors()
378
	{
379
		$errors = '';
380
381
		if (!self::$opened) {
382
383
			// for non-custom groups, normal error handling applies
384
			$errors = $this->app['former']->getErrors();
385
		} elseif (!empty($this->validations)) {
386
387
			// error handling only when validations specified for custom groups
388
			foreach ($this->validations as $validation) {
389
				$errors .= $this->app['former']->getErrors($validation);
390
			}
391
		}
392
393
		return $errors;
394
	}
395
396
	/**
397
	 * Wraps content in a group
398
	 *
399
	 * @param string $contents The content
400
	 * @param string $label    The label to add
401
	 *
402
	 * @return string A group
403
	 */
404
	public function wrap($contents, $label = null)
405
	{
406
		$group = $this->open();
407
		$group .= $label;
408
		$group .= $this->app['former.framework']->wrapField($contents);
409
		$group .= $this->close();
410
411
		return $group;
412
	}
413
414
	/**
415
	 * Prints out the current label
416
	 *
417
	 * @param  string $field The field to create a label for
418
	 *
419
	 * @return string        A <label> tag
420
	 */
421
	 protected function getLabel($field = null)
422
 	{
423
 		// Don't create a label if none exist
424
 		if (!$field or !$this->label) {
425
 			return null;
426
 		}
427
428
 		// Wrap label in framework classes
429
 		$labelClasses = $this->app['former.framework']->getLabelClasses();
430
 		if ($field->isCheckable() &&
0 ignored issues
show
Bug introduced by
The method isCheckable cannot be called on $field (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
431
 			$this->app['former']->framework() == 'TwitterBootstrap4' &&
432
 			$this->app['former.form']->isOfType('horizontal')
433
 		) {
434
 			$labelClasses = array_merge($labelClasses, array('pt-0'));
435
 		}
436
 		$this->label->addClass($labelClasses);
437
 		$this->label = $this->app['former.framework']->createLabelOf($field, $this->label);
438
 		$this->label = $this->app['former.framework']->wrapLabel($this->label);
439
440
 		return $this->label;
441
 	}
442
443
	/**
444
	 * Prints out the current help
445
	 *
446
	 * @return string A .help-block or .help-inline
447
	 */
448
	protected function getHelp()
449
	{
450
		$inline = Arr::get($this->help, 'inline');
451
		$block  = Arr::get($this->help, 'block');
452
453
		// Replace help text with error if any found
454
		$errors = $this->app['former']->getErrors();
455
		if ($errors and $this->app['former']->getOption('error_messages')) {
456
			$inline = $this->app['former.framework']->createValidationError($errors);
457
		}
458
459
		return join(null, array($inline, $block));
460
	}
461
462
	/**
463
	 * Format the field with prepended/appended elements
464
	 *
465
	 * @param  Field $field The field to format
466
	 *
467
	 * @return string        Field plus supplementary elements
468
	 */
469
	protected function prependAppend($field)
470
	{
471
		if (!$this->prepend and !$this->append) {
472
			return $field->render();
473
		}
474
475
		return $this->app['former.framework']->prependAppend($field, $this->prepend, $this->append);
476
	}
477
478
	/**
479
	 * Place elements around the field
480
	 *
481
	 * @param  array  $items An array of items to place
482
	 * @param  string $place Where they should end up (prepend|append)
483
	 */
484
	protected function placeAround($items, $place)
485
	{
486
		// Iterate over the items and place them where they should
487
		foreach ((array) $items as $item) {
488
			$item             = $this->app['former.framework']->placeAround($item, $place);
489
			$this->{$place}[] = $item;
490
		}
491
	}
492
}
493