Completed
Push — correct-classname-values ( f9b487 )
by Sam
08:29
created

CompositeField::setDisabled()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 9.4285
1
<?php
2
/**
3
 * Base class for all fields that contain other fields.
4
 *
5
 * Implements sequentialisation - so that when we're saving / loading data, we
6
 * can populate a tabbed form properly. All of the children are stored in
7
 * $this->children
8
 *
9
 * @package forms
10
 * @subpackage fields-structural
11
 */
12
class CompositeField extends FormField {
13
14
	/**
15
	 * @var FieldList
16
	 */
17
	protected $children;
18
19
	/**
20
	 * Set to true when this field is a readonly field
21
	 */
22
	protected $readonly;
23
24
	/**
25
	 * @var $columnCount int Toggle different css-rendering for multiple columns
26
	 * ("onecolumn", "twocolumns", "threecolumns"). The content is determined
27
	 * by the $children-array, so wrap all items you want to have grouped in a
28
	 * column inside a CompositeField.
29
	 * Caution: Please make sure that this variable actually matches the
30
	 * count of your $children.
31
	 */
32
	protected $columnCount = null;
33
34
	/**
35
	 * @var String custom HTML tag to render with, e.g. to produce a <fieldset>.
36
	 */
37
	protected $tag = 'div';
38
39
	/**
40
	 * @var String Optional description for this set of fields.
41
	 * If the {@link $tag} property is set to use a 'fieldset', this will be
42
	 * rendered as a <legend> tag, otherwise its a 'title' attribute.
43
	 */
44
	protected $legend;
45
46
	protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_STRUCTURAL;
47
48
	public function __construct($children = null) {
49
		if($children instanceof FieldList) {
50
			$this->children = $children;
51
		} elseif(is_array($children)) {
52
			$this->children = new FieldList($children);
53
		} else {
54
			//filter out null/empty items
55
			$children = array_filter(func_get_args());
56
			$this->children = new FieldList($children);
57
		}
58
		$this->children->setContainerField($this);
59
60
		parent::__construct(null, false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a null|string.

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...
61
	}
62
63
	/**
64
	 * Merge child field data into this form
65
	 */
66
	public function getSchemaDataDefaults() {
67
		$defaults = parent::getSchemaDataDefaults();
68
		$children = $this->getChildren();
69
		if($children && $children->count()) {
70
			$childSchema = [];
71
			/** @var FormField $child */
72
			foreach($children as $child) {
73
				$childSchema[] = $child->getSchemaData();
74
			}
75
			$defaults['children'] = $childSchema;
76
		}
77
		return $defaults;
78
	}
79
80
	/**
81
	 * Returns all the sub-fields, suitable for <% loop FieldList %>
82
	 *
83
	 * @return FieldList
84
	 */
85
	public function FieldList() {
86
		return $this->children;
87
	}
88
89
	/**
90
	 * Accessor method for $this->children
91
	 *
92
	 * @return FieldList
93
	 */
94
	public function getChildren() {
95
		return $this->children;
96
	}
97
98
	/**
99
	 * @param FieldList $children
100
	 * @return $this
101
	 */
102
	public function setChildren($children) {
103
		$this->children = $children;
104
		return $this;
105
	}
106
107
	/**
108
	 * @param string $tag
109
	 * @return $this
110
	 */
111
	public function setTag($tag) {
112
		$this->tag = $tag;
113
114
		return $this;
115
	}
116
117
	/**
118
	 * @return string
119
	 */
120
	public function getTag() {
121
		return $this->tag;
122
	}
123
124
	/**
125
	 * @param string $legend
126
	 * @return $this
127
	 */
128
	public function setLegend($legend) {
129
		$this->legend = $legend;
130
		return $this;
131
	}
132
133
	/**
134
	 * @return string
135
	 */
136
	public function getLegend() {
137
		return $this->legend;
138
	}
139
140
	/**
141
	 * @deprecated
142
	 */
143
	public function extraClasses() {
144
		Deprecation::notice('4.0', 'Use extraClass() instead');
145
		return $this->extraClass();
146
	}
147
148
	public function extraClass() {
149
		$classes = array('field', 'CompositeField', parent::extraClass());
150
		if($this->columnCount) $classes[] = 'multicolumn';
151
152
		return implode(' ', $classes);
153
	}
154
155
	public function getAttributes() {
156
		return array_merge(
157
			parent::getAttributes(),
158
			array(
159
				'tabindex' => null,
160
				'type' => null,
161
				'value' => null,
162
				'title' => ($this->tag == 'fieldset') ? null : $this->legend
163
			)
164
		);
165
	}
166
167
	/**
168
	 * Add all of the non-composite fields contained within this field to the
169
	 * list.
170
	 *
171
	 * Sequentialisation is used when connecting the form to its data source
172
	 *
173
	 * @param array $list
174
	 * @param bool $saveableOnly
175
	 */
176
	public function collateDataFields(&$list, $saveableOnly = false) {
177
		foreach($this->children as $field) {
178
			if(! $field instanceof FormField) {
179
				continue;
180
			}
181
			if($field instanceof CompositeField) {
182
				$field->collateDataFields($list, $saveableOnly);
183
			}
184 View Code Duplication
			if($saveableOnly) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
185
				$isIncluded =  ($field->hasData() && !$field->isReadonly() && !$field->isDisabled());
186
			} else {
187
				$isIncluded =  ($field->hasData());
188
			}
189
			if($isIncluded) {
190
				$name = $field->getName();
191
				if($name) {
192
					$formName = (isset($this->form)) ? $this->form->FormName() : '(unknown form)';
193
					if(isset($list[$name])) {
194
						user_error("collateDataFields() I noticed that a field called '$name' appears twice in"
195
							. " your form: '{$formName}'.  One is a '{$field->class}' and the other is a"
196
							. " '{$list[$name]->class}'", E_USER_ERROR);
197
					}
198
					$list[$name] = $field;
199
				}
200
			}
201
		}
202
	}
203
204
	public function setForm($form) {
205
		foreach($this->getChildren() as $field) {
206
			if ($field instanceof FormField) {
207
				$field->setForm($form);
208
			}
209
		}
210
211
		parent::setForm($form);
212
		return $this;
213
	}
214
215
216
217
	public function setDisabled($disabled) {
218
		parent::setDisabled($disabled);
219
		foreach($this->getChildren() as $child) {
220
			$child->setDisabled($disabled);
221
		}
222
		return $this;
223
	}
224
225
	public function setReadonly($readonly)
226
	{
227
		parent::setReadonly($readonly);
228
		foreach($this->getChildren() as $child) {
229
			$child->setReadonly($readonly);
230
		}
231
		return $this;
232
	}
233
234
	public function setColumnCount($columnCount) {
235
		$this->columnCount = $columnCount;
236
		return $this;
237
	}
238
239
	public function getColumnCount() {
240
		return $this->columnCount;
241
	}
242
243
	public function isComposite() {
244
		return true;
245
	}
246
247
	public function hasData() {
248
		return false;
249
	}
250
251
	public function fieldByName($name) {
252
		return $this->children->fieldByName($name);
253
	}
254
255
	/**
256
	 * Add a new child field to the end of the set.
257
	 *
258
	 * @param FormField
259
	 */
260
	public function push(FormField $field) {
261
		$this->children->push($field);
262
	}
263
264
	/**
265
	 * Add a new child field to the beginning of the set.
266
	 *
267
	 * @param FormField
268
	 */
269
	public function unshift(FormField $field) {
270
		$this->children->unshift($field);
271
	}
272
273
	/**
274
	 * @uses FieldList->insertBefore()
275
	 *
276
	 * @param string $insertBefore
277
	 * @param FormField $field
278
	 * @return false|FormField
279
	 */
280
	public function insertBefore($insertBefore, $field) {
281
		return $this->children->insertBefore($insertBefore, $field);
282
	}
283
284
	/**
285
	 * @uses FieldList->insertAfter()
286
	 * @param string $insertAfter
287
	 * @param FormField $field
288
	 * @return false|FormField
289
	 */
290
	public function insertAfter($insertAfter, $field) {
291
		return $this->children->insertAfter($insertAfter, $field);
292
	}
293
294
	/**
295
	 * Remove a field from this CompositeField by Name.
296
	 * The field could also be inside a CompositeField.
297
	 *
298
	 * @param string $fieldName The name of the field
299
	 * @param boolean $dataFieldOnly If this is true, then a field will only
300
	 * be removed if it's a data field.  Dataless fields, such as tabs, will
301
	 * be left as-is.
302
	 */
303
	public function removeByName($fieldName, $dataFieldOnly = false) {
304
		$this->children->removeByName($fieldName, $dataFieldOnly);
305
	}
306
307
	public function replaceField($fieldName, $newField) {
308
		return $this->children->replaceField($fieldName, $newField);
309
	}
310
311
	public function rootFieldList() {
312
		if(is_object($this->containerFieldList)) return $this->containerFieldList->rootFieldList();
313
		else return $this->children;
314
	}
315
316
	public function __clone() {
317
		/** {@see FieldList::__clone(}} */
318
		$this->setChildren(clone $this->children);
319
	}
320
321
	/**
322
	 * Return a readonly version of this field. Keeps the composition but returns readonly
323
	 * versions of all the child {@link FormField} objects.
324
	 *
325
	 * @return CompositeField
326
	 */
327
	public function performReadonlyTransformation() {
328
		$newChildren = new FieldList();
329
		$clone = clone $this;
330 View Code Duplication
		if($clone->getChildren()) foreach($clone->getChildren() as $child) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
331
			/** @var FormField $child */
332
			$child = $child->transform(new ReadonlyTransformation());
333
			$newChildren->push($child);
334
		}
335
336
		$clone->setChildren($newChildren);
337
		$clone->setReadonly(true);
338
		$clone->addExtraClass($this->extraClass());
339
		$clone->setDescription($this->getDescription());
340
341
		return $clone;
342
	}
