Custom model attribute in AfterSave()

Hello,

I am trying to manage an automatic rBAC configuration for the new user registrations.

I have a custom attribute in MyUsers model called auth_level.


public $auth_level;

In the form I pass an authentication item name according to the auth_item table and then try to link a new Assignment with relational data.




<?= $form->field($model, 'auth_level')->dropdownList(\yii\helpers\ArrayHelper::map(\app\models\AuthItem::find()->all(), 'name', 'description')) ?>



It is impossible to link relational data in BeforeSave() as there is no ID for the model before saving it to AI database row.

However in AfterSave(), the custom attribute $model->auth_level is already lost. Is there a solution for that without saving this info into the database to users? I wouldnā€™t want to have them in two places when the auth_assignments table is specifically exists for that.




public function afterSave($insert, $changedAttributes)

    {

        parent::afterSave($insert, $changedAttributes);

        if ($insert) {


            /**

             * Set user authentication level

             */

            $assignment = new AuthAssignment();

            $assignment->item_name = $this->auth_level;

            $assignment->link('user', $this);


            /**

             * Send notification for the new user

             */

            Yii::$app->Mail->NewUserRegistration($this);


        }

    }



I think you donā€™t need to use ā€˜beforeSaveā€™ nor ā€˜afterSaveā€™.




if ($model->save()) {

    $assignment = new AuthAssignment();

    $assignment->item_name = $model->auth_level;

    $assignment->link('user', $model);


    Yii::$app->Mail->NewUserRegistration($model);

}



1 Like

Thankā€™s for your response!

But where would you put it? Because if the auth_level variable is destroyed somewhere between beforeSave() and afterSave() then I cannot use it in the controller after POST either or can I?

I tried in controller and it is not working.

In the controller, or in a static method of the model.

Is it destroyed? Hmm, itā€™s against my intuition. I think it should not be destroyed.

Well, but if it really is destroyed, you could store it in a temporary variable before you call save().

I agree with you, it is not supposed to be destroyed. Maybe it is really notā€¦

I tried to link the relational data in the controller right after Save(), using a temp variable.





