mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 14:46:19 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			193 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
Working with Forms
 | 
						|
==================
 | 
						|
 | 
						|
> Note: This section is under development.
 | 
						|
 | 
						|
The primary way of using forms in Yii is through [[yii\widgets\ActiveForm]]. This approach should be preferred when
 | 
						|
the form is based upon  a model. Additionally, there are some useful methods in [[yii\helpers\Html]] that are typically
 | 
						|
used for adding buttons and help text to any form.
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
```php
 | 
						|
use yii\base\Model;
 | 
						|
 | 
						|
class LoginForm extends Model
 | 
						|
{
 | 
						|
    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;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
The controller will pass an instance of that model to the view, wherein the [[yii\widgets\ActiveForm|ActiveForm]] widget is used:
 | 
						|
 | 
						|
```php
 | 
						|
use yii\helpers\Html;
 | 
						|
use yii\widgets\ActiveForm;
 | 
						|
 | 
						|
<?php $form = ActiveForm::begin([
 | 
						|
    'id' => 'login-form',
 | 
						|
    'options' => ['class' => 'form-horizontal'],
 | 
						|
]) ?>
 | 
						|
    <?= $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>
 | 
						|
<?php ActiveForm::end() ?>
 | 
						|
```
 | 
						|
 | 
						|
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.
 | 
						|
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.
 | 
						|
 | 
						|
In order to create a form element in the form, along with the element's label, and any application JavaScript validation,
 | 
						|
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:
 | 
						|
 | 
						|
```php
 | 
						|
<?= $form->field($model, 'password')->passwordInput() ?>
 | 
						|
 | 
						|
// or
 | 
						|
 | 
						|
<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>
 | 
						|
```
 | 
						|
 | 
						|
This will create all the `<label>`, `<input>` and other tags according to the template defined by the form field.
 | 
						|
To add these tags yourself you can use the `Html` helper class.
 | 
						|
 | 
						|
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') ?>
 | 
						|
```
 | 
						|
 | 
						|
Specifying the attribute of the model can be done in more sophisticated ways. For example when an attribute may
 | 
						|
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:
 | 
						|
echo $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);
 | 
						|
 | 
						|
// allow multiple items to be checked:
 | 
						|
echo $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);
 | 
						|
```
 | 
						|
 | 
						|
> **Tip**: in order to style required fields with asterisk you can use the following CSS:
 | 
						|
>
 | 
						|
```css
 | 
						|
div.required label:after {
 | 
						|
    content: " *";
 | 
						|
    color: red;
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
Handling multiple models with a single form
 | 
						|
-------------------------------------------
 | 
						|
 | 
						|
Sometimes you need to handle multiple models of the same kind in a single form. For example, multiple settings where
 | 
						|
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
 | 
						|
{
 | 
						|
    // ...
 | 
						|
 | 
						|
    public function actionUpdate()
 | 
						|
    {
 | 
						|
        $settings = Setting::find()->indexBy('id')->all();
 | 
						|
 | 
						|
        if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {
 | 
						|
            foreach ($settings as $setting) {
 | 
						|
                $setting->save(false);
 | 
						|
            }
 | 
						|
 | 
						|
            return $this->redirect('index');
 | 
						|
        }
 | 
						|
 | 
						|
        return $this->render('update', ['settings' => $settings]);
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
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) {
 | 
						|
    echo Html::encode($setting->name) . ': ' . $form->field($setting, "[$index]value");
 | 
						|
}
 | 
						|
 | 
						|
ActiveForm::end();
 | 
						|
```
 | 
						|
 | 
						|
Here for each setting we are rendering name and an input with a value. It is important to add a proper index
 | 
						|
to input name since that is how `loadMultiple` determines which model to fill with which values.
 |