343
344
	/**
345
	 * Return a disabled version of this field. Keeps the composition but returns disabled
346
	 * versions of all the child {@link FormField} objects.
347
	 *
348
	 * @return CompositeField
349
	 */
350
	public function performDisabledTransformation() {
351
		$newChildren = new FieldList();
352
		$clone = clone $this;
353 View Code Duplication
		if($clone->getChildren()) foreach($clone->getChildren() as $child) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
354
			/** @var FormField $child */
355
			$child = $child->transform(new DisabledTransformation());
356
			$newChildren->push($child);
357
		}
358
359
		$clone->setChildren($newChildren);
360
		$clone->setDisabled(true);
361
		$clone->addExtraClass($this->extraClass());
362
		$clone->setDescription($this->getDescription());
363
		foreach($this->attributes as $k => $v) {
364
			$clone->setAttribute($k, $v);
365
		}
366
367
		return $clone;
368
	}
369
370
	public function IsReadonly() {
371
		return $this->readonly;
372
	}
373
374
	/**
375
	 * Find the numerical position of a field within
376
	 * the children collection. Doesn't work recursively.
377
	 *
378
	 * @param string|FormField
379
	 * @return int Position in children collection (first position starts with 0). Returns FALSE if the field can't
380
	 *             be found.
381
	 */
