Completed
Push — master ( fce20a...834936 )
by Dmitry
13:44 queued 10:44
created

ActiveForm::getClientOptions()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 32
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 2.0011

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 14
cts 15
cp 0.9333
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 25
nc 2
nop 0
crap 2.0011
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\widgets;
9
10
use Yii;
11
use yii\base\InvalidCallException;
12
use yii\base\Widget;
13
use yii\base\Model;
14
use yii\helpers\ArrayHelper;
15
use yii\helpers\Url;
16
use yii\helpers\Html;
17
use yii\helpers\Json;
18
19
/**
20
 * ActiveForm is a widget that builds an interactive HTML form for one or multiple data models.
21
 *
22
 * For more details and usage information on ActiveForm, see the [guide article on forms](guide:input-forms).
23
 *
24
 * @author Qiang Xue <[email protected]>
25
 * @since 2.0
26
 */
27
class ActiveForm extends Widget
28
{
29
    /**
30
     * @var array|string the form action URL. This parameter will be processed by [[\yii\helpers\Url::to()]].
31
     * @see method for specifying the HTTP method for this form.
32
     */
33
    public $action = '';
34
    /**
35
     * @var string the form submission method. This should be either `post` or `get`. Defaults to `post`.
36
     *
37
     * When you set this to `get` you may see the url parameters repeated on each request.
38
     * This is because the default value of [[action]] is set to be the current request url and each submit
39
     * will add new parameters instead of replacing existing ones.
40
     * You may set [[action]] explicitly to avoid this:
41
     *
42
     * ```php
43
     * $form = ActiveForm::begin([
44
     *     'method' => 'get',
45
     *     'action' => ['controller/action'],
46
     * ]);
47
     * ```
48
     */
49
    public $method = 'post';
50
    /**
51
     * @var array the HTML attributes (name-value pairs) for the form tag.
52
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
53
     */
54
    public $options = [];
55
    /**
56
     * @var string the default field class name when calling [[field()]] to create a new field.
57
     * @see fieldConfig
58
     */
59
    public $fieldClass = 'yii\widgets\ActiveField';
60
    /**
61
     * @var array|\Closure the default configuration used by [[field()]] when creating a new field object.
62
     * This can be either a configuration array or an anonymous function returning a configuration array.
63
     * If the latter, the signature should be as follows:
64
     *
65
     * ```php
66
     * function ($model, $attribute)
67
     * ```
68
     *
69
     * The value of this property will be merged recursively with the `$options` parameter passed to [[field()]].
70
     *
71
     * @see fieldClass
72
     */
73
    public $fieldConfig = [];
74
    /**
75
     * @var bool whether to perform encoding on the error summary.
76
     */
77
    public $encodeErrorSummary = true;
78
    /**
79
     * @var string the default CSS class for the error summary container.
80
     * @see errorSummary()
81
     */
82
    public $errorSummaryCssClass = 'error-summary';
83
    /**
84
     * @var string the CSS class that is added to a field container when the associated attribute is required.
85
     */
86
    public $requiredCssClass = 'required';
87
    /**
88
     * @var string the CSS class that is added to a field container when the associated attribute has validation error.
89
     */
90
    public $errorCssClass = 'has-error';
91
    /**
92
     * @var string the CSS class that is added to a field container when the associated attribute is successfully validated.
93
     */
94
    public $successCssClass = 'has-success';
95
    /**
96
     * @var string the CSS class that is added to a field container when the associated attribute is being validated.
97
     */
98
    public $validatingCssClass = 'validating';
99
    /**
100
     * @var bool whether to enable client-side data validation.
101
     * If [[ActiveField::enableClientValidation]] is set, its value will take precedence for that input field.
102
     */
103
    public $enableClientValidation = true;
104
    /**
105
     * @var bool whether to enable AJAX-based data validation.
106
     * If [[ActiveField::enableAjaxValidation]] is set, its value will take precedence for that input field.
107
     */
108
    public $enableAjaxValidation = false;
109
    /**
110
     * @var bool whether to hook up `yii.activeForm` JavaScript plugin.
111
     * This property must be set `true` if you want to support client validation and/or AJAX validation, or if you
112
     * want to take advantage of the `yii.activeForm` plugin. When this is `false`, the form will not generate
113
     * any JavaScript.
114
     * @see registerClientScript
115
     */
116
    public $enableClientScript = true;
117
    /**
118
     * @var array|string the URL for performing AJAX-based validation. This property will be processed by
119
     * [[Url::to()]]. Please refer to [[Url::to()]] for more details on how to configure this property.
120
     * If this property is not set, it will take the value of the form's action attribute.
121
     */
122
    public $validationUrl;
123
    /**
124
     * @var bool whether to perform validation when the form is submitted.
125
     */
126
    public $validateOnSubmit = true;
127
    /**
128
     * @var bool whether to perform validation when the value of an input field is changed.
129
     * If [[ActiveField::validateOnChange]] is set, its value will take precedence for that input field.
130
     */
131
    public $validateOnChange = true;
132
    /**
133
     * @var bool whether to perform validation when an input field loses focus.
134
     * If [[ActiveField::$validateOnBlur]] is set, its value will take precedence for that input field.
135
     */
136
    public $validateOnBlur = true;
137
    /**
138
     * @var bool whether to perform validation while the user is typing in an input field.
139
     * If [[ActiveField::validateOnType]] is set, its value will take precedence for that input field.
140
     * @see validationDelay
141
     */
142
    public $validateOnType = false;
143
    /**
144
     * @var int number of milliseconds that the validation should be delayed when the user types in the field
145
     * and [[validateOnType]] is set `true`.
146
     * If [[ActiveField::validationDelay]] is set, its value will take precedence for that input field.
147
     */
148
    public $validationDelay = 500;
149
    /**
150
     * @var string the name of the GET parameter indicating the validation request is an AJAX request.
151
     */
152
    public $ajaxParam = 'ajax';
153
    /**
154
     * @var string the type of data that you're expecting back from the server.
155
     */
156
    public $ajaxDataType = 'json';
157
    /**
158
     * @var bool whether to scroll to the first error after validation.
159
     * @since 2.0.6
160
     */
161
    public $scrollToError = true;
162
    /**
163
     * @var int offset in pixels that should be added when scrolling to the first error.
164
     * @since 2.0.11
165
     */
166
    public $scrollToErrorOffset = 0;
167
    /**
168
     * @var array the client validation options for individual attributes. Each element of the array
169
     * represents the validation options for a particular attribute.
170
     * @internal
171
     */
172
    public $attributes = [];
173
174
    /**
175
     * @var ActiveField[] the ActiveField objects that are currently active
176
     */
177
    private $_fields = [];
178
179
180
    /**
181
     * Initializes the widget.
182
     * This renders the form open tag.
183
     */
184 32
    public function init()
185
    {
186 32
        if (!isset($this->options['id'])) {
187 32
            $this->options['id'] = $this->getId();
188
        }
189 32
        ob_start();
190 32
        ob_implicit_flush(false);
191 32
    }
192
193
    /**
194
     * Runs the widget.
195
     * This registers the necessary JavaScript code and renders the form open and close tags.
196
     * @throws InvalidCallException if `beginField()` and `endField()` calls are not matching.
197
     */
198 32
    public function run()
199
    {
200 32
        if (!empty($this->_fields)) {
201
            throw new InvalidCallException('Each beginField() should have a matching endField() call.');
202
        }
203
204 32
        $content = ob_get_clean();
205 32
        echo Html::beginForm($this->action, $this->method, $this->options);
206 32
        echo $content;
207
208 32
        if ($this->enableClientScript) {
209 1
            $this->registerClientScript();
210
        }
211
212 32
        echo Html::endForm();
213 32
    }
214
215
    /**
216
     * This registers the necessary JavaScript code.
217
     */
218 1
    public function registerClientScript()
219
    {
220 1
        $id = $this->options['id'];
221 1
        $options = Json::htmlEncode($this->getClientOptions());
222 1
        $attributes = Json::htmlEncode($this->attributes);
223 1
        $view = $this->getView();
224 1
        ActiveFormAsset::register($view);
225 1
        $view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);");
226 1
    }
