Yii2 step-by-step guide on login from table in MySQL

40,590

Solution 1

Yii2 advanced app comes by default with a working example of the login part from the DB (I see the basic ones uses a static username and password). You do not have to install anything extra, just look at the code. Install the advanced app and take a look at the frontend.

In short SiteController uses LoginModel for validation then uses the login() of the LoginModel to login the User model to the User component.

If you do not want to use the User model, just create your own model and use that one. You do not want to use the default User component, just create your own. It is quite easy to do.

Edit: mate, remove the public declarations of variables bellow.

class User extends ActiveRecord implements \yii\web\IdentityInterface
{
public $id;
public $username;
public $password;
public $authKey;
public $accessToken;

You are telling Yii to ignore what is in the database.

Solution 2

Create your own model and then use that. I will post the code below.

1) First create a database table with your own requirements.

 CREATE TABLE `q_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) NOT NULL,
  `auth_key` varchar(32) NOT NULL,
  `password_hash` varchar(20) NOT NULL,
  `access_token` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 
  • Now go to the Model generator and create a Model using the q_user table

  • It will generate a QUser model. In this model you will have to
    implement the IdentityInterface

    class QUser extends \yii\db\ActiveRecord implements \yii\web\IdentityInterface

Now implement all the methods. If using Netbeans hit alt+ enter.

public function getAuthKey() {
        return $this->auth_key;
    }

    public function getId() {
        return $this->id;
    }

    public function validateAuthKey($authKey) {
       return $this->auth_key = $authkey;
    }

    public static function findIdentity($id) {

        return self::findOne($id);

    }

    public static function findIdentityByAccessToken($token, $type = null) {

        return $this->access_token;
    }

    public static function findByUsername($email){
        return self::findOne(['email'=>$email]);
    }

    public function validatePassword($password){
        return $this->password_hash === $password;
    }
  • Now in Login form you will have to define Quser model so it will return back the user

    class LoginForm extends Model { public $username; public $password; public $email; public $rememberMe = true;

    private $_user = false;
    
    
    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            // username and password are both required
            [['email', 'password'], 'required'],
            // rememberMe must be a boolean value
    
            ['rememberMe', 'boolean'],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
            ['password','match','pattern'=>'$\S*(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[\d])\S*$','message'=>'Password must have atleast 1 uppercase and 1 number '],
            [['password'],'string','min'=>6],
            //email validation
            ['email','email']
        ];
    }
    
    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
    
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }
    
    /**
     * Logs in a user using the provided username and password.
     * @return boolean whether the user is logged in successfully
     */
    public function login()
    {
        if ($this->validate()) {
            return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
        }
        return false;
    }
    
    /**
     * Finds user by [[username]]
     *
     * @return User|null
     */
    public function getUser()
    {
        if ($this->_user === false) {
            $this->_user = QUser::findByUsername($this->email);
        }
    
        return $this->_user;
    }
    

    }

That's it this will solve your problem.

Share:
40,590
André Castro
Author by

André Castro

My name is André Castro. I’m a Web Developer and CEO of acastro development. I enjoy turning complex problems into simple, beautiful and intuitive interface designs, front/back end applications.

Updated on June 03, 2020

Comments

  • André Castro
    André Castro almost 4 years

    I'm begining to make the first steps in Yii2. So far, I was able to write an application and connect a table in a database to it, just like I learned to do in Yii1.

    The table is contacts and the form in my create view sends the data to the database with no problems.

    The problem is that I only can login using Admin/Admin or demo/demo in the static user model that comes built-in in Yii2.

    In Yii1.xx i manage to validate login from a table in database using COMPONENTS/useridentity, just like this (I used a table named utilizador, with fields id, utilizador and password):

    class UserIdentity extends CUserIdentity
    {
    
        public function authenticate() {
           $conexao=Yii::app()->db;
           $consulta="select utilizador,password from utilizador ";
           $consulta.="where utilizador='".$this->username."' and ";
           $consulta.="password='".$this->password."'";
    
           $resultado=$conexao->createCommand($consulta)->query();
    
           $resultado->bindColumn(1,$this->username);
           $resultado->bindColumn(2,$this->password);
    
           while($resultado->read()!==false){
            $this->errorCode=self::ERROR_NONE;
            return !$this->errorCode;
           }
       }
    
    }
    

    With Yii2 I have read a lot of tutorials, including one in Stack Overflow but none of them enlighten me on the procedure to login users with username and password from a MySQL database. Yii2 don't have components/useridentity.php and I don't know where to start and what is the proper code to make it work overriding the static user model that comes out of the box.

    I also have tried an extension Yii2-user, read the PDF guide but didn't understand how to call the route's from the vendor folder in my controllers. Made several tries but all failed.

    Can someone teach me how to validate login from database in Yii2, preferentially without using an extension?

    Edit

    I read this tutorial in Stack Overflow Yii Framework 2.0 Login With User Database

    And also studied the PDF from Yii2-user extension, but don't know what to do with following part and next ones of the pdf. It speaks about routes, but i don't know how to work with them:

    2.3.1 Show users Route /user/admin/index shows a list of registered users. You will be able to see a lot of useful information such as registration time and ip address, confirmation and block status, etc.

    I have also read this: http://www.yiiframework.com/doc-2.0/yii-web-user.html but I don't think it has the steps to resolve my problem.

    EDIT 2

    I tried to implement the User Model and LoginForm Model in Yii basic template to validate user logins. Created a database and coneected to it. The database as a table user and fields username, password, authKey, acessToken populated with values. Extended the User Model from ActiveRecord and implemented \yii\web\IdentityInterface in order to make the in-built Yii2 functions do their job. Also wrrited the method public static function tableName() { return 'user'; }

    Every time i try to login it throws -> username or password incorrect, from the validatepassword() in LoginForm Model.

    Here is my code: LoginForm Model:

    namespace app\models;
    
    use Yii;
    use yii\base\Model;
    
    /**
     * LoginForm is the model behind the login form.
     */
    class LoginForm extends Model
    {
    public $username;
    public $password;
    public $rememberMe = true;
    
    private $_user = false;
    
    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            // username and password are both required
            [['username', 'password'], 'required'],
            // rememberMe must be a boolean value
            ['rememberMe', 'boolean'],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
        ];
    }
    
    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
    
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }
    
    /**
     * Logs in a user using the provided username and password.
     * @return boolean whether the user is logged in successfully
     */
    public function login()
    {
        if ($this->validate()) {
            return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
        } else {
            return false;
        }
    }
    
    /**
     * Finds user by [[username]]
     *
     * @return User|null
     */
    public function getUser()
    {
        if ($this->_user === false) {
            $this->_user = User::findByUsername($this->username);
        }
    
        return $this->_user;
    }
    

    }

    ... and here is my User.php Model

    <?php
    
    namespace app\models;
    
    use yii\db\ActiveRecord;
    
    class User extends ActiveRecord implements \yii\web\IdentityInterface
    {
    public $id;
    public $username;
    public $password;
    public $authKey;
    public $accessToken;
    
    public static function tableName() { return 'user'; }
    
       /**
     * @inheritdoc
     */
    public static function findIdentity($id) {
        $user = self::find()
                ->where([
                    "id" => $id
                ])
                ->one();
        if (!count($user)) {
            return null;
        }
        return new static($user);
    }
    
    /**
     * @inheritdoc
     */
    public static function findIdentityByAccessToken($token, $userType = null) {
    
        $user = self::find()
                ->where(["accessToken" => $token])
                ->one();
        if (!count($user)) {
            return null;
        }
        return new static($user);
    }
    
    /**
     * Finds user by username
     *
     * @param  string      $username
     * @return static|null
     */
    public static function findByUsername($username) {
        $user = self::find()
                ->where([
                    "username" => $username
                ])
                ->one();
        if (!count($user)) {
            return null;
        }
        return new static($user);
    }
    
    /**
     * @inheritdoc
     */
    public function getId() {
        return $this->id;
    }
    
    /**
     * @inheritdoc
     */
    public function getAuthKey() {
        return $this->authKey;
    }
    
    /**
     * @inheritdoc
     */
    public function validateAuthKey($authKey) {
        return $this->authKey === $authKey;
    }
    
    /**
     * Validates password
     *
     * @param  string  $password password to validate
     * @return boolean if password provided is valid for current user
     */
    public function validatePassword($password) {
        return $this->password === $password;
    }
    
    }
    

    Any ideas ?? -> Thanks...

    I don't know what else should i do, perhaps it has a problem in validating the password or find the username, in Yii2 debug it shows that is proper connected to the mysql database.

    Don't touched in the siteController actionLogin() because it is equal to the advanced template and i think it is better to stay that way.

    EDIT 3 -> 4 HOURS messing with the models code, putting in pratice every solution i read and it still throws "Incorrect username or password." from:

    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
    
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }
    

    I don't want to give up, but i'm considering go back to the old Yii1.xx. There i could easily query the database and make a good login system working.

    • halfer
      halfer over 9 years
      There is a couple of things to do here. Firstly, can you show us what you have so far, perhaps along the lines of the official docs or the Stack Overflow posts you have read, and then someone can help you fix/understand it? As it stands you are requesting several pages of tutorial, which probably makes the query too broad for the site. Also, when you mention official docs or plugins, please hyperlink them in your post, so people can follow links and make suggestions. We essentially need to see that the official docs are insufficient, but in Yii's case I'd have thought they would be OK?
    • André Castro
      André Castro over 9 years
      I understand your concerns and have edited the question acording to it. For now i don't have code to show because the tries didn't work and my project only outputted errors, and became unusable.
    • halfer
      halfer over 9 years
      What sort of answer are you after? If it is "a full working example" then I think that will come under the "too broad" category (i.e. answers would have to be substantial, and may not work in your use case). Does the official docs not have a tutorial that contains a working example?
    • halfer
      halfer over 9 years
    • André Castro
      André Castro over 9 years
      No. I cannot make the code in the documents to work. Perhaps, because i'm new to Yii2.
    • André Castro
      André Castro over 9 years
      Thanks for the links, i will give them a try.
  • André Castro
    André Castro over 9 years
    Yes. I think that is the path i need to follow. I will install the advanced template and grab things as needed. Many Thanks.
  • Mihai P.
    Mihai P. over 9 years
    I looked at the basic app, I understand the problem now. Look at the advanced one.
  • André Castro
    André Castro over 9 years
    I'm looking at Yii2 advanced and i think i can replicate the system with less table fields and implement something similar in the basic template. Tomorrow i will work on that. Many thanks.
  • Mihai P.
    Mihai P. over 9 years
    from what I saw you just have to change a couple of files and that is all.
  • André Castro
    André Castro over 9 years
    I have edited User Model and LoginForm Model as above explained in EDIT 2, and according to what i studied and saw in the advanced template. Didn't work :(
  • Mihai P.
    Mihai P. over 9 years
    see my answer :) I think that is it. I messed up like this before myself
  • André Castro
    André Castro over 9 years
    SOLVED. Many thanks to all that somehow tried to put me in the right path. A special thanks to Mihai P. for giving the correct answer. This one was a pain in the ass, and yet the solution was very simple.
  • Cymbals
    Cymbals almost 9 years
    Did removing the declarations fix it, or was there more, please?
  • André Castro
    André Castro almost 8 years
    Many thank's for the solution. I wiil make a prototype to learn with it.
  • Raja Ehtesham
    Raja Ehtesham almost 8 years
    @mohan Prasad : I think you mistyped the body of findIdentityByAccessToken() function, it should be public static function findIdentityByAccessToken($token, $type = null) { return self::findOne(['access_token'=>$token]); }