Form   F
last analyzed

Complexity

Total Complexity 158

Size/Duplication

Total Lines 1339
Duplicated Lines 0 %

Test Coverage

Coverage 51.72%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 288
dl 0
loc 1339
ccs 180
cts 348
cp 0.5172
rs 2
c 1
b 0
f 0
wmc 158

84 Methods

Rating   Name   Duplication   Size   Complexity  
A validatePost() 0 7 2
A _addField() 0 19 6
A getSubForms() 0 9 3
A getCsrfParam() 0 8 2
A getAttributes() 0 3 1
A getField() 0 6 2
A orderFields() 0 3 1
A renderFormHtml() 0 9 3
A hasField() 0 3 1
A load() 0 7 3
A removeField() 0 3 1
A setFields() 0 11 3
A renderFooter() 0 4 1
A setAttributes() 0 12 3
A getViewPath() 0 3 1
A renderHeader() 0 5 1
A registerScripts() 0 9 5
A renderCsrfField() 0 3 1
A add() 0 4 1
A getAction() 0 3 2
A init() 0 2 1
A getRawRequestData() 0 8 4
A getSubForm() 0 6 2
A formName() 0 3 1
A run() 0 4 1
A _getFormErrors() 0 10 3
A loadFromDb() 0 5 3
A hasRequestData() 0 7 2
A addError() 0 6 2
A getCsrfToken() 0 8 2
A getErrors() 0 8 3
A getFieldByPath() 0 18 5
A getValidationData() 0 5 1
A validate() 0 9 3
A generateUniqueFieldName() 0 11 3
A getFields() 0 7 2
A __construct() 0 12 3
A getAttributeLabel() 0 3 1
A hasError() 0 3 1
A setAction() 0 3 1
A offsetExists() 0 3 1
A getDdsName() 0 3 1
A getComponentDetails() 0 3 1
A get() 0 3 1
A offsetUnset() 0 3 1
A getClassLabel() 0 3 1
A offsetSet() 0 5 1
A getValue() 0 10 4
A getDataKey() 0 5 2
A __toString() 0 3 1
A setValue() 0 8 3
A getName() 0 7 2
A processRequest() 0 7 2
A getRequest() 0 5 2
A isForm() 0 3 1
A setHint() 0 4 1
A setDataKey() 0 4 1
A ddsAddField() 0 3 1
A offsetGet() 0 3 1
A getIsInput() 0 3 1
A getDataDisplay() 0 8 3
A getData() 0 9 4
A getFilterForm() 0 11 3
A getProperties() 0 12 2
A getFilterField() 0 3 1
A shouldProcess() 0 3 1
A ddsLoadFrom() 0 3 1
A getObjectToken() 0 3 1
A setRequired() 0 2 1
A setValueFromDb() 0 4 1
A processAsFilter() 0 9 4
A exportDefinition() 0 24 4
A getId() 0 5 2
A fake() 0 9 3
A setRequest() 0 3 1
A setClassLabel() 0 4 1
A setLabel() 0 4 1
A ddsSaveDefinition() 0 3 1
A getHint() 0 3 1
A ajaxValidation() 0 5 1
A getClass() 0 3 1
A reset() 0 4 2
A getValueDisplay() 0 3 1
A getLabel() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Form 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.

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 Form, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link http://www.newicon.net/neon
4
 * @copyright Copyright (c) 2016-18 Newicon Ltd
5
 * @license http://www.newicon.net/neon/license
6
 */
