input-forms.md 6.3 KB
Newer Older
Qiang Xue committed
1
Working with Forms
2 3
==================

4
> Note: This section is under development.
Qiang Xue committed
5

6
The primary way of using forms in Yii is through [[yii\widgets\ActiveForm]]. This approach should be preferred when
7
the form is based upon  a model. Additionally, there are some useful methods in [[yii\helpers\Html]] that are typically
8
used for adding buttons and help text to any form.
9

10 11
When creating model-based forms, the first step is to define the model itself. The model can be either based upon the
Active Record class, or the more generic Model class. For this login example, a generic model will be used:
12 13 14 15 16 17

```php
use yii\base\Model;

class LoginForm extends Model
{
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    public $username;
    public $password;

    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            // username and password are both required
            [['username', 'password'], 'required'],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
        ];
    }

    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     */
    public function validatePassword()
    {
        $user = User::findByUsername($this->username);
        if (!$user || !$user->validatePassword($this->password)) {
            $this->addError('password', 'Incorrect username or password.');
        }
    }

    /**
     * Logs in a user using the provided username and password.
     * @return boolean whether the user is logged in successfully
     */
    public function login()
    {
        if ($this->validate()) {
            $user = User::findByUsername($this->username);
            return true;
        } else {
            return false;
        }
    }
59 60 61
}
```

62
The controller will pass an instance of that model to the view, wherein the [[yii\widgets\ActiveForm|ActiveForm]] widget is used:
63 64 65 66 67 68

```php
use yii\helpers\Html;
use yii\widgets\ActiveForm;

<?php $form = ActiveForm::begin([
69 70
    'id' => 'login-form',
    'options' => ['class' => 'form-horizontal'],
71
]) ?>
72 73 74 75 76 77 78 79
    <?= $form->field($model, 'username') ?>
    <?= $form->field($model, 'password')->passwordInput() ?>

    <div class="form-group">
        <div class="col-lg-offset-1 col-lg-11">
            <?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>
        </div>
    </div>
80 81 82
<?php ActiveForm::end() ?>
```

83 84 85
In the above code, [[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] not only creates a form instance, but also marks the beginning of the form.
All of the content placed between [[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] and
[[yii\widgets\ActiveForm::end()|ActiveForm::end()]] will be wrapped within the `<form>` tag.
86 87
As with any widget, you can specify some options as to how the widget should be configured by passing an array to
the `begin` method. In this case, an extra CSS class and identifying ID are passed to be used in the opening `<form>` tag.
88

89
In order to create a form element in the form, along with the element's label, and any application JavaScript validation,
90 91 92
the [[yii\widgets\ActiveForm::field()|ActiveForm::field()]] method of the Active Form widget is called.
When the invocation of this method is echoed directly, the result is a regular (text) input.
To customize the output, you can chain additional methods to this call:
93 94 95 96 97 98 99 100

```php
<?= $form->field($model, 'password')->passwordInput() ?>

// or

<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>
```
Carsten Brandt committed
101 102

This will create all the `<label>`, `<input>` and other tags according to the template defined by the form field.
103
To add these tags yourself you can use the `Html` helper class.
104

105 106 107 108 109 110
If you want to use one of HTML5 fields you may specify input type directly like the following:

```php
<?= $form->field($model, 'email')->input('email') ?>
```

Carsten Brandt committed
111
Specifying the attribute of the model can be done in more sophisticated ways. For example when an attribute may
112 113 114 115 116
take an array value when uploading multiple files or selecting multiple items you may specify it by appending `[]`
to the attribute name:

```php
// allow multiple files to be uploaded:
ff committed
117
echo $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);
118 119 120 121 122

// allow multiple items to be checked:
echo $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);
```

123 124 125 126 127 128 129 130
> **Tip**: in order to style required fields with asterisk you can use the following CSS:
>
```css
div.required label:after {
    content: " *";
    color: red;
}
```
131

Qiang Xue committed
132 133
Handling multiple models with a single form
-------------------------------------------
134

Qiang Xue committed
135
Sometimes you need to handle multiple models of the same kind in a single form. For example, multiple settings where
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
each setting is stored as name-value and is represented by `Setting` model. The
following shows how to implement it with Yii.

Let's start with controller action:

```php
namespace app\controllers;

use Yii;
use yii\base\Model;
use yii\web\Controller;
use app\models\Setting;

class SettingsController extends Controller
{
151
    // ...
152

153 154 155
    public function actionUpdate()
    {
        $settings = Setting::find()->indexBy('id')->all();
156

157 158 159 160
        if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {
            foreach ($settings as $setting) {
                $setting->save(false);
            }
161

162 163
            return $this->redirect('index');
        }
164

165 166
        return $this->render('update', ['settings' => $settings]);
    }
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
}
```

In the code above we're using `indexBy` when retrieving models from database to make array indexed by model ids. These
will be later used to identify form fields. `loadMultiple` fills multiple modelds with the form data coming from POST
and `validateMultiple` validates all models at once. In order to skip validation when saving we're passing `false` as
a parameter to `save`.

Now the form that's in `update` view:

```php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;

$form = ActiveForm::begin();

foreach ($settings as $index => $setting) {
185
    echo Html::encode($setting->name) . ': ' . $form->field($setting, "[$index]value");
186 187 188 189 190 191
}

ActiveForm::end();
```

Here for each setting we are rendering name and an input with a value. It is important to add a proper index
Qiang Xue committed
192
to input name since that is how `loadMultiple` determines which model to fill with which values.