if ($model->load(Yii::$app->request->post())) {

            $temp = $model->auth_level;

             

            if($model->save()){

                /**

         		* Set user authentication level

         		*/

                $assignment = new AuthAssignment();

                $assignment->item_name = $temp;

                $assignment->link('user', $model);


            }

            return $this->redirect(['view', 'id' => $model->ID]);



The auth_level still appears to be NULL (I receive the error that item_name cannot be null).

In debug, the _POST contains the correct auth_level value, but printing the object after load(); gives the following result:




app\models\MyUsers Object ( [password_repeat] => asd [changes] => [auth_level] => [_attributes:yii\db\BaseActiveRecord:private] => Array ( [username] => teszt [employee_ID] => xyz123 [name] => Kekeszes Tej [email] => Orion [password] => asd ) 



I am not entirely sure what [size=ā€œ2ā€]_attributes:yii\db\BaseActiveRecord:private means. but seems like after populating the $model with the _POST data, auth_level cannot be accessed directly anymore, so even before ā€˜Save();ā€™ I cannot use the variable.[/size]

[size="2"]Got any idea?[/size]

What type of variable is $auth_level in the first place? I just thought it was a string that is the name of a role or some other auth item, but Iā€™m not sure now.

I would rather want to do it like the following, without worrying about the implementation details of RBAC.




if ($model->load(Yii::$app->request->post())) {

    if($model->save()){

        /* get Role from its name : assuming 'auth_level' is the name of a role */

        $role = Yii::$app->authManager->getItem($model->auth_level);

        /* assign the role to the user */

        Yii::$app->authManager->assign($role, $model->ID);

    }

    return $this->redirect(['view', 'id' => $model->ID]);

}



Exactly, it is a simple string with the name of the role.




if ($model->load(Yii::$app->request->post())) {

    if($model->save()){

        /* get Role from its name : assuming 'auth_level' is the name of a role */

        $role = Yii::$app->authManager->getItem($model->auth_level);

        /* assign the role to the user */

        Yii::$app->authManager->assign($role, $model->ID);

    }

    return $this->redirect(['view', 'id' => $model->ID]);

}



Missing function getItem, in the documentation it is getRole, however, I tried it and the assign() returns an error for empty value.

Checked the values for the following code:





        if ($model->load(Yii::$app->request->post())) {

            if($model->save()){

                /* get Role from its name : assuming 'auth_level' is the name of a role */

                $role = Yii::$app->authManager->getRole($model->auth_level);

                var_dump($role);

                Yii::$app->end();

                /* assign the role to the user */

                Yii::$app->authManager->assign($role, $model->ID);

            }



$role is a big fat ā€˜NULLā€™.

$_POST has the ā€˜auth_levelā€™ set, still after populating the model it becomes BaseActiverRecord:private





$_POST = [

    '_csrf' => 'WTZWeWptck5teWdNKz8gKClwZjE8GkU5LVESFVMfO2NubC4DDiEQFg==',

    'MyUsers' => [

        'username' => 'sass',

        'employee_ID' => 'xyz123',

        'name' => 'Kekeszes Tej',

        'email' => 'dev@test.com',

        'auth_level' => 'Level4',

        'password' => 'asd',

        'password_repeat' => 'asd',

    ],

];







if ($model->load(Yii::$app->request->post())) {

            print_r($model);

            echo '<br><br>';

            var_dump($model->auth_level);

            Yii::$app->end();



print_r($model);


app\models\MyUsers Object ( [password_repeat] => asd [changes] => [auth_level] => [_attributes:yii\db\BaseActiveRecord:private] => Array ( [username] => sass [employee_ID] => xyz123 [name] => Kekeszes Tej [email] => dev@hu.dsv.com [password] => asd ) 

var_dump($model->auth_level);


NULL

By the way, thanks for warning me to use the AuthManager, I donā€™t know why I wanted to link relations on the first place. :confused:

Here is the full print_r with validations etc.





app\models\MyUsers Object

(

    [password_repeat] =&gt; asd

    [changes] =&gt; 

    [auth_level] =&gt; 

    [_attributes:yii\db\BaseActiveRecord:private] =&gt; Array

        (

            [username] =&gt; sass

            [employee_ID] =&gt; xyz123

            [name] =&gt; Kekeszes Tej

            [email] =&gt; dev@test.com

            [password] =&gt; asd

        )


    [_oldAttributes:yii\db\BaseActiveRecord:private] =&gt; 

    [_related:yii\db\BaseActiveRecord:private] =&gt; Array

        (

        )


    [_errors:yii\base\Model:private] =&gt; 

    [_validators:yii\base\Model:private] =&gt; ArrayObject Object

        (

            [storage:ArrayObject:private] =&gt; Array

                (

                    [0] =&gt; yii\validators\RequiredValidator Object

                        (

                            [skipOnEmpty] =&gt; 

                            [requiredValue] =&gt; 

                            [strict] =&gt; 

                            [message] =&gt; {attribute} cannot be blank.

                            [attributes] =&gt; Array

                                (

                                    [0] =&gt; username

                                    [1] =&gt; password

                                    [2] =&gt; password_repeat

                                    [3] =&gt; employee_ID

                                    [4] =&gt; email

                                    [5] =&gt; name

                                )


                            [on] =&gt; Array

                                (

                                )


                            [except] =&gt; Array

                                (

                                    [0] =&gt; change

                                )


                            [skipOnError] =&gt; 1

                            [enableClientValidation] =&gt; 1

                            [isEmpty] =&gt; 

                            [when] =&gt; 

                            [whenClient] =&gt; 

                            [_events:yii\base\Component:private] =&gt; Array

                                (

                                )


                            [_behaviors:yii\base\Component:private] =&gt; 

                        )


                    [1] =&gt; yii\validators\RequiredValidator Object

                        (

                            [skipOnEmpty] =&gt; 

                            [requiredValue] =&gt; 

                            [strict] =&gt; 

                            [message] =&gt; {attribute} cannot be blank.

                            [attributes] =&gt; Array

                                (

                                    [0] =&gt; password

                                    [1] =&gt; password_repeat

                                )


                            [on] =&gt; Array

                                (

                                    [0] =&gt; change

                                )


                            [except] =&gt; Array

                                (

                                )


                            [skipOnError] =&gt; 1

                            [enableClientValidation] =&gt; 1

                            [isEmpty] =&gt; 

                            [when] =&gt; 

                            [whenClient] =&gt; 

                            [_events:yii\base\Component:private] =&gt; Array

                                (

                                )


                            [_behaviors:yii\base\Component:private] =&gt; 

                        )


                    [2] =&gt; yii\validators\StringValidator Object

                        (

                            [length] =&gt; 

                            [max] =&gt; 255

                            [min] =&gt; 

                            [message] =&gt; {attribute} must be a string.

                            [tooShort] =&gt; 

                            [tooLong] =&gt; {attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.

                            [notEqual] =&gt; 

                            [encoding] =&gt; utf-8

                            [attributes] =&gt; Array

                                (

                                    [0] =&gt; username

                                    [1] =&gt; password

                                    [2] =&gt; password_repeat

                                )


                            [on] =&gt; Array

                                (

                                )


                            [except] =&gt; Array

                                (

                                )


                            [skipOnError] =&gt; 1

                            [skipOnEmpty] =&gt; 1

                            [enableClientValidation] =&gt; 1

                            [isEmpty] =&gt; 

                            [when] =&gt; 

                            [whenClient] =&gt; 

                            [_events:yii\base\Component:private] =&gt; Array

                                (

                                )


                            [_behaviors:yii\base\Component:private] =&gt; 

                        )


                    [3] =&gt; yii\validators\StringValidator Object

                        (

                            [length] =&gt; 

                            [max] =&gt; 25

                            [min] =&gt; 

                            [message] =&gt; {attribute} must be a string.

                            [tooShort] =&gt; 

                            [tooLong] =&gt; {attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.

                            [notEqual] =&gt; 

                            [encoding] =&gt; utf-8

                            [attributes] =&gt; Array

                                (

                                    [0] =&gt; employee_ID

                                    [1] =&gt; auth_level

                                )


                            [on] =&gt; Array

                                (

                                )


                            [except] =&gt; Array

                                (

                                )


                            [skipOnError] =&gt; 1

                            [skipOnEmpty] =&gt; 1

                            [enableClientValidation] =&gt; 1

                            [isEmpty] =&gt; 

                            [when] =&gt; 

                            [whenClient] =&gt; 

                            [_events:yii\base\Component:private] =&gt; Array

                                (

                                )


                            [_behaviors:yii\base\Component:private] =&gt; 

                        )


                    [4] =&gt; yii\validators\NumberValidator Object

                        (

                            [integerOnly] =&gt; 1

                            [max] =&gt; 

                            [min] =&gt; 

                            [tooBig] =&gt; 

                            [tooSmall] =&gt; 

                            [integerPattern] =&gt; /^\s*[+-]?\d+\s*$/

                            [numberPattern] =&gt; /^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$/

                            [attributes] =&gt; Array

                                (

                                    [0] =&gt; manager

                                )


                            [message] =&gt; {attribute} must be an integer.

                            [on] =&gt; Array

                                (

                                )


                            [except] =&gt; Array

                                (

                                )


                            [skipOnError] =&gt; 1

                            [skipOnEmpty] =&gt; 1

                            [enableClientValidation] =&gt; 1

                            [isEmpty] =&gt; 

                            [when] =&gt; 

                            [whenClient] =&gt; 

                            [_events:yii\base\Component:private] =&gt; Array

                                (

                                )


                            [_behaviors:yii\base\Component:private] =&gt; 

                        )


                    [5] =&gt; yii\validators\StringValidator Object

                        (

                            [length] =&gt; 

                            [max] =&gt; 50

                            [min] =&gt; 

                            [message] =&gt; {attribute} must be a string.

                            [tooShort] =&gt; 

                            [tooLong] =&gt; {attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.

                            [notEqual] =&gt; 

                            [encoding] =&gt; utf-8

                            [attributes] =&gt; Array

                                (

                                    [0] =&gt; email

                                    [1] =&gt; name

                                    [2] =&gt; password

                                )


                            [on] =&gt; Array

                                (

                                )


                            [except] =&gt; Array

                                (

                                )


                            [skipOnError] =&gt; 1

                            [skipOnEmpty] =&gt; 1

                            [enableClientValidation] =&gt; 1

                            [isEmpty] =&gt; 

                            [when] =&gt; 

                            [whenClient] =&gt; 

                            [_events:yii\base\Component:private] =&gt; Array

                                (

                                )


                            [_behaviors:yii\base\Component:private] =&gt; 

                        )


                    [6] =&gt; yii\validators\CompareValidator Object

                        (

                            [compareAttribute] =&gt; password

                            [compareValue] =&gt; 

                            [type] =&gt; string

                            [operator] =&gt; ==

                            [message] =&gt; Passwords must match!

                            [attributes] =&gt; Array

                                (

           	ā€¦



Probably the validation rule for ā€˜auth_levelā€™ is missing. B)

1 Like

OMGā€¦ I feel so stupid now. :D

I added ā€˜auth_levelā€™ to the rules but left it out from the SCENARIO lists. :'D

Thank you for your help, now it works.

Glad to hear that. Have a nice weekend. :D