227
228
    /**
229
     * Returns the options for the form JS widget.
230
     * @return array the options.
231
     */
232 1
    protected function getClientOptions()
233
    {
234
        $options = [
235 1
            'encodeErrorSummary' => $this->encodeErrorSummary,
236 1
            'errorSummary' => '.' . implode('.', preg_split('/\s+/', $this->errorSummaryCssClass, -1, PREG_SPLIT_NO_EMPTY)),
237 1
            'validateOnSubmit' => $this->validateOnSubmit,
238 1
            'errorCssClass' => $this->errorCssClass,
239 1
            'successCssClass' => $this->successCssClass,
240 1
            'validatingCssClass' => $this->validatingCssClass,
241 1
            'ajaxParam' => $this->ajaxParam,
242 1
            'ajaxDataType' => $this->ajaxDataType,
243 1
            'scrollToError' => $this->scrollToError,
244 1
            'scrollToErrorOffset' => $this->scrollToErrorOffset,
245
        ];
246 1
        if ($this->validationUrl !== null) {
247
            $options['validationUrl'] = Url::to($this->validationUrl);
248
        }
249
250
        // only get the options that are different from the default ones (set in yii.activeForm.js)
251 1
        return array_diff_assoc($options, [
252 1
            'encodeErrorSummary' => true,
253
            'errorSummary' => '.error-summary',
254
            'validateOnSubmit' => true,
255
            'errorCssClass' => 'has-error',
256
            'successCssClass' => 'has-success',
257
            'validatingCssClass' => 'validating',
258
            'ajaxParam' => 'ajax',
259
            'ajaxDataType' => 'json',
260
            'scrollToError' => true,
261
            'scrollToErrorOffset' => 0,
262
        ]);
263
    }
