Customising an action

Hello. I have just started using Yii and it is quite a learning curve. If somebody could answer this question, it would really help me understand the framework.

In my previous website, I had a database table containing a list of contacts. Each contact was assigned a unique 7 digit alphanumeric value which we used in emails as an identifier. Therefore, when creating a new entry in the table, I would call a function which generated the string. I would then query the database and confirm that the string was unique. If it was, I would add it to the insert query. If it was not unique then I would call the function again.

Questions regarding best practice for Yii:

  1. Where should I place the random string generator function? In the Controller?

  2. When using Gii to create the CRUD, I somehow need to modify the form not to show the alphanumeric as an input field but, instead, it should call upon the function to create the alphanumeric and confirm its uniqueness. How do I do that?

Many thanks

You can add the beforeValidate() function in you model




function beforeValidate() {

    if($this->isNewRecord){

        $this->generateRandomIdentifier();

    }


    return parent::beforeValidate();

}


private function generateRandomIdentifier(){

    // Do the logic of generating new identifier, and assign to the related field.

    $this->identifier = $identifier;

}



The above code will run before performing model validation, and check if the record is a newly created record.

If so, call your custom method to generate random identifier.

It is a simple way to achieve your target. A more advanced way is to use behavior and event, which you can learn more in Behaviors

And there are many built-in events for ActiveRecord that you can choose when to perform your action

ActiveRecord

Remember, always do your logic inside models, or you can create your own component to group them. But DON’T put it in controller or it will become a disaster when you scale up the project.

Gii ship with a simple CRUD generator, which scan all the attributes from your model, and simply put all of them in the view. So you have to manually remove the field you don’t want to show.

If you feel annoyed about modifying the files again and again, you can create your own generator, simply by copying the default one and modify the generator template

More detail:

Gii

You are a KING! Thank you!

That was very helpful.

I would go a different approach

  1. Make sure your database column has a unique index

In the rules you can setup a default validator.

*In this instance you wouldn’t need the unique validator since it is a key but it wouldn’t hurt. If you do put it after the default since it goes in order.


[['unique_id'], 'default', 'value' => function($model) {

  return $model->generateUniqueID();

}]

  1. Checking uniqueness can be tricky and only way i know is looping. It is possible to get in an infinite loop if you run out of keys, so you might want to put a max tries then log and error out, up to you.

public function generateUniqueID()

{

  $unique = false;

  while(!$unique) {

    $unique_id = $this->generateRandomAlphaNum();

    if(ActiveRecord::find()->where(['unique_id' => $unique_id])->exists() == false) {

      $unique = true;

    }

  }

  return $unique_id;

}

  1. Now this will work on a new record if you run $active_record->validate().

If you want to pre-load it beforehand you can call $active_record->validate(‘unique_id’) just be careful with the logic.

  1. You can create a function in the database to do this. Some people like this approach then it doesn’t matter on the application.

Thank you - I have created a solution which combines both approaches. Fortunately, because i am using a 7 digit random alphanumeric in a very small database (a few thousand records at most) an infinite loop is exceedingly unlikely / impossible.

Thanks for the help. Your example is working great!