1. Definitions and conventions:
Definitions:
(1) Data layer: models is used to define data-related automatic validation, automatic completion, and data access interfaces;
(2) Logic layer: logics is used to define data-related business logic;
Conventions:
(1) Model class files in the /common/models directory are only allowed to be generated by the Gii tool, serving as the common model data layer;
(2) Model class files in the /common/logics directory are business logic related, inheriting from the /common/models data layer, serving as the common model logic layer;
(3) When referencing model class files in the /common directory, only reference the model class files in /common/logics, for example:
public $modelClass = 'common\logics\User';
(4) Model class files in the /api/models, /backend/models, and /frontend/models directories are business logic related (only related to their respective applications), inheriting from the /common/logics common logic layer;
(5) When referencing model class files in the API, frontend, and backend application directories, only reference the corresponding model class files in their own directories, for example:
use api\models\User;
(6) Model class files in the /api/modules/v1/models directory are business logic related (only related to the module), inheriting from the /api/models application logic layer;
(7) When referencing model class files in the module directory, only reference the corresponding model class files in their own module, for example:
public $modelClass = 'api\modules\v1\models\User';
2. Create a logics directory in the common directory to serve as the directory for the MySQL model logic layer, as shown in Figure 1

3. Copy \common\models\LoginForm.php and \common\models\User.php to \common\logics, as shown in Figure 2

4. MySQL model files in the common/models directory should only be generated by the Gii tool; delete \common\models\LoginForm.php, as shown in Figure 3

5. Configure routing to use pretty URLs; edit \frontend\config\main.php
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
],
],
6. For internationalization support, create directories \common\messages\en-US\model and \common\messages\zh-CN\model for the model data layer’s US English and Simplified Chinese, respectively; create \common\messages\{language}\app.php, \common\messages\{language}\error.php, and \common\messages\{language}\success.php for application global messages, application error messages, and application success messages, respectively, as shown in Figure 4

app.php
<?php
return [
];
error.php
<?php
return [
20000 => 'error',
];success.php
<?php
return [
10000 => 'success',
];7. Configure the application language. The source language is US English and the target language is Simplified Chinese. When the source and target languages are the same, whether to force message translation defaults to false; set it to true. Edit \common\config\main.php
'sourceLanguage' => 'en-US',
'language' => 'zh-CN',
'components' => [
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource',
'forceTranslation' => true,
'basePath'=>'@common/messages',
'fileMap' => [
'app' => 'app.php',
'error' => 'error.php',
'success' => 'success.php',
],
],
],
],
'cache' => [
'class' => 'yii\caching\FileCache',
],
],
8. Open the URL: http://www.github-shuijingwan-yii2-app-advanced.localhost/gii/model . In the options, set the namespace to common\models. At this point, internationalization support is required; overwrite \common\models\User.php, as shown in Figure 5

<?php
namespace common\models;
use Yii;
/**
* This is the model class for table "{{%user}}".
*
* @property int $id
* @property string $username
* @property string $auth_key
* @property string $password_hash
* @property string $password_reset_token
* @property string $email
* @property int $status
* @property int $created_at
* @property int $updated_at
*/
class User extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%user}}';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['username', 'auth_key', 'password_hash', 'email', 'created_at', 'updated_at'], 'required'],
[['status', 'created_at', 'updated_at'], 'integer'],
[['username', 'password_hash', 'password_reset_token', 'email'], 'string', 'max' => 255],
[['auth_key'], 'string', 'max' => 32],
[['username'], 'unique'],
[['email'], 'unique'],
[['password_reset_token'], 'unique'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('model/user', 'ID'),
'username' => Yii::t('model/user', 'Username'),
'auth_key' => Yii::t('model/user', 'Auth Key'),
'password_hash' => Yii::t('model/user', 'Password Hash'),
'password_reset_token' => Yii::t('model/user', 'Password Reset Token'),
'email' => Yii::t('model/user', 'Email'),
'status' => Yii::t('model/user', 'Status'),
'created_at' => Yii::t('model/user', 'Created At'),
'updated_at' => Yii::t('model/user', 'Updated At'),
];
}
}
9. MySQL model files in the common/logics directory are business logic related and inherit from the \common\models data layer. Open the URL: http://www.github-shuijingwan-yii2-app-advanced.localhost/gii/model , options, as shown in Figure 6

