Completed
Push — master ( c60a32...d8cf68 )
by Ben
02:28 queued 10s
created

Choice   F

Complexity

Total Complexity 73

Size/Duplication

Total Lines 476
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 73
lcom 1
cbo 4
dl 0
loc 476
rs 2.56
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 19 8
A render() 0 22 5
A getSelect() 0 16 2
A getOptions() 0 18 5
A getOptGroup() 0 12 2
A getOption() 0 18 4
F getCheckables() 0 81 18
A getPlaceholder() 0 13 3
A setChoiceType() 0 11 5
B hasValue() 0 12 7
A addChoice() 0 6 1
A choices() 0 17 4
A range() 0 7 1
A fromQuery() 0 6 1
A placeholder() 0 6 1
A multiple() 0 7 1
A expanded() 0 7 1
A inline() 0 6 1
A stacked() 0 6 1
A isCheckable() 0 4 1
A getChoices() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Choice often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Choice, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Former\Form\Fields;
3
4
use Former\Helpers;
5
use Former\Traits\Field;
6
use HtmlObject\Element;
7
use HtmlObject\Input;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Former\Form\Fields\Input.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Illuminate\Container\Container;
9
10
/**
11
 * Everything list-related (select, multiselect, ...)
12
 */
13
class Choice extends Field
14
{
15
	/**
16
	 * Renders the checkables as inline
17
	 *
18
	 * @var boolean
19
	 */
20
	protected $inline = false;
21
22
	/**
23
	 * The choice's placeholder
24
	 *
25
	 * @var string
26
	 */
27
	private $placeholder = null;
28
29
	/**
30
	 * The choice's options
31
	 *
32
	 * @var array
33
	 */
34
	protected $options = [
35
        'isMultiple' => false,
36
        'isExpanded' => false,
37
    ];
38
39
	/**
40
	 * The choice's choices
41
	 *
42
	 * @var array
43
	 */
44
	protected $choices = [];
45
46
	/**
47
	 * The choice's element
48
	 *
49
	 * @var string
50
	 */
51
	protected $element = 'choice';
52
53
	/**
54
	 * The choice's self-closing state
55
	 *
56
	 * @var boolean
57
	 */
58
	protected $isSelfClosing = false;
59
60
	////////////////////////////////////////////////////////////////////
61
	/////////////////////////// CORE METHODS ///////////////////////////
62
	////////////////////////////////////////////////////////////////////
63
64
	/**
65
	 * Easier arguments order for selects
66
	 *
67
	 * @param Container $app        The Container instance
68
	 * @param string    $type       select
69
	 * @param string    $name       Field name
70
	 * @param string    $label      Its label
71
	 * @param array     $choices    The choice's choices
72
	 * @param string    $selected   The selected choice(s)
73
	 * @param array     $attributes Attributes
74
	 */
75
	public function __construct(Container $app, $type, $name, $label, $choices, $selected, $attributes)
76
	{
77
		if ($selected) {
78
			$this->value = $selected;
79
		}
80
		if ($choices) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $choices of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
81
			$this->choices($choices);
82
		}
83
		
84
		parent::__construct($app, $type, $name, $label, $selected, $attributes);
85
		
86
		$this->setChoiceType();
87
88
		// Nested models population
89
		if (str_contains($this->name, '.') and is_array($this->value) and !empty($this->value) and is_string($this->value[key($this->value)])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
90
			$this->fromQuery($this->value);
91
			$this->value = $selected ?: null;
92
		}
93
	}
94
95
	/**
96
	 * Renders the choice
97
	 *
98
	 * @return string A <select> tag
99
	 */
100
	public function render()
101
	{
102
		$choiceType = $this->getType();
103
		$this->setFieldClasses();
104
105
		if (!isset($this->attributes['id'])) {
106
			$this->setAttribute('id', $this->name);
107
		}
108
109
		switch ($choiceType) {
110
			case 'select':
111
				$field = $this->getSelect();
112
				break;
113
			case 'checkbox':
114
			case 'radio':
115
				$field = $this->getCheckables($choiceType);
116
				break;
117
		}
118
		$this->value = null;
119
		$content = $field->render();
0 ignored issues
show
Bug introduced by
The variable $field does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
120
		return $content;
121
	}
