Let's assume we have two models: Customer and Supplier and we want both to log in. Yii is quite flexible when it comes to authentication and authorization so it's possible.
First of all, Yii assumes that there's a single type of user component used at once. Still, we're able to create a universal Identity
that works with both customers and suppliers.
So we create models/Identity.php
:
final class Identity implements IdentityInterface
{
const TYPE_CUSTOMER = 'customer';
const TYPE_SUPPLIER = 'supplier';
const ALLOWED_TYPES = [self::TYPE_CUSTOMER, self::TYPE_SUPPLIER];
private $_id;
private $_authkey;
private $_passwordHash;
public static function findIdentity($id)
{
$parts = explode('-', $id);
if (\count($parts) !== 2) {
throw new InvalidCallException('id should be in form of Type-number');
}
[$type, $number] = $parts;
if (!\in_array($type, self::ALLOWED_TYPES, true)) {
throw new InvalidCallException('Unsupported identity type');
}
$model = null;
switch ($type) {
case self::TYPE_CUSTOMER:
$model = Customer::find()->where(['id' => $number])->one();
break;
case self::TYPE_SUPPLIER:
$model = Supplier::find()->where(['id' => $number])->one();
break;
}
if ($model === null) {
return false;
}
$identity = new Identity();
$identity->_id = $id;
$identity->_authkey = $model->authkey;
$identity->_passwordHash = $model->password_hash;
return $identity;
}
public static function findIdentityByAccessToken($token, $type = null)
{
$model = Customer::find()->where(['token' => $token])->one();
if (!$model) {
$model = Supplier::find()->where(['token' => $token])->one();
}
if (!$model) {
return false;
}
if ($model instanceof Customer) {
$type = self::TYPE_CUSTOMER;
} else {
$type = self::TYPE_SUPPLIER;
}
$identity = new Identity();
$identity->_id = $type . '-' . $model->id;
$identity->_authkey = $model->authkey;
$identity->_passwordHash = $model->password_hash;
return $identity;
}
public function validatePassword($password)
{
return password_verify($password, $this->_passwordHash);
}
public static function findIdentityByEmail($email)
{
$model = Customer::find()->where(['email' => $email])->one();
if (!$model) {
$model = Supplier::find()->where(['email' => $email])->one();
}
if (!$model) {
return false;
}
if ($model instanceof Customer) {
$type = self::TYPE_CUSTOMER;
} else {
$type = self::TYPE_SUPPLIER;
}
$identity = new Identity();
$identity->_id = $type . '-' . $model->id;
$identity->_authkey = $model->authkey;
$identity->_passwordHash = $model->password_hash;
return $identity;
}
public function getId()
{
return $this->_id;
}
public function getAuthKey()
{
return $this->_authkey;
}
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
}
In the above we assume that our ids are like customer-23
or supplier-34
. When we need to get identity instance we split the id by -
and getting both type and integer id for the model corresponding to that type.
Having identity we can tell Yii to use it via config/main.php
:
[
// ...
'components' => [
// ...
'user' => [
'identityClass' => 'app\models\Identity',
'enableAutoLogin' => true,
],
],
],
The only thing left is to adjust models\LoginForm.php
:
class LoginForm extends Model
{
// ...
public function getUser()
{
if ($this->_user === false) {
$this->_user = Identity::findIdentityByEmail($this->username);
}
return $this->_user;
}
}
That's it. Now you can log in using both models.
Inside the "findIdentity" function at around line number 5
`
PHP[$type, $number] = $parts;
`
php syntax error, unexpected '='
check your php version
list($type, $number) = $parts;
little typo at line:
> Having identity we can tell Yii to use it via confing/main.php:
sorry, can not edit article :-)
Thats fine But how can we implement RBAC in such a case
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.