264
265
    /**
266
     * Generates a summary of the validation errors.
267
     * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden.
268
     * @param Model|Model[] $models the model(s) associated with this form.
269
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
270
     *
271
     * - `header`: string, the header HTML for the error summary. If not set, a default prompt string will be used.
272
     * - `footer`: string, the footer HTML for the error summary.
273
     *
274
     * The rest of the options will be rendered as the attributes of the container tag. The values will
275
     * be HTML-encoded using [[\yii\helpers\Html::encode()]]. If a value is `null`, the corresponding attribute will not be rendered.
276
     * @return string the generated error summary.
277
     * @see errorSummaryCssClass
278
     */
279
    public function errorSummary($models, $options = [])
280
    {
281
        Html::addCssClass($options, $this->errorSummaryCssClass);
282
        $options['encode'] = $this->encodeErrorSummary;
283
        return Html::errorSummary($models, $options);
284
    }
285
286
    /**
287
     * Generates a form field.
288
     * A form field is associated with a model and an attribute. It contains a label, an input and an error message
289
     * and use them to interact with end users to collect their inputs for the attribute.
290
     * @param Model $model the data model.
291
     * @param string $attribute the attribute name or expression. See [[Html::getAttributeName()]] for the format
292
     * about attribute expression.
293
     * @param array $options the additional configurations for the field object. These are properties of [[ActiveField]]
294
     * or a subclass, depending on the value of [[fieldClass]].
295
     * @return ActiveField the created ActiveField object.
296
     * @see fieldConfig
297
     */
298 4
    public function field($model, $attribute, $options = [])
299
    {
300 4
        $config = $this->fieldConfig;
301 4
        if ($config instanceof \Closure) {
302
            $config = call_user_func($config, $model, $attribute);
303
        }
304 4
        if (!isset($config['class'])) {
305 4
            $config['class'] = $this->fieldClass;
306
        }
307 4
        return Yii::createObject(ArrayHelper::merge($config, $options, [
308 4
            'model' => $model,
309 4
            'attribute' => $attribute,
310 4
            'form' => $this,
311
        ]));
312
    }
313
314
    /**
315
     * Begins a form field.
316
     * This method will create a new form field and returns its opening tag.
317
     * You should call [[endField()]] afterwards.
318
     * @param Model $model the data model.
319
     * @param string $attribute the attribute name or expression. See [[Html::getAttributeName()]] for the format
320
     * about attribute expression.
321
     * @param array $options the additional configurations for the field object.
322
     * @return string the opening tag.
323
     * @see endField()
324
     * @see field()
325
     */
