Completed
Push — namespace-template ( 7967f2...367a36 )
by Sam
10:48
created

SelectField::isSelectedValue()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 23
Code Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 7
eloc 10
nc 5
nop 2
dl 0
loc 23
rs 6.7272
1
<?php
2
3
/**
4
 * Represents a field that allows users to select one or more items from a list
5
 */
6
abstract class SelectField extends FormField {
7
8
	/**
9
	 * Associative or numeric array of all dropdown items,
10
	 * with array key as the submitted field value, and the array value as a
11
	 * natural language description shown in the interface element.
12
	 *
13
	 * @var array|ArrayAccess
14
	 */
15
	protected $source;
16
17
	/**
18
	 * The values for items that should be disabled (greyed out) in the dropdown.
19
	 * This is a non-associative array
20
	 *
21
	 * @var array
22
	 */
23
	protected $disabledItems = array();
24
25
	/**
26
	 * @param string $name The field name
27
	 * @param string $title The field title
28
	 * @param array|ArrayAccess $source A map of the dropdown items
29
	 * @param mixed $value The current value
30
	 */
31
	public function __construct($name, $title = null, $source = array(), $value = null) {
32
		$this->setSource($source);
33
		if(!isset($title)) {
34
			$title = $name;
35
		}
36
		parent::__construct($name, $title, $value);
37
	}
38
39
	/**
40
	 * Mark certain elements as disabled,
41
	 * regardless of the {@link setDisabled()} settings.
42
	 *
43
	 * These should be items that appear in the source list, not in addition to them.
44
	 *
45
	 * @param array|SS_List $items Collection of values or items
46
	 */
47
	public function setDisabledItems($items){
48
		$this->disabledItems = $this->getListValues($items);
49
		return $this;
50
	}
51
52
	/**
53
	 * Non-associative list of disabled item values
54
	 *
55
	 * @return array
56
	 */
57
	public function getDisabledItems(){
58
		return $this->disabledItems;
59
	}
60
61
	/**
62
	 * Check if the given value is disabled
63
	 *
64
	 * @param string $value
65
	 * @return bool
66
	 */
67
	protected function isDisabledValue($value) {
68
		if($this->isDisabled()) {
69
			return true;
70
		}
71
		return in_array($value, $this->getDisabledItems());
72
	}
73
74
	public function getAttributes() {
75
		return array_merge(
76
			parent::getAttributes(),
77
			array('type' => null, 'value' => null)
78
		);
79
	}
80
81
	/**
82
	 * Retrieve all values in the source array
83
	 *
84
	 * @return array
85
	 */
86
	protected function getSourceValues() {
87
		return array_keys($this->getSource());
88
	}
89
90
	/**
91
	 * Gets all valid values for this field.
92
	 *
93
	 * Does not include "empty" value if specified
94
	 *
95
	 * @return array
96
	 */
97
	public function getValidValues() {
98
		$valid = array_diff($this->getSourceValues(), $this->getDisabledItems());
99
		// Renumber indexes from 0
100
		return array_values($valid);
101
	}
102
103
	/**
104
	 * Gets the source array not including any empty default values.
105
	 *
106
	 * @return array|ArrayAccess
107
	 */
108
	public function getSource() {
109
		return $this->source;
110
	}
111
112
	/**
113
	 * Set the source for this list
114
	 *
115
	 * @param mixed $source
116
	 * @return $this
117
	 */
118
	public function setSource($source) {
119
		$this->source = $this->getListMap($source);
120
		return $this;
121
	}
122
123
	/**
124
	 * Given a list of values, extract the associative map of id => title
125
	 *
126
	 * @param mixed $source
127
	 * @return array Associative array of ids and titles
128
	 */
129
	protected function getListMap($source) {
130
		// Extract source as an array
131
		if($source instanceof SS_List) {
132
			$source = $source->map();
133
		}
134
		if($source instanceof SS_Map) {
135
			$source = $source->toArray();
136
		}
137
		if(!is_array($source) && !($source instanceof ArrayAccess)) {
138
			user_error('$source passed in as invalid type', E_USER_ERROR);
139
		}
140
141
		return $source;
142
	}
143
144
	/**
145
	 * Given a non-array collection, extract the non-associative list of ids
146
	 * If passing as array, treat the array values (not the keys) as the ids
147
	 *
148
	 * @param mixed $values
149
	 * @return array Non-associative list of values
150
	 */
151
	protected function getListValues($values) {
152
		// Empty values
153
		if(empty($values)) {
154
			return array();
155
		}
156
157
		// Direct array
158
		if(is_array($values)) {
159
			return array_values($values);
160
		}
161
162
		// Extract lists
163
		if($values instanceof SS_List) {
164
			return $values->column('ID');
165
		}
166
167
		return array(trim($values));
168
	}
169
170
	/**
171
	 * Determine if the current value of this field matches the given option value
172
	 *
173
	 * @param mixed $dataValue The value as extracted from the source of this field (or empty value if available)
174
	 * @param mixed $userValue The value as submitted by the user
175
	 * @return boolean True if the selected value matches the given option value
176
	 */
177
	public function isSelectedValue($dataValue, $userValue) {
178
		if($dataValue === $userValue) {
179
			return true;
180
		}
181
182
		// Allow null to match empty strings
183
		if($dataValue === '' && $userValue === null) {
184
			return true;
185
		}
186
187
		// Safety check against casting arrays as strings in PHP>5.4
188
 		if(is_array($dataValue) || is_array($userValue)) {
189
			return false;
190
		}
191
192
		// For non-falsey values do loose comparison
193
		if($dataValue) {
194
			return $dataValue == $userValue;
195
		}
196
197
		// For empty values, use string comparison to perform visible value match
198
		return ((string) $dataValue) === ((string) $userValue);
199
	}
200
201
	public function performReadonlyTransformation() {
202
		$field = $this->castedCopy('LookupField');
203
		$field->setSource($this->getSource());
204
		$field->setReadonly(true);
205
206
		return $field;
207
	}
208
209
	public function performDisabledTransformation() {
210
		$clone = clone $this;
211
		$clone->setDisabled(true);
212
		return $clone;
213
	}
214
215
	/**
216
	 * Returns another instance of this field, but "cast" to a different class.
217
	 *
218
	 * @see FormField::castedCopy()
219
	 *
220
	 * @param String $classOrCopy
221
	 * @return FormField
222
	 */
223
	public function castedCopy($classOrCopy) {
224
		$field = parent::castedCopy($classOrCopy);
225
		if($field instanceof SelectField) {
226
			$field->setSource($this->getSource());
227
		}
228
		return $field;
229
	}
230
}
231