Completed
Push — master ( a36f01...2c25be )
by Tortue
01:41
created

Group::prependAppend()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 3
nc 2
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
 		$labelClasses = $this->app['former.framework']->getLabelClasses();
447
 		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...
448
 			$this->app['former']->framework() == 'TwitterBootstrap4' &&
449
 			$this->app['former.form']->isOfType('horizontal')
450
 		) {
451
 			$labelClasses = array_merge($labelClasses, array('pt-0'));
452
 		}
453
 		$this->label->addClass($labelClasses);
454
 		$this->label = $this->app['former.framework']->createLabelOf($field, $this->label);
455
 		$this->label = $this->app['former.framework']->wrapLabel($this->label);
456
457
 		return $this->label;
458
 	}
459
460
	/**
461
	 * Prints out the current help
462
	 *
463
	 * @return string A .help-block or .help-inline
464
	 */
465
	protected function getHelp()
466
	{
467
		$inline = Arr::get($this->help, 'inline');
468
		$block  = Arr::get($this->help, 'block');
469
470
		// Replace help text with error if any found
471
		$errors = $this->app['former']->getErrors();
472
		if ($errors and $this->app['former']->getOption('error_messages')) {
473
			$inline = $this->app['former.framework']->createValidationError($errors);
474
		}
475
476
		return join(null, array($inline, $block));
477
	}
478
479
	/**
480
	 * Format the field with prepended/appended elements
481
	 *
482
	 * @param  Field $field The field to format
483
	 *
484
	 * @return string        Field plus supplementary elements
485
	 */
486
	protected function prependAppend($field)
487
	{
488
		if (!$this->prepend and !$this->append) {
489
			return $field->render();
490
		}
491
492
		return $this->app['former.framework']->prependAppend($field, $this->prepend, $this->append);
493
	}
494
495
	/**
496
	 * Place elements around the field
497
	 *
498
	 * @param  array  $items An array of items to place
499
	 * @param  string $place Where they should end up (prepend|append)
500
	 */
501
	protected function placeAround($items, $place)
502
	{
503
		// Iterate over the items and place them where they should
504
		foreach ((array) $items as $item) {
505
			$item             = $this->app['former.framework']->placeAround($item, $place);
506
			$this->{$place}[] = $item;
507
		}
508
	}
509
}
510