Как войти, используя две разные модели или переключить класс идентичности в yii2?

Я хочу разрешить вход пользователя из двух разных моделей.

config.php

'user' => [
        'identityClass' => 'app\models\User', //one more class here
        'enableAutoLogin' => false,
        'authTimeout' => 3600*2,
    ],

LoginForm.php

 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'],
    ];
}

 public function validatePassword($attribute, $params)
{
    if (!$this->hasErrors()) {
        $user = $this->getUser();

        if (!$user || !$user->validatePassword($this->password)) {
            $this->addError($attribute, Yii::t('user', 'Incorrect username or password.'));
        }
    }
}

public function login()
{
    if ($this->validate()) {
        return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
    } else {
        return false;
    }
}

public function parentLogin()
{
    // How to validate parent Login?
}

public function getUser()
{
    if ($this->_user === false) {
        $this->_user = User::findByUsername($this->username);
    }

    return $this->_user;
}

User.php

class User extends \yii\db\ActiveRecord implements IdentityInterface
{
    public static function tableName()
   {
    return 'users';
   }

   public static function findIdentity($id)
  {
    return static::findOne($id);
  }

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

controller.php

 public function actionLogin()
{
    // Working
}

public function actionParentLogin()
{
    $model = new LoginForm();

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

            $parent = ParentLogin::find()->where(['p_username' => $model->p_username])->one();
        if($parent){
            \Yii::$app->session->set('p_id',$parent->p_id);
            return $this->redirect(['parent-dashboard']);
        }
        else
        {
            Yii::$app->getSession()->setFlash('error', Yii::t('site', 'Incorrect username or password.'));
        }
    }
    return $this->render('parent-login', [
            'model' => $model,
        ]);
}

Я не знаю, как проверить родительский логин. Я часами пытался найти обходной путь, но безуспешно.

Я застрял на Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0); потому что у таблицы пользователя нет родительских записей входа.

Мои вопросы

1) можно ли иметь два identityClass, Если да, то как?
2) Можно ли продлить ParentLogin модель для User, Если да, то как проверить?

Рекомендации

Как продлить класс пользователя?
Настройка класса CWebUser
Пользовательский класс userIdentity в yii2

2 ответа

Решение

У Джо Миллера есть хорошее предположение о наличии одного пользовательского класса и нескольких логических полей в таблице пользователей для проверки роли пользователя, в качестве альтернативы rbac. Но поскольку в вашей ситуации это невозможно, вот что я могу вам предложить (этот подход проверен наполовину и его необходимо принять).

Да, вы можете иметь два или более identityClasses, но не одновременно. Вы должны справиться с переключением между личностями. Итак, во-первых, я предлагаю вам отредактировать LoginForm Модель немного:

class LoginForm extends Model
{
    public $username;
    public $password;
    public $rememberMe = true;
    // we added this parameter to handle userModel class
    // that is responsible for getting correct user
    public $userModel;

    private $_user = false;

    /* all other methods stay same */

    /**
     * Finds user by [[username]]
     *
     * @return User|null
     */
    public function getUser()
    {
        if ($this->_user === false) {
            // calling findByUsername method dynamically
            $this->_user = call_user_func(
                [$this->userModel, 'findByUsername'], 
                $this->username
            );
        }

        return $this->_user;
    }
}

Сейчас в контроллере:

public function actionParentLogin()
{
    $model = new LoginForm(['userModel' => ParentLogin::className()]);
    // calling model->login() here as we usually do
    if ($model->load(Yii::$app->request->post()) && $model->login()) {
            // no need to worry about checking if we found parent it's all done polymorphycally for us in LoginForm
            // here is the trick, since we loggin in via parentLogin action we set this session variable.
            Yii::$app->session->set('isParent', true);
            return $this->redirect(['parent-dashboard']);
        } else {
            Yii::$app->getSession()->setFlash('error', Yii::t('site', 'Incorrect username or password.'));
        }
    }
    return $this->render('parent-login', [
            'model' => $model,
        ]);
}

Ваш parentLogin модель должна расширяться User Модель, чтобы сделать всю эту работу:

class parentLogin extends User
{
    public static function tableName()
    {
        //you parent users table name
        return 'parent_users';
    }

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

Теперь, когда вы вошли в систему, вам нужно обрабатывать идентификацию переключателя, потому что в конфигурации у вас есть 'identityClass' => 'app\models\User', Мы можем использовать bootstrap свойство для этого:

//in your config file
'bootstrap' => [
    'log',
    //component for switching identities
    'app\components\IdentitySwitcher'
],

Класс IdentitySwitcher:

class IdentitySwitcher extends Component implements BootstrapInterface
{
    public function bootstrap($app)
    {
        //we set this in parentLogin action
        //so if we loggin in as a parent user it will be true
        if ($app->session->get('isParent')) {
            $app->user->identityClass = 'app\models\ParentLogin';
        }
    }
}

** Изменить, это не работает, вы можете иметь только один класс идентификации ** ** Ссылка https://github.com/yiisoft/yii2/issues/5134 ** Я бы предложил попробовать следующее - не проверено.

В вашей конфигурации добавьте дополнительный интерфейс идентификации, как вы предлагаете;

'user' => [
        'identityClass' => 'app\models\User',
        'enableAutoLogin' => false,
        'authTimeout' => 3600*2,
    ],
'parent' => [
        'identityClass' => 'app\models\Parent',
        'enableAutoLogin' => false,
        'authTimeout' => 3600*2,
    ],

Ваш Parent модель может затем либо расширить User модель, которая даст те же методы проверки, что и оригинал User модель или реализовать IdentityInterface с нуля. Из имен ваших столбцов в вашем parent Таблица, я бы предложил второй метод, так как столбцы отличаются от User Таблица.

Тогда вам понадобятся два loginForms: loginForm а также parentLoginForm, так как валидация отличается в каждом случае.

Затем, в вашем контроллере, вы можете вызвать соответствующую форму входа в систему, как требуется.

Другие вопросы по тегам