122
123
	public function getSelect()
124
	{
125
		$field = Element::create('select', null, $this->attributes);
126
127
		$name = $this->name;
128
		if ($this->options['isMultiple']) {
129
			$field->multiple();
130
			$name .= '[]';
131
		}
132
		
133
		$field->setAttribute('name', $name);
134
135
		$field->nest($this->getOptions());
136
137
		return $field;
138
	}
139
140
	public function getOptions()
141
	{
142
		$children = [];
143
		
144
		// Add placeholder text if any
145
		if ($placeholder = $this->getPlaceholder()) {
146
			$children[] = $placeholder;
147
		}
148
149
		foreach ($this->choices as $key => $value) {
150
			if (is_array($value) and !isset($value['value'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
151
				$children[] = $this->getOptGroup($key, $value);
152
			} else {
153
				$children[] = $this->getOption($key, $value);
154
			}
155
		}
156
		return $children;
157
	}
158
159
	public function getOptGroup($label, $options)
160
	{
161
		$element = Element::create('optgroup')->label($label);
162
		$children = [];
163
		foreach ($options as $key => $value) {
164
			$option = $this->getOption($key, $value);
165
			$children[] = $option;
166
		}
167
		$element->nest($children);
168
169
		return $element;
170
	}
171
172
	public function getOption($key, $value)
173
	{
174
		if (is_array($value) and isset($value['value'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
175
			$attributes = $value;
176
			$text = $key;
177
			$key = null;
0 ignored issues
show
Unused Code introduced by
$key is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
178
		} else {
179
			$attributes = array('value' => $key);
180
			$text = $value;
181
		}
182
183
		$element = Element::create('option', $text, $attributes);
184
		if ($this->hasValue($attributes['value'])) {
185
			$element->selected('selected');
0 ignored issues
show
Documentation Bug introduced by
The method selected does not exist on object<Former\Form\Fields\Choice>? 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...
186
		}
187
188
		return $element;
189
	}
190
191
	public function getCheckables($choiceType)
192
	{
193
		if (!(is_array($this->value) || $this->value instanceof \ArrayAccess)) {
194
			$this->value = explode(',', $this->value);
0 ignored issues
show
Documentation Bug introduced by
It seems like explode(',', $this->value) of type array is incompatible with the declared type string|null|object<HtmlObject\Traits\Tag> of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
195
		}
196
197
		$disabled = isset($this->attributes['disabled']);
198
		unset($this->attributes['disabled']);
199
200
		$field = Element::create('div', null, $this->attributes);
201
202
		$children = [];
203
		foreach ($this->choices as $key => $value) {
204
			$attributes = [];
0 ignored issues
show
Unused Code introduced by
$attributes is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
205
206
			if (is_array($value) and isset($value['value'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
207
				$attributes = $value;
208
				$label = $key;
209
				$inputValue = $value['value'];
210
			} else {
211
				$attributes = [];
212
				$label = $value;
213
				$inputValue = $key;
214
			}
215
216
			if ($disabled) {
217
				$attributes['disabled'] = true;
218
			}
219
220
			if(isset($attributes['name'])) {
221
				$name = $attributes['name'];
222
				unset($attributes['name']);
223
			} else {
224
				$name = $this->name;
225
			}
226
			if ($this->options['isMultiple']) {
227
				$name .= '[]';
228
			}
229
		
230
			if(!isset($attributes['id'])) {
231
				$attributes['id'] = $this->id.'_'.count($children);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Former\Form\Fields\Choice>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
232
			}
233
234
			// If inline items, add class
235
			$isInline = $this->inline ? ' '.$this->app['former.framework']->getInlineLabelClass($this) : null;
236
237
			// In Bootsrap 3, don't append the the checkable type (radio/checkbox) as a class if
238
			// rendering inline.
239
			$class = $this->app['former']->framework() == 'TwitterBootstrap3' ? trim($isInline) : $choiceType.$isInline;
240
241
			$element = Input::create($choiceType, $name, $inputValue, $attributes);
242
			
243
			// $element->setAttribute('name', $name);
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
244
245
			if ($this->hasValue($inputValue)) {
246
				$element->checked('checked');
247
			}
248
			// $attributes['value'] = $key;
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
249
			if (!$label) {
250
				$element = (is_object($field)) ? $field->render() : $field;
251
			} else {
252
				$rendered = $element->render();
253
				$labelElement = Element::create('label', $rendered.$label);
254
				$element = $labelElement->for($attributes['id'])->class($class);
0 ignored issues
show
Documentation Bug introduced by
The method for does not exist on object<Former\Form\Fields\Choice>? 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...
255
			}
256
			
257
			// If BS3, if checkables are stacked, wrap them in a div with the checkable type
258
			if (!$isInline && $this->app['former']->framework() == 'TwitterBootstrap3') {
0 ignored issues
show
Bug Best Practice introduced by
The expression $isInline of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
259
				$wrapper = Element::create('div', $element->render())->class($choiceType);
0 ignored issues
show
Documentation Bug introduced by
The method class does not exist on object<Former\Form\Fields\Choice>? 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...
260
				if ($disabled) {
261
					$wrapper->addClass('disabled');
262
				}
263
				$element = $wrapper;
264
			}
265
266
			$children[] = $element;
267
		}
268
		$field->nest($children);
269
		
270
		return $field;
271
	}
272
273
	/**
274
	 * Get the choice's placeholder
275
	 *
276
	 * @return Element
277
	 */
278
	protected function getPlaceholder()
279
	{
280
		if (!$this->placeholder) {
281
			return false;
282
		}
283
284
		$attributes = array('value' => '', 'disabled' => 'disabled');
285
		if (!(array)$this->value) {
286
			$attributes['selected'] = 'selected';
287
		}
288
289
		return Element::create('option', $this->placeholder, $attributes);
290
	}
291
292
	/**
293
	 * Sets the element's type based on options
294
	 *
295
	 * @return this
296
	 */
297
	protected function setChoiceType()
298
	{
299
		if ($this->options['isExpanded'] && !$this->options['isMultiple']) {
300
			$this->setType('radio');
301
		} elseif ($this->options['isExpanded'] && $this->options['isMultiple']) {
302
			$this->setType('checkbox');
303
		} else {
304
			$this->setType('select');
305
		}
306
		return $this;
307
	}
308
309
	/**
310
	 * Select a value in the field's children
311
	 *
312
	 * @param mixed   $value
0 ignored issues
show
Bug introduced by
There is no parameter named $value. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
313
	 *
314
	 * @return bool
315
	 */
316
	protected function hasValue($choiceValue)
317
	{
318
		foreach ((array)$this->value as $key => $value) {
319
			if (is_object($value) && method_exists($value, 'getKey')) {
320
				$value = $value->getKey();
321
			}
322
			if ($choiceValue === $value || is_numeric($value) && $choiceValue === (int)$value) {
323
				return true;
324
			}
325
		}
326
		return false;
327
	}
328
329
	////////////////////////////////////////////////////////////////////
330
	////////////////////////// FIELD METHODS ///////////////////////////
331
	////////////////////////////////////////////////////////////////////
332
333
	/**
334
	 * Set the choices
335
	 *
336
	 * @param  array   $_choices     The choices as an array
0 ignored issues
show
Bug introduced by
There is no parameter named $_choices. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
337
	 * @param  mixed   $selected     Facultative selected entry
0 ignored issues
show
Bug introduced by
There is no parameter named $selected. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
338
	 * @param  boolean $valuesAsKeys Whether the array's values should be used as
0 ignored issues
show
Bug introduced by
There is no parameter named $valuesAsKeys. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
339
	 *                               the option's values instead of the array's keys
340
	 */
341
	public function addChoice($value, $key = null)
342
	{
343
		$this->choices[$key ?? $value] = $value;
344
345
		return $this;
346
	}
347
348
	/**
349
	 * Set the choices
350
	 *
351
	 * @param  array   $_choices     The choices as an array
352
	 * @param  mixed   $selected     Facultative selected entry
0 ignored issues
show
Bug introduced by
There is no parameter named $selected. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
353
	 * @param  boolean $valuesAsKeys Whether the array's values should be used as
354
	 *                               the option's values instead of the array's keys
355
	 */
356
	public function choices($_choices, $valuesAsKeys = false)
357
	{
358
		$choices = (array) $_choices;
359
360
		// If valuesAsKeys is true, use the values as keys
361
		if ($valuesAsKeys) {
362
			foreach ($choices as $value) {
363
				$this->addChoice($value, $value);
364
			}
365
		} else {
366
			foreach ($choices as $key => $value) {
367
				$this->addChoice($value, $key);
368
			}
369
		}
370
371
		return $this;
372
	}
373
374
	/**
375
	 * Creates a list of choices from a range
376
	 *
377
	 * @param  integer $from
378
	 * @param  integer $to
379
	 * @param  integer $step
380
	 */
381
	public function range($from, $to, $step = 1)
382
	{
383
		$range = range($from, $to, $step);
384
		$this->choices($range, true);
385
386
		return $this;
387
	}
388
389
	/**
390
	 * Use the results from a Fluent/Eloquent query as choices
391
	 *
392
	 * @param  array           $results    An array of Eloquent models
393
	 * @param  string|function $text       The value to use as text
394
	 * @param  string|array    $attributes The data to use as attributes
395
	 * @param  string	       $selected   The default selected item
396
	 */
397
	public function fromQuery($results, $text = null, $attributes = null, $selected = null)
398
	{
399
		$this->choices(Helpers::queryToArray($results, $text, $attributes))->value($selected);
0 ignored issues
show
Bug introduced by
It seems like \Former\Helpers::queryTo...ts, $text, $attributes) targeting Former\Helpers::queryToArray() can also be of type object<Illuminate\Support\Collection>; however, Former\Form\Fields\Choice::choices() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
400
401
		return $this;
402
	}
403
404
	/**
405
	 * Add a placeholder to the current select
406
	 *
407
	 * @param  string $placeholder The placeholder text
408
	 */
409
	public function placeholder($placeholder)
410
	{
411
		$this->placeholder = Helpers::translate($placeholder);
412
413
		return $this;
414
	}
415
	
416
	/**
417
	 * Set isMultiple
418
	 *
419
	 * @param boolean $isMultiple
420
	 * @return $this
421
	 */
422
	public function multiple($isMultiple = true)
423
	{
424
		$this->options['isMultiple'] = $isMultiple;
425
		$this->setChoiceType();
426
427
		return $this;
428
	}
429
	
430
	/**
431
	 * Set isExpanded
432
	 *
433
	 * @param boolean $isExpanded
434
	 * @return $this
435
	 */
436
	public function expanded($isExpanded = true)
437
	{
438
		$this->options['isExpanded'] = $isExpanded;
439
		$this->setChoiceType();
440
441
		return $this;
442
	}
443
444
	/**
445
	 * Set the choices as inline (for expanded items)
446
	 */
447
	public function inline($isInline = true)
448
	{
449
		$this->inline = $isInline;
450
451
		return $this;
452
	}
453
454
	/**
455
	 * Set the choices as stacked (for expanded items)
456
	 */
457
	public function stacked($isStacked = true)
458
	{
459
		$this->inline = !$isStacked;
460
461
		return $this;
462
	}
463
464
	/**
465
	 * Check if field is a checkbox or a radio
466
	 *
467
	 * @return boolean
468
	 */
469
	public function isCheckable()
470
	{
471
		return $this->options['isExpanded'];
472
	}
473
474
	////////////////////////////////////////////////////////////////////
475
	////////////////////////////// HELPERS /////////////////////////////
476
	////////////////////////////////////////////////////////////////////
477
478
	/**
479
	 * Returns the current choices in memory for manipulations
480
	 *
481
	 * @return array The current choices array
482
	 */
483
	public function getChoices()
484
	{
485
		return $this->choices;
486
	}
487
	
488
}
489