-
Notifications
You must be signed in to change notification settings - Fork 438
Dynamic Forms With Yii2 relation trait (VERY EASY)
The Yii2-dynamicforms is a great extension who saves us time when developing dynamic forms. With this extension I could do things that it took me a long time in a few hours.
I believe that most people feel very easy to work with this extension. But I asked myself a question: Can this get any easier?
The answer is YES and it is called yii2-relation-trait!
With yii2-relation-trait you can load all models includind the related ones at once and all operations work under transactions.
We will use the example in the home page of yii2-dynamicforms (Address Book)
We need yii2-dynamicforms and yii2-relation-trait, that can be installed through composer:
composer require 'wbraganca/yii2-dynamicform:dev-master'
composer require 'mootensai/yii2-relation-trait:dev-master'
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use wbraganca\dynamicform\DynamicFormWidget;
?>
<div class="customer-form">
<?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?>
<div class="row">
<div class="col-sm-6">
<?= $form->field($modelCustomer, 'first_name')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelCustomer, 'last_name')->textInput(['maxlength' => true]) ?>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><h4><i class="glyphicon glyphicon-envelope"></i> Addresses</h4></div>
<div class="panel-body">
<?php DynamicFormWidget::begin([
'widgetContainer' => 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
'widgetBody' => '.container-items', // required: css class selector
'widgetItem' => '.item', // required: css class
'limit' => 4, // the maximum times, an element can be cloned (default 999)
'min' => 1, // 0 or 1 (default 1)
'insertButton' => '.add-item', // css class
'deleteButton' => '.remove-item', // css class
'model' => $modelsAddress[0],
'formId' => 'dynamic-form',
'formFields' => [
'full_name',
'address_line1',
'address_line2',
'city',
'state',
'postal_code',
],
]); ?>
<div class="container-items"><!-- widgetContainer -->
<?php foreach ($modelsAddress as $i => $modelAddress): ?>
<div class="item panel panel-default"><!-- widgetBody -->
<div class="panel-heading">
<h3 class="panel-title pull-left">Address</h3>
<div class="pull-right">
<button type="button" class="add-item btn btn-success btn-xs"><i class="glyphicon glyphicon-plus"></i></button>
<button type="button" class="remove-item btn btn-danger btn-xs"><i class="glyphicon glyphicon-minus"></i></button>
</div>
<div class="clearfix"></div>
</div>
<div class="panel-body">
<?php
// necessary for update action.
if (! $modelAddress->isNewRecord) {
echo Html::activeHiddenInput($modelAddress, "[{$i}]id");
}
?>
<?= $form->field($modelAddress, "[{$i}]full_name")->textInput(['maxlength' => true]) ?>
<div class="row">
<div class="col-sm-6">
<?= $form->field($modelAddress, "[{$i}]address_line1")->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelAddress, "[{$i}]address_line2")->textInput(['maxlength' => true]) ?>
</div>
</div><!-- .row -->
<div class="row">
<div class="col-sm-4">
<?= $form->field($modelAddress, "[{$i}]city")->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-4">
<?= $form->field($modelAddress, "[{$i}]state")->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-4">
<?= $form->field($modelAddress, "[{$i}]postal_code")->textInput(['maxlength' => true]) ?>
</div>
</div><!-- .row -->
</div>
</div>
<?php endforeach; ?>
</div>
<?php DynamicFormWidget::end(); ?>
</div>
</div>
<div class="form-group">
<?= Html::submitButton($modelAddress->isNewRecord ? 'Create' : 'Update', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
Note that we use \mootensai\relation\RelationTrait
right after the class declaration.
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "customer".
*
* @property integer $id
* @property string $first_name
* @property string $last_name
*
* @property Address[] $addresses
*/
class Customer extends \yii\db\ActiveRecord
{
use \mootensai\relation\RelationTrait;
/**
* @inheritdoc
*/
public static function tableName()
{
return 'customer';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['first_name', 'last_name'], 'required'],
[['first_name'], 'string', 'max' => 32],
[['last_name'], 'string', 'max' => 64],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'first_name' => 'First Name',
'last_name' => 'Last Name',
];
}
/**
* @return \yii\db\ActiveQuery
*/
public function getAddresses()
{
return $this->hasMany(Address::className(), ['customer_id' => 'id']);
}
}
Note that we use loadAll()
and saveAll()
instead of the native ones.
These two methods also identifies the records that have been removed or added (for update)
public function actionCreate()
{
$modelCustomer = new Customer;
$modelsAddress = [new Address];
if ($modelCustomer->loadAll(Yii::$app->request->post()) && $modelCustomer->saveAll()) {
return $this->redirect(['view', 'id' => $modelCustomer->id]);
} else {
return $this->render('create', [
'modelCustomer' => $modelCustomer,
'modelsAddress' => (empty($modelsAddress)) ? [new Address] : $modelsAddress
]);
}
}
public function actionUpdate($id)
{
$modelCustomer = $this->findModel($id);
$modelsAddress = $modelCustomer->addresses;
if ($modelCustomer->loadAll(Yii::$app->request->post()) && $modelCustomer->saveAll()) {
return $this->redirect(['view', 'id' => $modelCustomer->id]);
} else {
return $this->render('update', [
'modelCustomer' => $modelCustomer,
'modelsAddress' => (empty($modelsAddress)) ? [new Address] : $modelsAddress
]);
}
}
Hope this helps!
PS: Sorry for my bad english!