-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7fbf128
commit 8fc21af
Showing
3 changed files
with
426 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
<?php | ||
|
||
namespace yii1tech\web\user; | ||
|
||
use CBehavior; | ||
use CEvent; | ||
|
||
/** | ||
* ActiveRecordModelBehavior allows operating AcriveRecord model at the WebUser component level. | ||
* | ||
* Application configuration example: | ||
* | ||
* ```php | ||
* return [ | ||
* 'components' => [ | ||
* 'user' => [ | ||
* 'class' => yii1tech\web\user\WebUser::class, | ||
* 'behaviors' => [ | ||
* 'modelBehavior' => [ | ||
* 'class' => yii1tech\web\user\ActiveRecordModelBehavior::class, | ||
* 'modelClass' => app\models\User::class, | ||
* ], | ||
* ], | ||
* ], | ||
* // ... | ||
* ], | ||
* // ... | ||
* ]; | ||
* ``` | ||
* | ||
* Model access example: | ||
* | ||
* ```php | ||
* $model = Yii::app()->user->getModel(); | ||
* ``` | ||
* | ||
* @property \yii1tech\web\user\WebUser $owner | ||
* @property \CActiveRecord|null $model | ||
* | ||
* @author Paul Klimov <[email protected]> | ||
* @since 1.0 | ||
*/ | ||
class ActiveRecordModelBehavior extends CBehavior | ||
{ | ||
/** | ||
* @var string|\CActiveRecord name of the {@see \CActiveRecord} model class, which stores users. | ||
*/ | ||
public $modelClass; | ||
|
||
/** | ||
* @var array|string|\CDbCriteria user model search query additional condition or criteria. | ||
* For example: | ||
* | ||
* ```php | ||
* [ | ||
* 'scopes' => [ | ||
* 'activeOnly', | ||
* ], | ||
* ] | ||
* ``` | ||
*/ | ||
public $modelFindCriteria = ''; | ||
|
||
/** | ||
* @var bool whether to automatically synchronize owner WebUser component with model. | ||
* @see syncModel() | ||
*/ | ||
public $autoSyncModel = true; | ||
|
||
/** | ||
* @var array<string, string> map defining which model attribute should be saved as WebUser state on model synchronization. | ||
* For example: | ||
* | ||
* ```php | ||
* [ | ||
* 'username' => '__name', | ||
* 'email' => 'email', | ||
* ] | ||
* ``` | ||
*/ | ||
public $attributeToStateMap = []; | ||
|
||
/** | ||
* @var array<int, \CActiveRecord> stores related model instance. | ||
*/ | ||
private $_model = []; | ||
|
||
/** | ||
* Returns model matching currently authenticated user. | ||
* | ||
* @return \CActiveRecord|null user model, `null` - if not found. | ||
*/ | ||
public function getModel() | ||
{ | ||
$userId = $this->owner->getId(); | ||
|
||
if (empty($userId)) { | ||
return null; | ||
} | ||
|
||
if (!array_key_exists($userId, $this->_model)) { | ||
$this->_model = [ | ||
$userId => $this->findModel($userId), | ||
]; | ||
} | ||
|
||
return $this->_model[$userId]; | ||
} | ||
|
||
/** | ||
* Sets the user model, changing authenticated user' ID at related WebUser component. | ||
* | ||
* > Note: this method can be used for user identity switching, however it is not equal | ||
* to {@see \CWebUser::login()} or {@see \CWebUser::changeIdentity()}. | ||
* | ||
* @param \CActiveRecord|null $model user model. | ||
* @return \yii1tech\web\user\WebUser|static owner WebUser component. | ||
*/ | ||
public function setModel($model) | ||
{ | ||
if (empty($model)) { | ||
$this->_model = []; | ||
|
||
$this->owner->setId(null); | ||
|
||
return $this->owner; | ||
} | ||
|
||
$userId = $model->getPrimaryKey(); | ||
|
||
$this->_model = [ | ||
$userId => $model, | ||
]; | ||
|
||
$this->owner->setId($userId); | ||
|
||
return $this->syncModel(); | ||
} | ||
|
||
/** | ||
* Finds user model by the given ID. | ||
* | ||
* @param mixed $userId user's ID. | ||
* @return \CActiveRecord|null user model, `null` - if not found. | ||
*/ | ||
protected function findModel($userId) | ||
{ | ||
return $this->modelClass::model($this->modelClass) | ||
->findByPk($userId, $this->modelFindCriteria); | ||
} | ||
|
||
/** | ||
* Synchronizes owner WebUser component with the model. | ||
* If related model does not exist performs logout. | ||
* If related model does exist - synchronizes WebUser states with it. | ||
* | ||
* @return \yii1tech\web\user\WebUser|static owner WebUser component. | ||
*/ | ||
public function syncModel() | ||
{ | ||
if ($this->owner->getIsGuest()) { | ||
return $this->owner; | ||
} | ||
|
||
$model = $this->getModel(); | ||
if (empty($model)) { | ||
$this->owner->logout(false); | ||
} else { | ||
foreach ($this->attributeToStateMap as $attribute => $state) { | ||
$this->owner->setState($state, $model->{$attribute}); | ||
} | ||
} | ||
|
||
return $this->owner; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function events() | ||
{ | ||
return [ | ||
'onAfterRestore' => 'afterRestore', | ||
]; | ||
} | ||
|
||
/** | ||
* Responds to {@see \yii1tech\web\user\WebUser::$onAfterRestore} event. | ||
* | ||
* @param \CEvent $event event parameter. | ||
*/ | ||
public function afterRestore(CEvent $event): void | ||
{ | ||
if ($this->autoSyncModel) { | ||
$this->syncModel(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
<?php | ||
|
||
namespace yii1tech\web\user\test; | ||
|
||
use Yii; | ||
use yii1tech\web\user\ActiveRecordModelBehavior; | ||
use yii1tech\web\user\test\support\User; | ||
use yii1tech\web\user\WebUser; | ||
|
||
class ActiveRecordModelBehaviorTest extends TestCase | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->createTestDbSchema(); | ||
$this->seedTestDb(); | ||
} | ||
|
||
/** | ||
* @return string test table name | ||
*/ | ||
protected function getTestTableName(): string | ||
{ | ||
return 'user'; | ||
} | ||
|
||
/** | ||
* Creates test config table. | ||
*/ | ||
protected function createTestDbSchema(): void | ||
{ | ||
$dbConnection = Yii::app()->db; | ||
$columns = [ | ||
'id' => 'pk', | ||
'username' => 'string', | ||
'email' => 'string', | ||
'status' => 'int', | ||
]; | ||
$dbConnection->createCommand()->createTable($this->getTestTableName(), $columns); | ||
} | ||
|
||
/** | ||
* Inserts test data to the database. | ||
*/ | ||
protected function seedTestDb(): void | ||
{ | ||
$dbConnection = Yii::app()->db; | ||
|
||
$dbConnection->getCommandBuilder()->createMultipleInsertCommand($this->getTestTableName(), [ | ||
[ | ||
'username' => 'active-user', | ||
'email' => '[email protected]', | ||
'status' => User::STATUS_ACTIVE, | ||
], | ||
[ | ||
'username' => 'inactive-user', | ||
'email' => '[email protected]', | ||
'status' => User::STATUS_INACTIVE, | ||
], | ||
])->execute(); | ||
} | ||
|
||
/** | ||
* @param array $behaviorConfig | ||
* @return \yii1tech\web\user\WebUser|\yii1tech\web\user\ActiveRecordModelBehavior | ||
*/ | ||
protected function createWebUser(array $behaviorConfig = []): WebUser | ||
{ | ||
$user = Yii::createComponent([ | ||
'class' => WebUser::class, | ||
'stateKeyPrefix' => '', | ||
'behaviors' => [ | ||
'modelBehavior' => array_merge([ | ||
'class' => ActiveRecordModelBehavior::class, | ||
'modelClass' => User::class, | ||
], $behaviorConfig), | ||
], | ||
]); | ||
|
||
$user->init(); | ||
|
||
return $user; | ||
} | ||
|
||
public function testGetModel(): void | ||
{ | ||
$webUser = $this->createWebUser(); | ||
|
||
$this->assertNull($webUser->getModel()); | ||
|
||
$webUser->setId(1); | ||
|
||
$model = $webUser->getModel(); | ||
|
||
$this->assertTrue($model instanceof User); | ||
|
||
$webUser->setId(99); | ||
|
||
$this->assertNull($webUser->getModel()); | ||
} | ||
|
||
/** | ||
* @depends testGetModel | ||
*/ | ||
public function testSetModel(): void | ||
{ | ||
$webUser = $this->createWebUser(); | ||
|
||
$model = User::model()->findByPk(1); | ||
|
||
$webUser->setModel($model); | ||
|
||
$this->assertSame($model, $webUser->getModel()); | ||
$this->assertEquals($model->id, $webUser->getId()); | ||
} | ||
|
||
/** | ||
* @depends testGetModel | ||
*/ | ||
public function testSyncModel(): void | ||
{ | ||
Yii::app()->session->open(); | ||
|
||
$_SESSION['__id'] = 1; | ||
|
||
$webUser = $this->createWebUser([ | ||
'autoSyncModel' => false, | ||
'attributeToStateMap' => [ | ||
'username' => '__name', | ||
'email' => 'email', | ||
], | ||
]); | ||
|
||
$webUser->syncModel(); | ||
|
||
$this->assertNotEmpty($webUser->getModel()); | ||
|
||
$this->assertEquals('active-user', $webUser->getName()); | ||
$this->assertEquals('[email protected]', $webUser->getState('email')); | ||
|
||
$_SESSION['__id'] = 99; | ||
|
||
$webUser->syncModel(); | ||
|
||
$this->assertNull($webUser->getModel()); | ||
$this->assertNull($webUser->getId()); | ||
} | ||
|
||
/** | ||
* @depends testSyncModel | ||
*/ | ||
public function testAutoSyncModel(): void | ||
{ | ||
Yii::app()->session->open(); | ||
|
||
$_SESSION['__id'] = 99; | ||
|
||
$webUser = $this->createWebUser([ | ||
'autoSyncModel' => true, | ||
]); | ||
|
||
$this->assertNull($webUser->getModel()); | ||
$this->assertNull($webUser->getId()); | ||
} | ||
|
||
/** | ||
* @depends testGetModel | ||
*/ | ||
public function testModelFindCriteria(): void | ||
{ | ||
$webUser = $this->createWebUser([ | ||
'autoSyncModel' => false, | ||
'modelFindCriteria' => [ | ||
'condition' => 'status = :status', | ||
'params' => [ | ||
'status' => User::STATUS_ACTIVE, | ||
], | ||
], | ||
]); | ||
|
||
$webUser->setId(1); | ||
$model = $webUser->getModel(); | ||
|
||
$this->assertNotEmpty($model); | ||
|
||
$webUser->setId(2); | ||
$model = $webUser->getModel(); | ||
|
||
$this->assertEmpty($model); | ||
} | ||
} |
Oops, something went wrong.