10. Based on the diff, edit \common\logics\User.php, as shown in Figure 7
11. MySQL model files in the common/logics directory are business logic related and inherit from the \common\models\User data layer
<?php
namespace common\logics;
use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\web\IdentityInterface;
/**
* This is the model class for table "{{%user}}".
*
* @property int $id
* @property string $username
* @property string $auth_key
* @property string $password_hash
* @property string $password_reset_token
* @property string $email
* @property int $status
* @property int $created_at
* @property int $updated_at
* @property string $password write-only password
*/
class User extends \common\models\User implements IdentityInterface
{
const STATUS_DELETED = 0;
const STATUS_ACTIVE = 10;
/**
* @inheritdoc
*/
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['username', 'auth_key', 'password_hash', 'email'], 'required'],
[['status', 'created_at', 'updated_at'], 'integer'],
[['username', 'password_hash', 'password_reset_token', 'email'], 'string', 'max' => 255],
[['auth_key'], 'string', 'max' => 32],
[['username'], 'unique'],
[['email'], 'unique'],
[['password_reset_token'], 'unique'],
['status', 'default', 'value' => self::STATUS_ACTIVE],
['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
];
}
/**
* @inheritdoc
*/
public static function findIdentity($id)
{
return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
}
/**
* @inheritdoc
*/
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
/**
* Finds user by username
*
* @param string $username
* @return static|null
*/
public static function findByUsername($username)
{
return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]);
}
/**
* Finds user by password reset token
*
* @param string $token password reset token
* @return static|null
*/
public static function findByPasswordResetToken($token)
{
if (!static::isPasswordResetTokenValid($token)) {
return null;
}
return static::findOne([
'password_reset_token' => $token,
'status' => self::STATUS_ACTIVE,
]);
}
/**
* Finds out if password reset token is valid
*
* @param string $token password reset token
* @return bool
*/
public static function isPasswordResetTokenValid($token)
{
if (empty($token)) {
return false;
}
$timestamp = (int) substr($token, strrpos($token, '_') + 1);
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
return $timestamp + $expire >= time();
}
/**
* @inheritdoc
*/
public function getId()
{
return $this->getPrimaryKey();
}
/**
* @inheritdoc
*/
public function getAuthKey()
{
return $this->auth_key;
}
/**
* @inheritdoc
*/
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
/**
* Validates password
*
* @param string $password password to validate
* @return bool if password provided is valid for current user
*/
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
/**
* Generates password hash from password and sets it to the model
*
* @param string $password
*/
public function setPassword($password)
{
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
/**
* Generates "remember me" authentication key
*/
public function generateAuthKey()
{
$this->auth_key = Yii::$app->security->generateRandomString();
}
/**
* Generates new password reset token
*/
public function generatePasswordResetToken()
{
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
}
/**
* Removes password reset token
*/
public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
}
12. Create \common\messages\en-US\model\user.php to support message translation when the target language is US English
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 10:34
*/
return [
'ID' => 'ID',
'Username' => 'Username',
'Auth Key' => 'Auth Key',
'Password Hash' => 'Password Hash',
'Password Reset Token' => 'Password Reset Token',
'Email' => 'Email',
'Status' => 'Status',
'Created At' => 'Created At',
'Updated At' => 'Updated At',
];13. Create \common\messages\zh-CN\model\user.php to support message translation when the target language is Simplified Chinese
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 10:39
*/
return [
'ID' => 'ID',
'Username' => '用户名',
'Auth Key' => '认证密钥',
'Password Hash' => '密码哈希',
'Password Reset Token' => '密码重置令牌',
'Email' => '邮箱',
'Status' => '状态',
'Created At' => '创建时间',
'Updated At' => '更新时间',
];14. Create \api\models\User.php. MySQL model files in the api/models directory are business logic related (only related to the API) and inherit from the \common\logics\User logic layer
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 10:44
*/
namespace api\models;
class User extends \common\logics\User
{
}
15. Copy \api\models\User.php to \frontend\models\User.php and \backend\models\User.php, and adjust their respective namespaces
\frontend\models\User.php
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 10:44
*/
namespace frontend\models;
class User extends \common\logics\User
{
}
\backend\models\User.php
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 10:44
*/
namespace backend\models;
class User extends \common\logics\User
{
}
16. In the API application, search for use common\models\User; and replace it with: use api\models\User;. Apply similar processing in the frontend and backend applications, as shown in Figure 8

17. In the API application, search for common\models\User and replace it with: api\models\User. Apply similar processing in the frontend and backend applications, as shown in Figure 9

18. Edit \common\logics\LoginForm.php to adjust the namespace
<?php
namespace common\logics;
use Yii;
use yii\base\Model;
/**
* Login form
*/
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
private $_user;
/**
* {@inheritdoc}
*/
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 bool 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
*/
protected function getUser()
{
if ($this->_user === null) {
$this->_user = User::findByUsername($this->username);
}
return $this->_user;
}
}
19. Create \api\models\LoginForm.php. MySQL model files in the api/models directory are business logic related (only related to the API) and inherit from the \common\logics\LoginForm logic layer
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 11:10
*/
namespace api\models;
class LoginForm extends \common\logics\LoginForm
{
}
20. Copy \api\models\LoginForm.php to \frontend\models\LoginForm.php and \backend\models\LoginForm.php, and adjust their respective namespaces
\frontend\models\LoginForm.php
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 11:10
*/
namespace frontend\models;
class LoginForm extends \common\logics\LoginForm
{
}
\backend\models\LoginForm.php
<?php
/**
* Created by PhpStorm.
* User: WangQiang
* Date: 2018/04/04
* Time: 11:10
*/
namespace backend\models;
class LoginForm extends \common\logics\LoginForm
{
}
21. In the API application, search for use common\models\LoginForm; and replace it with: use api\models\LoginForm;. Apply similar processing in the frontend and backend applications, as shown in Figure 10

22. In the API application, search for common\models\LoginForm and replace it with: api\models\LoginForm. Apply similar processing in the frontend and backend applications, as shown in Figure 11

23. In the common directory, search for common\models\User and replace it with: common\logics\User, with the exception of \common\logics\User.php, as shown in Figure 12

24. In the common directory, search for common\models\LoginForm and replace it with: common\logics\LoginForm, as shown in Figure 13

25. Test the registration feature by opening the URL: http://www.github-shuijingwan-yii2-app-advanced.localhost/site/signup . It works as expected, as shown in Figure 14

26. View the database, as shown in Figure 15


