0
votes

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).

2
@Muhammad Aslam Omer Bro i need your help..I have trying many technique but could not find solution - rajwa766
can you please show your code of controller. - Amitesh Kumar
@AmiteshKumar have updated the code - rajwa766
The problem could have to do with your data, so the problem may have nothing to do with the code; we would need to see the data to actually help troubleshoot. Also, which unique validation rule do you think is broken? - dataskills

2 Answers

1
votes

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 ,
            ] );
}
-1
votes

There are two three thing :

1. You are not accepting email address by user input .

2. It will insert a blank entry if in database your field is taken as NULL

3. In second insertion it will show you the error message if db field is not NUll.

Solution: 1. Make db field Not NUll .

2. Take email as user input .

3. Try to print error

if ($model->validate()) {
    // all inputs are valid
} else {
    // validation failed: $errors is an array containing error messages
    $errors = $model->errors;
}

4. remove 'skipOnError' => true,

Try Above I am sure you will get the solution

Help Full LINK For validation