382 View Code Duplication
	public function fieldPosition($field) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
383
		if(is_string($field)) {
384
			$field = $this->fieldByName($field);
385
		}
386
		if(!$field) {
387
			return false;
388
		}
389
390
		$i = 0;
391
		foreach($this->children as $child) {
392
			/** @var FormField $child */
393
			if($child->getName() == $field->getName()) {
394
				return $i;
395
			}
396
			$i++;
397
		}
398
399
		return false;
400
	}
401
402
	/**
403
	 * Transform the named field into a readonly feld.
404
	 *
405
	 * @param string|FormField
406
	 * @return bool
407
	 */
408
	public function makeFieldReadonly($field) {
409
		$fieldName = ($field instanceof FormField) ? $field->getName() : $field;
410
411
		// Iterate on items, looking for the applicable field
412
		foreach($this->children as $i => $item) {
413
			if($item instanceof CompositeField) {
414
				if($item->makeFieldReadonly($fieldName)) {
415
					return true;
416
				};
417
			} elseif($item instanceof FormField && $item->getName() == $fieldName) {
418
				// Once it's found, use FormField::transform to turn the field into a readonly version of itself.
419
				$this->children->replaceField($fieldName, $item->transform(new ReadonlyTransformation()));
420
421
				// A true results indicates that the field was found
422
				return true;
423
			}
424
		}
425
		return false;
426
	}
427
428
	public function debug() {
429
		$result = "$this->class ($this->name) <ul>";
430
		foreach($this->children as $child) {
431
			$result .= "<li>" . Debug::text($child) . "&nbsp;</li>";
432
		}
433
		$result .= "</ul>";
434
		return $result;
435
	}
436
437
	/**
438
	 * Validate this field
439
	 *
440
	 * @param Validator $validator
441
	 * @return bool
442
	 */
443
	public function validate($validator) {
444
		$valid = true;
445
		foreach($this->children as $child){
446
			/** @var FormField $child */
447
			$valid = ($child && $child->validate($validator) && $valid);
448
		}
449
		return $valid;
450
	}
451
452
}
453