I have a User model that stores the users in the database. With GII create a CRUD to be able to register and update users. The problem is with the password. How could I do for when I create a user to load a password and when I update it if I want to be able to update it?
My table:
CREATE TABLE
user
(id
int(11) NOT NULL,username
varchar(250) NOT NULL,auth_key
varchar(32) NOT NULL,password_hash
varchar(255) NOT NULL,password_reset_token
varchar(255) NOT NULL,status
smallint(6) NOT NULL,role_id
int(11) NOT NULL,created_at
datetime NOT NULL,updated_at
datetime NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
My form:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;
use app\models\Role;
/* @var $this yii\web\View */
/* @var $model app\models\User */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="user-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'username')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'role_id')->dropDownList(
ArrayHelper::map(Role::find()->all(),'id','description'),
['prompt'=> Yii::t('app', 'Select...')]
)?>
<?= $form->field($model, 'newPassword')->passwordInput(['placeholder' => Yii::t('app', 'Password'), 'value' => ''])->label('') ?>
<?= $form->field($model, 'status')->checkBox(['label' => Yii::t('app', 'Status'), 'selected' => $model->status])?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
My controller:
<?php
namespace app\controllers;
use Yii;
use app\models\User;
use app\models\Permission;
use app\models\search\UserSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* UserController implements the CRUD actions for User model.
*/
class UserController extends Controller
{
/**
* @inheritdoc
*/
public function behaviors()
{
return [
'access' => [
'class' => \yii\filters\AccessControl::className(),
'rules' => [
[
'actions' => ['index','view','update','create','delete'],
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return Permission::hasAllowed($action->controller->id,$action->id);
}
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all User models.
* @return mixed
*/
public function actionIndex()
{
$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single User model.
* @param integer $id
* @return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new User model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionCreate()
{
$model = new User();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', Yii::t('app', 'Successful create!'));
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
/**
* Updates an existing User model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param integer $id
* @return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', Yii::t('app', 'Successful update!'));
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing User model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param integer $id
* @return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the User model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param integer $id
* @return User the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = User::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
My model:
<?php
namespace app\models;
use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\db\Expression;
use yii\web\IdentityInterface;
/**
* User model
*
* @property integer $id
* @property string $username
* @property string $password_hash
* @property string $password_reset_token
* @property string $email
* @property string $auth_key
* @property integer $status
* @property integer $created_at
* @property integer $updated_at
* @property string $password write-only password
*/
class User extends ActiveRecord implements IdentityInterface
{
const STATUS_DELETED = 0;
const STATUS_ACTIVE = 1;
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%user}}';
}
/**
* @inheritdoc
*/
/*
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
*/
public function behaviors()
{
return [
'timestamp' => [
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at']
],
'value' => new Expression('NOW()'),
],
];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['status', 'role_id', 'username','email'], 'required'],
['status', 'default', 'value' => self::STATUS_ACTIVE],
['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
[['role_id'], 'exist', 'skipOnError' => true, 'targetClass' => Role::className(), 'targetAttribute' => ['role_id' => 'id']],
];
}
/**
* @inheritdoc
*/
public static function findIdentity($id)
{
return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
}
/**
* @inheritdoc
*/
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
/**
* Finds user by username
*
* @param string $username
* @return static|null
*/
public static function findByUsername($username)
{
return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]);
}
/**
* Finds user by password reset token
*
* @param string $token password reset token
* @return static|null
*/
public static function findByPasswordResetToken($token)
{
if (!static::isPasswordResetTokenValid($token)) {
return null;
}
return static::findOne([
'password_reset_token' => $token,
'status' => self::STATUS_ACTIVE,
]);
}
/**
* Finds out if password reset token is valid
*
* @param string $token password reset token
* @return bool
*/
public static function isPasswordResetTokenValid($token)
{
if (empty($token)) {
return false;
}
$timestamp = (int) substr($token, strrpos($token, '_') + 1);
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
return $timestamp + $expire >= time();
}
/**
* @inheritdoc
*/
public function getId()
{
return $this->getPrimaryKey();
}
/**
* @inheritdoc
*/
public function getAuthKey()
{
return $this->auth_key;
}
/**
* @inheritdoc
*/
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
/**
* Validates password
*
* @param string $password password to validate
* @return bool if password provided is valid for current user
*/
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
/**
* Generates password hash from password and sets it to the model
*
* @param string $password
*/
public function setPassword($password)
{
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
/**
* Generates "remember me" authentication key
*/
public function generateAuthKey()
{
$this->auth_key = Yii::$app->security->generateRandomString();
}
/**
* Generates new password reset token
*/
public function generatePasswordResetToken()
{
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
}
/**
* Removes password reset token
*/
public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
public static function isActive()
{
return Yii::$app->user->identity->status == self::STATUS_ACTIVE;
}
/**
* @return \yii\db\ActiveQuery
*/
public function getRole()
{
return $this->hasOne(Role::className(), ['id' => 'role_id']);
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'status' => Yii::t('app', 'Status Active'),
];
}
}
You could use the beforeSave Event in your User model
public function beforeSave($insert) {
if ($insert) {
$this->setPassword($this->password_hash);
} else {
if (!empty($this->password_hash)) {
$this->setPassword($this->password_hash);
} else {
$this->password_hash = (string) $this->getOldAttribute('password_hash');
}
}
return parent::beforeSave($insert);
}
and update the update action in your controller
/**
* Updates an existing User model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param integer $id
* @return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
$model->setAttribute('password_hash', null);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', Yii::t('app', 'Successful update!'));
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
and update your form view field to
<?= $form->field($model, 'password_hash')->passwordInput(['placeholder' => Yii::t('app', 'Password'), 'value' => ''])->label(false) ?>
update your user model validations rules
/**
* @inheritdoc
*/
public function rules()
{
return [
[['status', 'role_id', 'username','email'], 'required'],
['password_hash', 'required', 'on' => 'insert'],
['password_hash', 'string'],
['status', 'default', 'value' => self::STATUS_ACTIVE],
['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
[['role_id'], 'exist', 'skipOnError' => true, 'targetClass' => Role::className(), 'targetAttribute' => ['role_id' => 'id']],
];
}