7
namespace neon\core\form;
8
9
use neon\core\form\fields\Field;
10
use neon\core\grid\query\IQuery;
11
use neon\core\helpers\Collection;
12
use neon\core\helpers\Hash;
13
use neon\core\traits\PropertiesTrait;
14
use neon\core\form\traits\BuilderTrait;
15
use ReflectionClass;
16
use yii\base\InvalidCallException;
17
use yii\base\UnknownPropertyException;
18
use neon\core\helpers\Arr;
19
use neon\core\helpers\Html;
20
use neon\core\form\assets\FormAsset;
21
use neon\core\form\interfaces\IField;
22
23
class Form extends FormField implements IField, \ArrayAccess
24
{
25
	use PropertiesTrait;
26
	use BuilderTrait;
27
28
	/**
29
	 * The form submission method, such as "post", "get", "put", "delete" (case-insensitive).
30
	 * Since most browsers only support "post" and "get", if other methods are given, they will
31
	 * be simulated using "post", and a hidden input will be added which contains the actual method type.
32
	 * @see \yii\web\Request::$methodParam for more details.
33
	 * @var string
34
	 */
35
	public $method = 'post';
36
	/**
37
	 * @var bool
38
	 */
39
	public $readOnly = false;
40
	/**
41
	 * @var bool
42
	 */
43
	public $printOnly = false;
44
	/**
45
	 * @var boolean whether to enable client-side data validation.
46
	 */
47
	public $enableClientValidation = true;
48
	/**
49
	 * @var boolean whether to enable AJAX-based data validation.
50
	 */
51
	public $enableAjaxValidation = true;
52
	/**
53
	 * @var boolean whether to enable AJAX-based form submission.
54
	 */
55
	public $enableAjaxSubmission = false;
56
	/**
57
	 * @var array|string the URL for performing AJAX-based validation. This property will be processed by
58
	 * [[Url::to()]]. Please refer to [[Url::to()]] for more details on how to configure this property.
59
	 * If this property is not set, it will take the value of the form's action attribute.
60
	 */
61
	public $validationUrl;
62
	/**
63
	 * @var string the name of the GET parameter indicating the validation request is an AJAX request.
64
	 */
65
	public $ajaxParam = 'ajax';
66
	/**
67
	 * @var string the type of data that you're expecting back from the server.
68
	 */
69
	public $ajaxDataType = 'json';
70
	/**
71
	 * @var boolean whether to scroll to the first error after validation.
72
	 * @since 2.0.6
73
	 */
74
	public $scrollToError = true;
75
	/**
76
	 * @var bool
77
	 */
78
	public $forceUniqueFieldNames = true;
79
	/**
80
	 * @var string  the name of the button used to submit the form
81
	 */
82
	public $submittedButton = null;
83
84
	/**
85
	 * @var bool Whether the form has been submitted - alters the js rendering as to whether errors should display
86
	 */
87
	public $isSubmitted = false;
88
89
	/**
90
	 * An array of form html attributes to be applied to the form tag
91
	 * @see \neon\core\helpers\Html::renderTagAttributes()
92
	 * @var array
93
	 */
94
	protected $_attributes = [];
95
96
	/**
97
	 * Sets html attributes where the key is the attribute name and the value is the value
98
	 * Will merge certain properties like 'class' use replace if you do not want the merge behaviour
99
	 * @param $attrs
100
	 * @param bool $replace - will replace any existing attributes
101
	 * @return $this - chainable interface
102
	 */
103 4
	public function setAttributes($attrs, $replace=false)
104
	{
105 4
		if ($replace) {
106
			$this->_attributes = $attrs;
107
		} else {
108 4
			if (isset($attrs['class'])) {
109
				$this->_attributes['class'] = ' ' . $attrs['class'];
110
				unset($attrs['class']);
111
			}
112 4
			$this->_attributes = array_merge($this->_attributes, $attrs);
113
		}
114 4
		return $this;
115
	}
116
117
	/**
118
	 * Returns all currently defined html attributes
119
	 * @return array
120
	 */
121 12
	public function getAttributes()
122
	{
123 12
		return $this->_attributes;
124
	}
125
126
	/**
127
	 * @inheritDoc
128
	 */
129
	public static $autoIdPrefix = 'neonForm';
130
131
	/**
132
	 * If this form is a sub form and repeater is true
133
	 * then this is a repeatable form instance
134
	 * @var bool
135
	 */
136
	public $repeater = false;
137
138
	// protected props configurable via getters and setter
139
	protected $_template = 'form.tpl';
140
141
	protected $_hint;
142
	protected $_label;
143
	protected $_classLabel;
144
	protected $_dataKey = null;
145
146
	/**
147
	 * Constructor.
148
	 * @inheritdoc
149
	 * @param array|string $config  - name value pairs that will be used to initialize the object properties or a string name
150
	 * if a string is provided then it is used as the name property
151
	 * @param array $config additional configuration
152
	 */
153 124
	public function __construct($name=[], array $config=[])
154
	{
155
		// The $name field can also be a configuration array
156 124
		if (is_array($name)) {
157 56
			$config = $name;
158
		}
159
		// if a name has been specified then add this to the config
160 70
		elseif (is_string($name)) {
161 70
			$config['name'] = $name;
162
		}
163
		// call the parent object construct function - this will configure the form object with the config properties
164 124
		parent::__construct($config);
165 114
	}
166
167
	/**
168
	 * initialise the form
169
	 * @return void
170
	 */
171 114
	public function init()
172
	{
173 114
	}
174
175
	/**
176
	 * @return string
177
	 */
178
	public function formName()
179
	{
180
		return $this->getInputName();
181
	}
182
183
	/**
184
	 * Set the form action url
185
	 * @param array|string $action - suitable format for Url::to
186
	 */
187 4
	public function setAction($action)
188
	{
189 4
		$this->_attributes['action'] = $action;
190 4
	}
191
192
	/**
193
	 * Return the action url - should be in format suitable for Url::to
194
	 * @return array|string
195
	 */
196 12
	public function getAction()
197
	{
198 12
		return isset($this->_attributes['action']) ? $this->_attributes['action'] : '';
199
	}
200
201
	/**
202
	 * @inheritdoc
203
	 */
204
	public function registerScripts($view=null, $mount=true)
205
	{
206
		$view = ($view === null) ? $this->getView() : $view;
207
		FormAsset::register($view);
208
		foreach ($this->getFields() as $field)
209
			$field->registerScripts($view);
210
		if ($this->isRootForm()) {
211
			if ($mount)
212
				$view->registerJs('(new Vue()).$mount("#' . $this->id . '");', $view::POS_END, $this->id . 'vue');
213
		}
214
	}
215
216
	/**
217
	 * run the widget - essentially render the form
218
	 * This follows the standard widget convention of calling run.
219
	 * Render can not be used as this is reserved in Yii widgets for rendering view template files
220
	 * @return string
221
	 */
222
	public function run()
223
	{
224
		$this->registerScripts($this->getView());
225
		return $this->renderFormHtml();
226
	}
227
228
	/**
229
	 * Render the forms html output
230
	 * @return string
231
	 */
232
	public function renderFormHtml()
233
	{
234
		$data = ['form' => $this, 'token' => $this->getObjectToken()];
235
		if ($this->isSubForm()) {
236
			$tpl = $this->repeater ? 'repeater-instance.tpl' : $this->_template;
237
			$data['parentFormId'] = $this->getForm()->getId();
238
			return $this->render($tpl, $data);
239
		}
240
		return $this->render($this->_template, $data);
241
	}
242
243
// region: Validation and error specific functions
244
// ============================================================================
245
246
	/**
247
	 * Performs the data validation.
248
	 *
249
	 * @See \yii\base\Model
250
	 *
251
	 * @param array   $names list of attribute names that should be validated.
252
	 * If this parameter is empty, it means any attribute listed in the applicable
253
	 * validation rules should be validated.
254
	 * @param boolean $clearErrors whether to call [[clearErrors()]] before performing validation
255
	 *
256
	 * @return boolean whether the validation is successful without any error.
257
	 * @throws UnknownPropertyException - if a validator attempts to access an unknown property
258
	 */
259 4
	public function validate($names = null, $clearErrors = true)
260
	{
261 4
		$valid = true;
262 4
		foreach($this->getFields($names) as $key => $field) {
263 4
			$valid = $field->validate() && $valid;
264
		}
265
		// if validation has run - you will want to see it when the form is loaded
266 4
		$this->isSubmitted = true;
267 4
		return $valid;
268
	}
269
270
	/**
271
	 * Validates one or several models and returns an error message array indexed by the attribute IDs.
272
	 * This is a helper method that simplifies the way of writing AJAX validation code.
273
	 * For example, you may use the following code in a controller action to respond
274
	 * to an AJAX validation request:
275
	 * ```php
276
	 * $model = new Post;
277
	 * $model->load($_POST);
278
	 * if (Yii::$app->request->isAjax) {
279
	 *     Yii::$app->response->format = Response::FORMAT_JSON;
280
	 *     return ActiveForm::validate($model);
281
	 * }
282
	 * // ... respond to non-AJAX request ...
283
	 * ```
284
	 * To validate multiple models, simply pass each model as a parameter to this method, like
285
	 * the following:
286
	 * ```php
287
	 * ActiveForm::validate($model1, $model2, ...);
288
	 * ```
289
	 * If this parameter is empty, it means any attribute listed in the applicable
290
	 * validation rules should be validated.
291
	 * When this method is used to validate multiple models, this parameter will be interpreted
292
	 * as a model.
293
	 * @throws UnknownPropertyException - if a validator attempts to access an unknown property
294
	 * @return array the error message array indexed by the attribute IDs.
295
	 */
296
	public function getValidationData()
297
	{
298
		$this->validate();
299
		$result = $this->_getFormErrors($this);
300
		return $result;
301
	}
302
303
	/**
304
	 * Get all form errors indexed by field id
305
	 *
306
	 * @param \neon\core\form\Form $form - A form object
307
	 * @param array $errors
308
	 * @return array
309
	 */
310
	protected function _getFormErrors($form, &$errors=[])
311
	{
312
		foreach ($form->getFields() as $field) {
313
			if ($field->isForm()) {
314
				$this->_getFormErrors($field, $errors);
0 ignored issues
show
Bug introduced by
$field of type neon\core\form\fields\Field is incompatible with the type neon\core\form\Form expected by parameter $form of neon\core\form\Form::_getFormErrors(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

314
				$this->_getFormErrors(/** @scrutinizer ignore-type */ $field, $errors);
Loading history...
315
			} else {
316
				$errors[$field->getIdPath()] = $field->getErrors();
317
			}
318
		}
319
		return $errors;
320
	}
321
322
	/**
323
	 * Store an array of errors for this object
324
	 * @var array
325
	 */
326
	private $_errors = [];
327
328
	/**
329
	 * Get all errors from this form and any fields
330
	 * @return array
331
	 */
332
	public function getErrors()
333
	{
334
		$errors = [];
335
		foreach($this->getFields() as $name => $field) {
336
			if ($field->hasError())
337
				$errors[$name] = $field->getErrors();
338
		}
339
		return array_merge($this->_errors, $errors);
340
	}
341
342
	/**
343
	 * Returns true if this form or any subform has errors
344
	 * @return boolean
345
	 */
346
	public function hasError()
347
	{
348
		return !empty($this->getErrors());
349
	}
350
351
	/**
352
	 * Adds a new error to this form.
353
	 *
354
	 * This method is dual purposed so that errors can be set directly on the form
355
	 * or, if during validation of a field that needed to pass in the form model,
356
	 * we can add the error to that field instead. This is a bit of a hack but
357
	 * required because of how validateAttribute works in Yii Validators.
358
	 *
359
	 * @param string $fieldNameOrError  if $error is not null, then this is
360
	 *   the form field name, otherwise it is the error
361
	 * @param string $error  if this is not null then it is the error
362
	 * @return void
363
	 */
364
	public function addError($fieldNameOrError='', $error=null)
365
	{
366
		if ($error === null)
367
			$this->_errors[] = $fieldNameOrError;
368
		else
369
			$this->getField($fieldNameOrError)->addError($error);
370
	}
371
372
// ============================================================================
373
// endregion
374
375
	/**
376
	 * Gets a field label - this function matches the structure of yii models so the form can
377
	 * be used as the model input for Yii validation and render functions
378
	 * @param string $attribute - the field name
379
	 * @return String
380
	 */
381
	public function getAttributeLabel($attribute)
382
	{
383
		return $this->getField($attribute)->getLabel();
384
	}
385
386
	/**
387
	 * Shortcut function to load in post data and validate the form
388
	 * @return bool
389
	 */
390
	public function validatePost()
391
	{
392
		if ($this->getRequest()->getIsPost()) {
393
			$this->load();
394
			return $this->validate();
395
		}
396
		return false;
397
	}
398
399
	/**
400
	 * @inheritdoc
401
	 */
402
	public function getViewPath()
403
	{
404
		return __DIR__ . DIRECTORY_SEPARATOR . 'views';
405
	}
406
407
	/**
408
	 * Generate a name that is unique in the context of the other fields in the form
409
	 * for e.g. if you have one field with a name of "field" in your form and you call this function with the same name of
410
	 * "field" it will return "field_1" and so on to ensure the name is unique
411
	 * @param string $name the name of the field - it will be appended with a incremented to number to enforce uniqueness
412
	 *
413
	 * @return string the unique name
414
	 */
415
	public function generateUniqueFieldName($name)
416
	{
417
		$i = 1;
418
		while ($this->hasField($name)) {
419
			if (preg_match('/.*(_[0-9]+)/', $name, $matches)) {
420
				$name = str_replace($matches[1], '_' . $i++, $name);
421
			} else {
422
				$name .= '_' . $i++;
423
			}
424
		}
425
		return $name;
426
	}
427
428
// region: Field Management
429
// ============================================================================
430
	/**
431
	 * @var array of IField Objects
432
	 */
433
	protected $_fields = [];
434
435
	/**
436
	 * Get field by its name
437
	 *
438
	 * @param string $name the name of the field
439
	 * @throws InvalidCallException if no field exists with name
440
	 * @return fields\Field|Form
441
	 */
442 58
	public function getField($name)
443
	{
444 58
		if (!isset($this->_fields[$name])) {
445
			throw new InvalidCallException("No field with the name '$name' exists in the form");
446
		}
447 58
		return $this->_fields[$name];
448
	}
449
450
	/**
451
	 * Get a field by its path
452
	 * @param string $path - '.' delimited path to field - the first item should be the current form name or id
453
	 * @return IField|Form|null
454
	 * @throws \Exception if the first part of the path is not the current form
455
	 */
456 2
	public function getFieldByPath($path)
457
	{
458
		// the root path bit can be the the name or the id of the form
459
		// all children of the root will use the name property
460
		// multiple versions of the same form can exist on a page
461
		// in these scenarios the forms each can have different root form ids.
462 2
		$bits = explode('.', $path);
463 2
		if (!($bits[0] === $this->name || ($this->isRootForm() && $bits[0] === $this->id)))
464
			throw new \Exception("The first part of the path should be the form name or the id if this is a root form. The forms name and id is: name={$this->name}, id={$this->id}. The path you used was: path=$path.");
465
		// remove first item and reindex array : note unset does not update array indexes
466 2
		array_splice($bits, 0, 1);
467 2
		$field = $this->getField($bits[0]);
468 2
		if ($field->isForm()) {
469
			// if this is a form then delegate to the sub form to find its children.
470
			// this is important for repeaters that manage a collection of forms with unique ids
471 2
			return $field->getFieldByPath(implode('.', $bits));
0 ignored issues
show
Bug introduced by
The method getFieldByPath() does not exist on neon\core\form\fields\Field. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

471
			return $field->/** @scrutinizer ignore-call */ getFieldByPath(implode('.', $bits));
Loading history...
472
		}
473 2
		return $field;
474
	}
475
476
	/**
477
	 * Returns boolean whether the field exists
478
	 *
479
	 * @param string $name
480
	 * @return bool
481
	 */
482 54
	public function hasField($name)
483
	{
484 54
		return isset($this->_fields[$name]);
485
	}
486
487
	/**
488
	 * Return an array of form field objects indexed by the field name
489
	 *
490
	 * @param array $names | null provide an array of names to return array of only the specified fields default is null
491
	 * which will return all fields
492
	 * @throws InvalidCallException if a name key specified in the $names array does not exist as a field
493
	 * @return \neon\core\form\fields\Field[] [name => field object]
494
	 */
495 62
	public function getFields($names=null)
496
	{
497 62
		$this->orderFields();
498 62
		if ($names === null)
499 62
			return $this->_fields;
500
501
		return Arr::only($this->_fields, $names);
502
	}
503
504
	/**
505
	 * Get an array of child sub forms
506
	 *
507
	 * @return \neon\core\form\Form[] indexed by form name
508
	 */
509 2
	public function getSubForms()
510
	{
511 2
		$ret = [];
512 2
		foreach ($this->_fields as $key => $field) {
513 2
			if ($field instanceof Form) {
514 2
				$ret[$key] = $field;
515
			}
516
		}
517 2
		return $ret;
518
	}
519
520
	/**
521
	 * Get a sub form
522
	 * @param string $name
523
	 * @throws \Exception if no subform exists with the name specified
524
	 * @return Form
525
	 */
526
	public function getSubForm($name)
527
	{
528
		if (! $this->hasField($name)) {
529
			throw new \Exception("No sub form exists with the name '$name'");
530
		}
531
		return $this->getField($name);
532
	}
533
534
	/**
535
	 * Order the fields by their `order` property values
536
	 */
537 62
	public function orderFields()
538
	{
539 62
		Arr::multisort($this->_fields, 'order');
540 62
	}
541
542
	/**
543
	 * Takes a configuration array which is an array of field configuration arrays
544
	 * This would look something like this:
545
	 *
546
	 * ```php
547
	 *  [
548
	 *      [
549
	 *           'class' => '\neon\core\form\fields\Text'
550
	 *           'name' => 'first_name'
551
	 *           // 'validators' => ...
552
	 *           // ... (other config options)
553
	 *      ],
554
	 *      [
555
	 *           'class' => '\neon\core\form\fields\Text'
556
	 *           'name' => 'last_name'
557
	 *           "validators" : [
558
	 *               ["string", ["max" : 10]]
559
	 *           ]
560
	 *      ],
561
	 *      'dataKey' => [
562
	 *           'class' => '\neon\core\form\fields\Text',
563
	 *           'label' => 'Thing'
564
	 *      ]
565
	 *  ]
566
	 * ```
567
	 *
568
	 * **Note** that if a key is defined for a field and no name or dataKey property exists in the config array
569
	 * then the key will be used as the ```dataKey``` and the ```name``` property for the field.
570
	 *
571
	 * Therefore given a config array of:
572
	 *
573
	 * ```php
574
	 *
575
	 * [
576
	 *     'myField' => [
577
	 *          'class' => 'neon\core\form\fields\Text'
578
	 *     ]
579
	 * ]
580
	 * ```
581
	 *
582
	 * Will add a field with the config:
583
	 *
584
	 * ```php
585
	 * [
586
	 *      'class' => 'neon\core\form\fields\Text',
587
	 *      'name' => 'myField',
588
	 *      'dataKey' => 'myField'
589
	 * ]
590
	 * ```
591
	 * @param array $config
592
	 * @return $this - make chainable method
593
	 */
594 10
	public function setFields($config)
595
	{
596 10
		foreach ($config as $key => $fieldConfig) {
597
			// if a key is defined in the array then assume we want this to become the name and the data key for the field element
598
			// this will be ignored if 'name' or 'dataKey' is explicitly defined in $fieldConfig
599 10
			if (is_string($key)) {
600 4
				$fieldConfig = Arr::merge(['name' => $key, 'dataKey' => $key], $fieldConfig);
601
			}
602 10
			$this->add($fieldConfig);
603
		}
604 10
		return $this;
605
	}
606
607
	/**
608
	 * Remove a field
609
	 * @param string $name
610
	 */
611
	public function removeField($name)
612
	{
613
		unset($this->_fields[$name]);
614
	}
615
616
	/**
617
	 * A generic add function to add fields to a form
618
	 *
619
	 * ```PHP
620
	 * $field = new \neon\core\form\fields\Email(['name' => 'email']);
621
	 * $form->add($field);
622
	 * ```
623
	 *
624
	 * @param array|IField $field a configuration array or an IField object
625
	 * @param array $defaults (optional) - This is ignored if the $field parameter is an object otherwise
626
	 *   it represents defaults for the configuration array
627
	 * @param boolean $overwrite (optional) - whether to overwrite an existing field of the same name defaults to false
628
	 * @throws exceptions\UnknownFieldClass - if the field class can not be created
629
	 * @return fields\Field
630
	 */
631 112
	public function add($field, $defaults=[], $overwrite=false)
632
	{
633 112
		$object = $this->makeField($field, $defaults);
634 104
		return $this->_addField($object, $overwrite);
635
	}
636
637
	/**
638
	 * Add a form field to this form
639
	 * @param  IField  $object instance of a form Field
640
	 * @param boolean $overwrite (optional) - Whether to overwrite any existing fields with the same name default is false
641
	 *
642
	 * @return \neon\core\form\fields\Field
643
	 */
644 104
	protected function _addField(IField $object, $overwrite=false)
645
	{
646
		// pass the field a reference to its parent form
647 104
		$object->setForm($this);
648 104
		if ($object->getName() == '') {
649
			throw new \InvalidArgumentException('A field object must have a name set before adding it to the form. You tried to add ' . print_r($object->toArray(), true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($object->toArray(), true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

649
			throw new \InvalidArgumentException('A field object must have a name set before adding it to the form. You tried to add ' . /** @scrutinizer ignore-type */ print_r($object->toArray(), true));
Loading history...
650
		}
651 104
		if ($object instanceof Field || $object instanceof Form) {
652
			// the field object seems to be valid so before we add it we just check this is not a conflict with an existing field
653
			// if overwrite is true then we overwrite with the new field
654 104
			if (!$overwrite && array_key_exists($object->getName(), $this->_fields)) {
655
				throw new \InvalidArgumentException('The field with the key "'.$object->getName().'" already exists in the forms field array');
656
			}
657
			// add this form as the parent form reference
658 104
			$this->_fields[$object->getName()] = $object;
659
		} else {
660
			throw new \InvalidArgumentException('The $object parameter was not a valid \neon\core\form\Field or \neon\core\form\Form object "' . print_r($object, true) . '" given');
0 ignored issues
show
Bug introduced by
Are you sure print_r($object, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

660
			throw new \InvalidArgumentException('The $object parameter was not a valid \neon\core\form\Field or \neon\core\form\Form object "' . /** @scrutinizer ignore-type */ print_r($object, true) . '" given');
Loading history...
661
		}
662 104
		return $object;
663
	}
664
665
// endregion
666
667
// region: Render functions
668
// ============================================================================
669
670
	/**
671
	 * render the form header html
672
	 * @return string
673
    */
674
	public function renderHeader()
675
	{
676
		$options = $this->toArray();
677
		unset($options['fields']);
678
		return "<neon-core-form-form v-bind='{$this->jsonEncode($options)}' id='{$this->id}'>";
679
	}
680
681
	/**
682
	 * Render the csrf validation token prevent - cross site request forgery
683
	 * @return string
684
	 */
685
	public function renderCsrfField()
686
	{
687
		return '<input type="hidden" name="'.$this->getRequest()->csrfParam.'" value="' . $this->getRequest()->getCsrfToken() . '" />';
688
	}
689
690
	/**
691
	 * Get the CSRF param string - this is the parameter name that the request expects the csrf token to be in
692
	 * @see $this->getCsrfToken()
693
	 * @return string
694
	 * @deprecated use $this->getRequest()->csrfParam
695
	 */
696 10
	public function getCsrfParam()
697
	{
698
		// this method assumes the form has been created via a request
699
		// which is not always true.
700
		try {
701 10
			return $this->getRequest()->csrfParam;
702
		} catch (\Exception $e) {
703
			return null;
704
		}
705
	}
706
707
	/**
708
	 * Get the csrf request token string
709
	 * @see $this->getCsrfParam()
710
	 * @return string
711
	 * @deprecated use $this->getRequest()->getCsrfToken
712
	 */
713 10
	public function getCsrfToken()
714
	{
715
		// this method assumes the form has been created via a request
716
		// which is not always true.
717
		try {
718 10
			return $this->getRequest()->csrfToken;
719
		} catch (\Exception $e) {
720
			return null;
721
		}
722
	}
723
724
	/**
725
	 * render the form footer html
726
	 * @return string
727
	 */
728
	public function renderFooter()
729
	{
730
		$this->registerScripts($this->getView());
731
		return '</neon-core-form-form>';
732
	}
733
734
// endregion
735
736
737
	/**
738
	 * Check to see if there is request data for this form
739
	 * If there is then this will return that data
740
	 * @throws \yii\base\InvalidConfigException getBodyParams may throw this error if a registered parser does not implement the [[RequestParserInterface]].
741
	 * @return bool  true if there is request data
742
	 */
743 2
	public function hasRequestData()
744
	{
745 2
		if ($this->shouldProcess()) {
746 2
			$params = $this->getRequest()->getBodyParams();
747 2
			return isset($params[$this->getName()]);
748
		}
749
		return false;
750
	}
751
752
	/**
753
	 * Get hold of the raw unprocessed form request data. To get the
754
	 * actual form data use @see getData
755
	 * @param $key  if passed in, select data[$key]
0 ignored issues
show
Bug introduced by
The type neon\core\form\if was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
756
	 * @throws \yii\base\InvalidConfigException getBodyParams may throw this error if a registered parser does not implement the [[RequestParserInterface]].
757
	 * @return array  the request data
758
	 */
759 2
	public function getRawRequestData($key=null)
760
	{
761 2
		$params = $this->getRequest()->getBodyParams();
762 2
		$data = isset($params[$this->getName()]) ? $params[$this->getName()] : [];
763 2
		if (!empty($key)) {
764
			return isset($data[$key]) ? $data[$key] : [];
765
		}
766 2
		return $data;
767
	}
768
769
	/**
770
	 * Load data into the form
771
	 * typical example ```$form->load(neon()->request->post())```
772
	 * The passed in array can be indexed by the form name. e.g. [[FormName] => ['field_1' => 'val', 'field2' => 'val']]
773
	 * Or simply be a list of fields the load function checks to see if the key is its form name or a name of one of its
774
	 * fields.
775
	 *
776
	 * Load is really only a short cut method when you do not want to do the following:
777
	 *
778
	 * ```php
779
	 * // if you call load without parameters
780
	 * $this->load(); // This will look for the form name as a post parameter key which is equivalent to the following:
781
	 * $this->setValue(neon()->request->post('my_form_name'));
782
	 * // equivalent of:
783
	 * $this->load(neon()->request->post('my_form_name'));
784
	 * ```
785
	 *
786
	 * @param array|null $data  if null will attempt to load the form from the current request data
787
	 * @throws \Exception - if request object is configured incorrectly
788
	 * @return $this
789
	 */
790 48
	public function load($data=[])
791
	{
792 48
		if (empty($data) && $this->hasRequestData()) {
793 2
			$data = $this->getRawRequestData();
794
		}
795 48
		$this->setValue($data);
796 48
		return $this;
797
	}
798
799
	/**
800
	 * Load form data from the system representations - these will be data in
801
	 * the same format as is generated by the getData() function
802
	 *
803
	 * @param array $data
804
	 */
805 2
	public function loadFromDb($data)
806
	{
807 2
		foreach ($data as $name => $value) {
808 2
			if ($this->hasField($name)) {
809 2
				$this->getField($name)->setValueFromDb($value);
810
			}
811
		}
812 2
	}
813
814
	/**
815
	 * @inheritdoc
816
	 */
817 48
	public function getData()
818
	{
819 48
		$data = [];
820 48
		foreach ($this->getFields() as $field) {
821 48
			if ($field->deleted) continue;
822 48
			if ($field->getIsInput())
823 48
				$data[$field->getDataKey()] = $field->getData();
824
		}
825 48
		return $data;
826
	}
827
828
	/**
829
	 * Gets a representation of the forms data by its value display
830
	 * Typically this will use the human readable output for the fields of the form
831
	 * @return array
832
	 */
833
	public function getDataDisplay()
834
	{
835
		$data = [];
836
		foreach ($this->getFields() as $field) {
837
			if ($field->getIsInput())
838
				$data[$field->getName()] = $field->getValueDisplay();
839
		}
840
		return $data;
841
	}
842
843
// region: implement the IField interface - this allows forms to be used as a field within a parent form
844
// =================================================================================================================
845
846
	/**
847
	 *  @inheritdoc
848
	 */
849 2
	public function getDataKey()
850
	{
851 2
		if ($this->_dataKey !== null)
852 2
			return $this->_dataKey;
853 2
		return $this->getName();
854
	}
855
856
	/**
857
	 * @inheritdoc
858
	 */
859 4
	public function setDataKey($key)
860
	{
861 4
		$this->_dataKey = $key;
862 4
		return $this;
863
	}
864
865
	/**
866
	 * @inheritdoc
867
	 */
868 14
	public function getName()
869
	{
870 14
		if ($this->_name == null) {
871
			$reflector = new ReflectionClass($this);
872
			$this->_name = $reflector->getShortName();
873
		}
874 14
		return $this->_name;
875
	}
876
877
	/**
878
	 * Root forms always require an id
879
	 * @return String
880
	 */
881 12
	public function getId($autoGenerate=false)
882
	{
883 12
		if ($this->_id)
884 2
			return $this->_id;
885 12
		return $this->getName();
886
	}
887
888
	/**
889
	 * @inheritDoc
890
	 */
891 52
	public function setValue($value)
892
	{
893 52
		foreach ($value as $name => $val) {
894 52
			if ($this->hasField($name)) {
895 52
				$this->getField($name)->setValue($val);
896
			}
897
		}
898 52
		return $this;
899
	}
900
901
	/**
902
	 * @inheritDoc
903
	 */
904 2
	public function setValueFromDb($value)
905
	{
906 2
		$this->loadFromDb($value);
907 2
		return $this;
908
	}
909
910
	/**
911
	 * @inheritdoc
912
	 */
913 46
	public function getValue()
914
	{
915 46
		$value = [];
916 46
		foreach ($this->getFields() as $field) {
917 46
			if (!$field->deleted) {
918 46
				if ($field->getIsInput())
919 46
					$value[$field->getDataKey()] = $field->getValue();
920
			}
921
		}
922 46
		return $value;
923
	}
924
925
	/**
926
	 * Get the hint message for this field
927
	 * @return string
928
	 */
929 12
	public function getHint()
930
	{
931 12
		return $this->_hint;
932
	}
933
934
	/**
935
	 * Set the hint for this field
936
	 *
937
	 * @param string $hint
938
	 *
939
	 * @return $this
940
	 */
941 10
	public function setHint($hint)
942
	{
943 10
		$this->_hint = $hint;
944 10
		return $this;
945
	}
946
947
	/**
948
	 * Set the label for the field
949
	 *
950
	 * @param string $label
951
	 * @return $this is a chainable method
952
	 */
953 10
	public function setLabel($label)
954
	{
955 10
		$this->_label = $label;
956 10
		return $this;
957
	}
958
959
	/**
960
	 * Return the label for this field
961
	 * @return $this is a chainable method
962
	 */
963 12
	public function getLabel()
964
	{
965 12
		return $this->_label;
966
	}
967
968
	/**
969
	 * Set the classLabel for the field
970
	 *  a user friendly name for the field class
971
	 * @param string $label
972
	 * @return $this is a chainable method
973
	 */
974
	public function setClassLabel($label)
975
	{
976
		$this->_classLabel = $label;
977
		return $this;
978
	}
979
980
	/**
981
	 * Return the class label for this field
982
	 * @return $this is a chainable method
983
	 */
984
	public function getClassLabel()
985
	{
986
		return $this->_classLabel;
987
	}
988
989
	/**
990
	 * Whether this is a form
991
	 * @return bool
992
	 */
993 12
	public function isForm()
994
	{
995 12
		return true;
996
	}
997
998
	/**
999
	 * @inheritdoc - IField implementation,
1000
	 * Possibly applicable to the form - perhaps if set the form will complain unless all its children are set??
1001
	 */
1002
	public function setRequired($required=true)
1003
	{}
1004
// endregion
1005
1006
	/**
1007
	 * Helper function to reduce controller code
1008
	 * if the form supports ajax validation this will return
1009
	 * the appropriate validation array data and set the response object to process as json
1010
	 * note the result of this function should be returned by the controller action.
1011
	 *
1012
	 * Inside the controller action:
1013
	 *
1014
	 * ~~~php
1015
	 * if (neon()->request->isAjax) {
1016
	 *     return $form->ajaxValidation()
1017
	 * }
1018
	 * ~~~
1019
	 *
1020
	 * Steve: Personally I dislike this code - I dislike the current yii ajax form validation clogging up my controller actions
1021
	 * The ajax validation is so tightly coupled to the Form and the JQuery yiiActiveForm plugin that the controller
1022
	 * just gets in the way - its only job is to know its ajax - perhaps setting up a form object convention so we can
1023
	 * have a more generic ajax validation route for forms would remove this layer of cruft from every single controller action
1024
	 * @throws UnknownPropertyException - if a validator attempts to access an unknown property
1025
	 *
1026
	 * @return array
1027
	 */
1028
	public function ajaxValidation()
1029
	{
1030
		$this->load();
1031
		$this->validate();
1032
		return $this->getValidationData();
1033
	}
1034
1035
	/**
1036
	 * Process the form in the context of http request
1037
	 * The form class knows generally how it sends data via its method property
1038
	 * Therefore process the data in the context of the form - load the data and validate against the form fields
1039
	 * @return boolean true if validate if data is loaded and validates successfully
1040
	 */
1041
	public function processRequest()
1042
	{
1043
		if ($this->hasRequestData()) {
1044
			$this->load();
1045
			return $this->validate();
1046
		}
1047
		return false;
1048
	}
1049
1050
	/**
1051
	 * Whether the form should process the request.
1052
	 * Essentially if the current request method matches the form method.
1053
	 * Typically if the request is POST and the form method is POST this function will return true.
1054
	 * @return bool
1055
	 */
1056 2
	public function shouldProcess()
1057
	{
1058 2
		return $this->getRequest()->getMethod() == strtoupper($this->method);
1059
	}
1060
1061
	/**
1062
	 * @return string
1063
	 */
1064
	public function __toString()
1065
	{
1066
		return $this->run();
1067
	}
1068
1069
	/**
1070
	 * Array serialize
1071
	 */
1072 12
	public function getProperties()
1073
	{
1074
		$props = [
1075 12
			'class', 'id', 'name', 'enableAjaxValidation','enableAjaxSubmission',
1076
			'label', 'hint', 'inline', 'visible', 'order', 'action', 'readOnly', 'printOnly', 'validationUrl',
1077
			'attributes', 'fields', 'objectToken', 'isSubmitted'
1078
		];
1079 12
		if ($this->isRootForm()) {
1080 10
			$props[] = 'csrfParam';
1081 10
			$props[] = 'csrfToken';
1082
		}
1083 12
		return $props;
1084
	}
1085
1086
	/**
1087
	 * Get the full class name of this class
1088
	 * @return string
1089
	 */
1090 12
	public function getClass()
1091
	{
1092 12
		return get_class($this);
1093
	}
1094
1095
	/**
1096
	 * Return the rendered output of the form
1097
	 * @return string
1098
	 */
1099
	public function getValueDisplay($context='')
1100
	{
1101
		return $this->run();
1102
	}
1103
1104
	/* =========================================================================
1105
	 * Creation and Extraction of forms by Definition
1106
	 * =========================================================================
1107
	 *
1108
	 * The form definition is defined partially by the form and partially
1109
	 * by the systems that will deal with storing this information
1110
	 *
1111
	 * =========================================================================
1112
	 */
1113
1114
	/**
1115
	 * Get the definition of this form suitable for use elsewhere.
1116
	 * This is not the same as getting the toArray version of this form for
1117
	 * the following reasons:
1118
	 *
1119
	 * 1. there are additional values that whilst they could be extracted out from
1120
	 * the toArray data elsewhere puts the responsibility for knowing how to in
1121
	 * the wrong place (it forces data scrapping). A change in definition here
1122
	 * should not break code elsewhere.
1123
	 *
1124
	 * 2. there are irrelevant fields on the form that should be removed. These
1125
	 * are any that are to do with security between client and server but which
1126
	 * don't change the meaning of the form. Only those values that are properly
1127
	 * part of the form definition should be returned.
1128
	 */
1129 6
	public function exportDefinition()
1130
	{
1131 6
		$definition = $this->toArray();
1132
		// remove any irrelevant fields or ones that will be created separately
1133 6
		unset($definition['csrfParam']);
1134 6
		unset($definition['csrfToken']);
1135 6
		unset($definition['fields']);
1136 6
		unset($definition['errors']);
1137
1138
		$formDefinition = [
1139 6
			'name' => $this->getName(),
1140 6
			'label' => $this->getLabel(),
1141 6
			'description' => $this->getHint(),
1142 6
			'definition' => $definition
1143
		];
1144 6
		$fields = [];
1145 6
		foreach ($this->getFields() as $key => $field) {
1146 6
			$fieldDefinition = $field->exportDefinition();
1147
1148 6
			if (! is_null($field->ddsDataType) && ! is_null($fieldDefinition) )
1149 6
				$fields[$key] = $fieldDefinition;
1150
		}
1151 6
		$formDefinition['definition']['fields'] = $fields;
1152 6
		return $formDefinition;
1153
	}
1154
1155
	// TODO: remove the need for this function!
1156
	public function getDdsName()
1157
	{
1158
		return preg_replace("/[^a-z0-9_]/", '', strtolower(preg_replace("/ +/", '_', $this->getName())));
1159
	}
1160
1161
// endregion
1162
1163
// region: ArrayAccess implementation methods
1164
// ============================================================================
1165
1166
	/**
1167
	 * @inheritDoc
1168
	 */
1169
	public function offsetExists($offset)
1170
	{
1171
		return $this->hasField($offset);
1172
	}
1173
1174
	/**
1175
	 * @inheritDoc
1176
	 */
1177 52
	public function offsetGet($offset)
1178
	{
1179 52
		return $this->getField($offset);
1180
	}
1181
1182
	/**
1183
	 * @inheritDoc
1184
	 */
1185
	public function offsetSet($offset, $value)
1186
	{
1187
		$object = $this->makeField($value);
1188
		$object->setName($offset);
1189
		return $this->add($object, [], true);
1190
	}
1191
1192
	/**
1193
	 * @inheritDoc
1194
	 */
1195
	public function offsetUnset($offset)
1196
	{
1197
		$this->removeField($offset);
1198
	}
1199
// endregion
1200
1201
// region: Filter Form
1202
// ============================================================================
1203
	/**
1204
	 * Asks each field member for a suitable field to represent its search data.  Then returns this as a form object.
1205
	 * Some member fields will have different fields to represent them in a search form.
1206
	 * For example a Date field may return a DateRange field in order to search.
1207
	 * A checkbox may return a drop down list of search choices e.g. ("on" | "off" | "all")
1208
	 *
1209
	 * @param array $config
1210
	 *
1211
	 * @return Form
1212
	 */
1213
	public function getFilterForm($config=[])
1214
	{
1215
		$searchForm = new Form($config);
1216
		foreach($this->getFields() as $field) {
1217
			$filterField = $field->getFilterField();
1218
1219
			if ($filterField !== false) {
1220
				$searchForm->add($field->getFilterField(), ['name' => $field->getName()]);
1221
			}
1222
		}
1223
		return $searchForm;
1224
	}
1225
1226
	/**
1227
	 * @inheritDoc
1228
	 */
1229
	public function getFilterField()
1230
	{
1231
		return $this->getFilterForm();
1232
	}
1233
1234
	/**
1235
	 * @inheritDoc
1236
	 */
1237
	public function processAsFilter(IQuery $query, $searchData = null)
1238
	{
1239
		$searchData = ($searchData === null) ? $this->getValue() : $searchData;
1240
		foreach($this->getFields() as $key => $field) {
1241
			if (isset($searchData[$key])) {
1242
				$field->processAsFilter($query, $searchData[$key]);
1243
			}
1244
		}
1245
		return $query;
1246
	}
1247
// endregion
1248
1249
	/**
1250
	 * @var \neon\core\web\Request
1251
	 */
1252
	protected $_request;
1253
1254
	/**
1255
	 * Returns the request object
1256
	 * @return \neon\core\web\Request
1257
	 */
1258 12
	public function getRequest()
1259
	{
1260 12
		if ($this->_request === null)
1261 10
			$this->_request = neon()->request;
1262 12
		return $this->_request;
1263
	}
1264
1265
	/**
1266
	 * Set the request object the form should use
1267
	 *
1268
	 * @param \neon\core\web\Request $request
1269
	 */
1270 6
	public function setRequest($request)
1271
	{
1272 6
		$this->_request = $request;
1273 6
	}
1274
1275
	/**
1276
	 * Reset all the values within the form
1277
	 */
1278
	public function reset()
1279
	{
1280
		foreach($this->getFields() as $field)
1281
			$field->reset();
1282
	}
1283
1284
	/**
1285
	 * @inheritdoc
1286
	 */
1287 2
	public function getIsInput()
1288
	{
1289 2
		return true;
1290
	}
1291
1292
	/**
1293
	 * Generate fake data
1294
	 * @return array
1295
	 */
1296
	public function fake()
1297
	{
1298
		$fake = [];
1299
		foreach($this->getFields() as $key => $field) {
1300
			$val = $field->fake();
1301
			if (empty($val)) continue;
1302
			$fake[$key] = $val;
1303
		}
1304
		return $fake;
1305
	}
1306
1307
	/**
1308
	 * @deprecated should be using phoebe this should be replaced with using the loadFromDefinition via phoebe
1309
	 * @see Deprecated::ddsLoadFrom
1310
	 */
1311
	public function ddsLoadFrom($classType, $includeDeleted=false)
1312
	{
1313
		return Deprecated::ddsLoadFrom($this, $classType, $includeDeleted);
0 ignored issues
show
Deprecated Code introduced by
The function neon\core\form\Deprecated::ddsLoadFrom() has been deprecated: should be using phoebe this should be replaced with using the loadFromDefinition via phoebe ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1313
		return /** @scrutinizer ignore-deprecated */ Deprecated::ddsLoadFrom($this, $classType, $includeDeleted);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1314
	}
1315
1316
	/**
1317
	 * @deprecated - should be using phoebe
1318
	 * @see Deprecated::ddsSaveDefinition
1319
	 */
1320
	public function ddsSaveDefinition($classType=null, $ddsMembers=null)
1321
	{
1322
		Deprecated::ddsSaveDefinition($this, $classType, $ddsMembers);
0 ignored issues
show
Deprecated Code introduced by
The function neon\core\form\Deprecated::ddsSaveDefinition() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1322
		/** @scrutinizer ignore-deprecated */ Deprecated::ddsSaveDefinition($this, $classType, $ddsMembers);
Loading history...
1323
	}
1324
1325
	/**
1326
	 * @deprecated should be using phoebe
1327
	 * @see Deprecated::ddsAddField
1328
	 */
1329
	public function ddsAddField($member)
1330
	{
1331
		Deprecated::ddsAddField($this, $member);
0 ignored issues
show
Deprecated Code introduced by
The function neon\core\form\Deprecated::ddsAddField() has been deprecated: should be using phoebe ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1331
		/** @scrutinizer ignore-deprecated */ Deprecated::ddsAddField($this, $member);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1332
	}
1333
1334
	/**
1335
	 * Generate a token pointing to a serialised version of the form
1336
	 * This can then be unserialised using Hash::getObjectFromToken($token);
1337
	 * We only ever seralize the root form token - todo - seems to break properties trait
1338
	 * @return string
1339
	 * @throws \ReflectionException
1340
	 */
1341 12
	public function getObjectToken()
1342
	{
1343 12
		return Hash::setObjectToToken($this);
1344
	}
1345
1346
	/**
1347
	 * @inheritdoc
1348
	 */
1349
	public function getComponentDetails()
1350
	{
1351
		return false;
1352
	}
1353
1354
	/**
1355
	 * Alias of getField
1356
	 * @param $name
1357
	 * @return Field|Form
1358
	 */
1359
	public function get($name)
1360
	{
1361
		return $this->getField($name);
1362
	}
1363
}
1364