326
    public function beginField($model, $attribute, $options = [])
327
    {
328
        $field = $this->field($model, $attribute, $options);
329
        $this->_fields[] = $field;
330
        return $field->begin();
331
    }
332
333
    /**
334
     * Ends a form field.
335
     * This method will return the closing tag of an active form field started by [[beginField()]].
336
     * @return string the closing tag of the form field.
337
     * @throws InvalidCallException if this method is called without a prior [[beginField()]] call.
338
     */
339
    public function endField()
340
    {
341
        $field = array_pop($this->_fields);
342
        if ($field instanceof ActiveField) {
343
            return $field->end();
344
        } else {
345
            throw new InvalidCallException('Mismatching endField() call.');
346
        }
347
    }
348
349
    /**
350
     * Validates one or several models and returns an error message array indexed by the attribute IDs.
351
     * This is a helper method that simplifies the way of writing AJAX validation code.
352
     *
353
     * For example, you may use the following code in a controller action to respond
354
     * to an AJAX validation request:
355
     *
356
     * ```php
357
     * $model = new Post;
358
     * $model->load(Yii::$app->request->post());
359
     * if (Yii::$app->request->isAjax) {
360
     *     Yii::$app->response->format = Response::FORMAT_JSON;
361
     *     return ActiveForm::validate($model);
362
     * }
363
     * // ... respond to non-AJAX request ...
364
     * ```
365
     *
366
     * To validate multiple models, simply pass each model as a parameter to this method, like
367
     * the following:
368
     *
369
     * ```php
370
     * ActiveForm::validate($model1, $model2, ...);
371
     * ```
372
     *
373
     * @param Model $model the model to be validated.
374
     * @param mixed $attributes list of attributes that should be validated.
375
     * If this parameter is empty, it means any attribute listed in the applicable
376
     * validation rules should be validated.
377
     *
378
     * When this method is used to validate multiple models, this parameter will be interpreted
379
     * as a model.
380
     *
381
     * @return array the error message array indexed by the attribute IDs.
382
     */
383
    public static function validate($model, $attributes = null)
384
    {
385
        $result = [];
386
        if ($attributes instanceof Model) {
387
            // validating multiple models
388
            $models = func_get_args();
389
            $attributes = null;
390
        } else {
391
            $models = [$model];
392
        }
393
        /* @var $model Model */
394
        foreach ($models as $model) {
395
            $model->validate($attributes);
396
            foreach ($model->getErrors() as $attribute => $errors) {
397
                $result[Html::getInputId($model, $attribute)] = $errors;
398
            }
399
        }
400
401
        return $result;
402
    }
403
404
    /**
405
     * Validates an array of model instances and returns an error message array indexed by the attribute IDs.
406
     * This is a helper method that simplifies the way of writing AJAX validation code for tabular input.
407
     *
408
     * For example, you may use the following code in a controller action to respond
409
     * to an AJAX validation request:
410
     *
411
     * ```php
412
     * // ... load $models ...
413
     * if (Yii::$app->request->isAjax) {
414
     *     Yii::$app->response->format = Response::FORMAT_JSON;
415
     *     return ActiveForm::validateMultiple($models);
416
     * }
417
     * // ... respond to non-AJAX request ...
418
     * ```
419
     *
420
     * @param array $models an array of models to be validated.
421
     * @param mixed $attributes list of attributes that should be validated.
422
     * If this parameter is empty, it means any attribute listed in the applicable
423
     * validation rules should be validated.
424
     * @return array the error message array indexed by the attribute IDs.
425
     */
426
    public static function validateMultiple($models, $attributes = null)
427
    {
428
        $result = [];
429
        /* @var $model Model */
430
        foreach ($models as $i => $model) {
431
            $model->validate($attributes);
432
            foreach ($model->getErrors() as $attribute => $errors) {
433
                $result[Html::getInputId($model, "[$i]" . $attribute)] = $errors;
434
            }
435
        }
436
437
        return $result;
438
    }
439
}
440