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

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

Config.php

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

ЛогинФорма.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]);
 }

Контроллер.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


person Insane Skull    schedule 23.01.2016    source источник
comment
Вы уже пытались расширить нужный класс в User.php?   -  person Guilherme Lopes    schedule 23.01.2016
comment
@ГильермеЛопес. Я пробовал это, но не удалось. Итак, откат изменений.   -  person Insane Skull    schedule 23.01.2016
comment
Я всегда делаю так... Я использую модель по умолчанию и расширяю одну из своих, изменяя функции модели по умолчанию с помощью findOne's   -  person Guilherme Lopes    schedule 23.01.2016
comment
@ГильермеЛопес. Не могли бы вы уточнить это с ответом. На решение у меня ушло полдня.   -  person Insane Skull    schedule 23.01.2016
comment
Я не понимаю, зачем вам нужны отдельные таблицы и модели для этой ситуации. Почему бы просто не добавить дополнительный столбец в таблицу user, назвать его isParent и установить для него значение boolean. Тогда вы можете просто проверить, правда ли это, сделать что-то, если оно ложно, сделать что-то еще. Тогда вы просто используете одну модель, один набор проверки и два представления в зависимости от значения isParent.   -  person Joe Miller    schedule 23.01.2016
comment
@Джо Миллер. Если я объединяю две таблицы, количество записей превышает границы базы данных. Для логического предложения неприменимо, потому что у меня уже есть три разных пользователя в модели User. У меня отдельный логин для parentLogin модели.   -  person Insane Skull    schedule 23.01.2016


Ответы (2)


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

Да, вы можете иметь два или более идентификаторов, но не одновременно. Вам нужно обрабатывать переключение между удостоверениями. Итак, сначала я предлагаю вам немного отредактировать вашу модель 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';
        }
    }
}
person Tony    schedule 23.01.2016
comment
Спасибо. @Тони. Проверка. - person Insane Skull; 23.01.2016
comment
Хорошо, дайте мне знать, как это происходит - person Tony; 23.01.2016
comment
какой компонент для переключения личности в config - person Insane Skull; 23.01.2016
comment
извините, не понял. чат Yii - person Insane Skull; 23.01.2016
comment
вам нужно создать класс. В этом примере я назвал его IdentitySwitcher. Итак, вам нужно создать каталог components и создать в нем файл IdentitySwitcher.php с содержимым из примера - person Tony; 23.01.2016
comment
Не могли бы вы посетить чат yii. - person Insane Skull; 23.01.2016

** Изменить, это не работает, у вас может быть только один класс идентификации ** ** Ссылка 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, так как проверка в каждом случае отличается.

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

person Joe Miller    schedule 23.01.2016
comment
Я уже пробовал, не получится. он дает первую ошибку «отсутствует класс», а после добавления дает ошибку identity. - person Insane Skull; 23.01.2016
comment
Да, только что узнал, что это не сработает, поддерживается только один класс идентификации. Я оставлю это здесь, чтобы люди знали, что это не сработает! - person Joe Miller; 23.01.2016