I am using bootstrap ActiveForm
. Here is my form:
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use kartik\file\FileInput;
/* @var $this yii\web\View */
/* @var $model common\models\Customer */
/* @var $form yii\widgets\ActiveForm */
?>
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data'],
'id' => 'customer-form',
'enableClientValidation' => true,
'options' => [
'validateOnSubmit' => true,
'class' => 'form'
],
'layout' => 'horizontal',
'fieldConfig' => [
'horizontalCssClasses' => [
'label' => 'col-sm-4',
// 'offset' => 'col-sm-offset-2',
'wrapper' => 'col-sm-8',
],
],
]); ?>
<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>
<?php ActiveForm::end(); ?>
Here is my model:
class Customer extends \yii\db\ActiveRecord
{
public $username;
public $password;
public $status;
public $email;
public $uploads;
public function rules()
{
return [
[['user_id', 'created_by', 'updated_by'], 'integer'],
[['created_at','uploads', 'updated_at','legacy_customer_id','fax','phone_two','trn'], 'safe'],
[['company_name','customer_name','username','password', 'tax_id'], 'string', 'max' => 200],
[['customer_name','email','legacy_customer_id','company_name','city'], 'required'],
[['is_deleted','status'], 'boolean'],
[['address_line_1', 'state','phone', 'country'], 'string', 'max' => 450],
[['address_line_2', 'city', 'zip_code'], 'string', 'max' => 45],
[['user_id','legacy_customer_id'], 'unique'],
['email', 'email'],
[['uploads'], 'file', 'maxFiles' => 10],
[['email'], 'unique', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['email' => 'email'], 'message' => 'This email address has already been taken.'],
[['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],
];
}
}
Here is Controller
public function actionCreate() {
$model = new Customer();
if ($model->load(Yii::$app->request->post())) {
$transaction = Yii::$app->db->beginTransaction();
try
{
$user_create = \common\models\User::customeruser($model);
if ($user_create) {
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole('customer');
$auth->assign($role, $model->user_id);
}
if ($user_create && $model->save()) {
$photo = UploadedFile::getInstances($model, 'uploads');
if ($photo !== null) {
$save_images = \common\models\CustomerDocuments::save_document($model->user_id, $photo);
}
$transaction->commit();
return $this->redirect(['view', 'id' => $model->user_id]);
}
}catch (Exception $e)
{
$transaction->rollBack();
}
}
if (Yii::$app->request->isAjax) {
return $this->renderAjax('create', [
'model' => $model,
]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
Now the required attribute working used in the rules. It does not allow the form to submit until the required field filled with some value, but at the same time the unique attribute using with target class not working and allow the form to submit. After click the submit form the form will not submit but it does not show the error of unique validation. Regard I am using the form in bootstrap modal and I want the form will show the unique submit error before submission like the same as required working. I can do it using jQuery on blur function and send custom AJAX request but I want the default solution of Yii 2.
EDIT
This is where the error is throw due to user not being saved
public static function customeruser( $model ) {
$user = new User();
$user->username = $model->email;
$user->email = $model->email;
$user->setPassword ( $model->legacy_customer_id );
$user->generateAuthKey ();
if ( !$user->save () ) {
var_dump ( $user->getErrors () );
exit ();
} return $user->save () ? $user : null;
}
var_dump()
shows the following
'username' => array (size = 1) 0 => string 'This username has already been taken.' (length = 37) 'email' => array (size = 1) 0 => string 'This email address has already been taken.' (length = 42).
As you are using the try catch block along with the transaction you should throw and catch such errors as the exception so that the transaction is rolled back, and the message is displayed to the user too.
You are not consuming or using the beauty of try{}catch(){}
block with transactions. You should always throw an Exception
in case any of the models are not saved and the catch block will rollback the transaction.
For example, you are saving the user in the function customeruser()
by calling
$user_create = \common\models\User::customeruser($model);
and returning the user
object or null
otherwise and then in the very next line, you are verifying the user was created or not.
if ($user_create) {
You should simply throw an exception from the function customeruser()
in case the model was not saved and return the $user
object otherwise, you don't have to check $user_create
again to verify if user was not saved the exception will be thrown and control will be transferred to the catch
block and the lines after $user_create = \common\models\User::customeruser($model);
will never be called.
I mostly use the following way when i have multiple models to save and i am using the transaction block.
$transaction = Yii::$app->db->beginTransaction ();
try {
if ( !$modelUser->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelUser->errors , 0 , false ) ) );
}
if ( !$modelProfile->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelProfile->errors , 0 , false ) ) );
}
$transaction->commit();
} catch ( \Exception $ex ) {
$transaction->rollBack();
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
}
So you can do the same for your code
public static function customeruser( $model ) {
$user = new User();
$user->username = $model->email;
$user->email = $model->email;
$user->setPassword ( $model->legacy_customer_id );
$user->generateAuthKey ();
if(!$user->save()){
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $user->errors , 0 , false ) ) );
}
return $user;
}
change your actionCreate
to the following
public function actionCreate() {
$model = new Customer();
if ( $model->load ( Yii::$app->request->post () ) ) {
$transaction = Yii::$app->db->beginTransaction ();
try {
$user_create = \common\models\User::customeruser ( $model );
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole ( 'customer' );
$auth->assign ( $role , $model->user_id );
if ( !$model->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) );
}
$photo = UploadedFile::getInstances ( $model , 'uploads' );
if ( $photo !== null ) {
$save_images = \common\models\CustomerDocuments::save_document ( $model->user_id , $photo );
}
$transaction->commit ();
return $this->redirect ( [ 'view' , 'id' => $model->user_id ] );
} catch ( \Exception $ex ) {
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
$transaction->rollBack ();
}
}
if ( Yii::$app->request->isAjax ) {
return $this->renderAjax ( 'create' , [
'model' => $model ,
] );
}
return $this->render ( 'create' , [
'model' => $model ,
] );
}