用Yii2构建自己的API框架(五、1创建User用户模型)

598 阅读3分钟

提示

本教程相关代码托管在Gitee:gitee.com/toshcn/micr…

配置数据库参数

打开common/config/main-local.php文件,修改数据库连接Mysql,如你的数据库名为micro_hau_db,账号为root,密码为123456

<?php
// 本地相关配置项
return [
    'components' => [
        // 配置数据库组件
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=127.0.0.1;dbname=micro_hau_db', // 数据库连接
            'username' => 'root', // 数据库账号
            'password' => '123456', // 数据库账号密码
            'charset' => 'utf8', // 数据库连接字符集
            'tablePrefix' => 'hau_', // 表前缀
        ],
    ]
];

创建user表数据库迁移

在项目根目录下,打开命令行终端,运行php yii migrate/create user命令,输入yes看到如下内容:

$ php yii migrate/create user
Yii Migration Tool (based on Yii v2.0.45)

Create new migration 'D:\works\yii\micro-hau\console/migrations\m220416_133208_u
ser.php'? (yes|no) [no]:yes
New migration created successfully.

查看console/migrateions目录,是否已生成对应的文件,类名称按照 m<YYMMDD_HHMMSS>_ 的格式自动生成。 打开生成的文件,修改为以下内容:

<?php

use yii\db\Migration;

/**
 * Class m220416_133208_user
 */
class m220416_133208_user extends Migration
{
    const TB_USER = '{{%user}}'; // 用户表

    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $userOptions = null;
        if ($this->db->driverName === 'mysql') {
            // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
            $userOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB AUTO_INCREMENT=1';
        }

        $this->createTable(self::TB_USER, [
            'id' => $this->primaryKey()->unsigned()->unique(),
            'country_code' => $this->smallInteger()->unsigned()->notNull()->defaultValue(86)->comment('国家代码'),
            'mobile' => $this->bigInteger(11)->unsigned()->notNull()->defaultValue(0)->comment('手机号'),
            'nickname' => $this->string(20)->notNull()->defaultValue('')->comment('昵称'),
            'avatar' => $this->string(255)->notNull()->defaultValue('')->comment('头像链接地址'),
            'password_hash' => $this->string(72)->notNull()->comment('账号密码'),
            'password_salt' => $this->char(20)->notNull()->comment('密码加密盐'),
            'access_token' => $this->string(32)->unique()->notNull()->comment('API登录令牌'),
            'access_token_expire' => $this->bigInteger()->unsigned()->notNull()->defaultValue(0)->comment('API登录令牌有效时长'),
            'status' => $this->boolean()->notNull()->defaultValue(10)->comment('账号状态:-1已注销,0黑名单,10激活'),

            'created_at' => $this->dateTime()->notNull(),
            'updated_at' => $this->dateTime()->notNull(),
        ], $userOptions);
        // 创建手机号为索引
        $this->createIndex('idx-mobile', self::TB_USER, ['mobile', 'country_code', 'status']);
        // 创建昵称为索引
        $this->createIndex('idx-nickname', self::TB_USER, ['nickname', 'status']);
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        if (YII_ENV_DEV) {
            // 如果是测试环境 删除用户表
            $this->dropTable(self::TB_USER);
            return true;
        }
        echo "m220416_133208_user cannot be reverted.\n";

        return false;
    }
}

运行user表数据库迁移

在项目根目录下,打开命令行终端,运行php yii migrate命令,输入yes看到如下内容:

$ php yii migrate
Yii Migration Tool (based on Yii v2.0.45)

Creating migration history table "hau_migration"...Done.
Total 1 new migration to be applied:
        m220416_133208_user

Apply the above migration? (yes|no) [no]:yes
*** applying m220416_133208_user
    > create table {{%user}} ... done (time: 0.990s)
    > create index idx-mobile on {{%user}} (mobile,country_code,status) ... done
 (time: 0.107s)
    > create index idx-nickname on {{%user}} (nickname,status) ... done (time: 0
.185s)
*** applied m220416_133208_user (time: 1.558s)


1 migration was applied.

Migrated up successfully.

表示成功生成用户表。

创建User模型类

在common/models目录下,创建User.php文件,文件内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/16 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace common\modules;

use Yii;
use yii\db\ActiveRecord; // 活动记录模型
use yii\filters\RateLimitInterface; // 速率限制接口
use yii\web\IdentityInterface; // 用户身份接口

/**
 * user表模型类 继承ActiveRecord活动记录类
 * 并需实现两个接口:RateLimitInterface速率限制接口 IdentityInterface 用户身份接口
 * This is the model class for table "{{%user}}".
 *
 * @property int $id 自增id
 * @property int $country_code 国家代码
 * @property int $mobile 手机号
 * @property string $nickname 账号昵称
 * @property string $avatar 头像链接地址
 * @property string $password_hash 账号密码
 * @property string $password_salt 密码盐
 * @property string $access_token API登录令牌
 * @property int $access_token_expire API登录令牌有效时长
 * @property int $status 账号状态:账号状态:-1已注销,0黑名单,10激活
 * @property string $created_at 创建时间
 * @property string $updated_at 更新时间
 */
class User extends ActiveRecord implements IdentityInterface, RateLimitInterface
{
    const STATUS_CANCEL = -1;      // 账号状态码 已注销
    const STATUS_BLACK = 0;         // 账号状态码 黑名单
    const STATUS_ACTIVE = 10;       // 账号状态码 正常

    /**
     * 返回用户表表名
     * @return string
     */
    public static function tableName()
    {
        return '{{%user}}'; // 前%为表前缀,如果有后缀需要在表名user后加%: 即{{%user%}}
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['mobile', 'country_code', 'access_token_expire', 'status'], 'integer'], // 限定字段为数字
            [['password_hash', 'password_salt', 'access_token'], 'required'], // 限定字段为必需
            [['nickname'], 'string', 'max' => 20], // 限制昵称最长20个字符
            [['avatar'], 'string', 'max' => 255], // 限制头像地址最长255个字符
            [['password_hash'], 'string', 'max' => 72],
            [['password_salt'], 'string', 'max' => 20],
            [['access_token'], 'string', 'max' => 32],
            [['access_token'], 'unique'],
            // 限制手国家代码+机号码唯一
            [['mobile', 'country_code'], 'unique', 'targetAttribute' => ['mobile', 'country_code']],
            ['status', 'default', 'value' => static::STATUS_ACTIVE], // 账号默认状态
            ['status', 'in', 'range' => [static::STATUS_ACTIVE, static::STATUS_BLACK, static::STATUS_CANCEL]],
            [['created_at', 'updated_at'], 'safe'],
        ];
    }

    /**
     * 实现查找用户接口
     * 通过用户ID方式 查找用户表对应记录
     * @param int|string $id 用户表自增ID字段
     * @return User|null|IdentityInterface 返回用户模型对象
     */
    public static function findIdentity($id)
    {
        return static::findOne(['id' => (int) $id]);
    }

    /**
     * 实现查找用户接口
     * 通过access_token方式
     * @param string $token 令牌
     * @param null $type 令牌类型
     * @return array|null|\yii\db\ActiveRecord
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        // 通过令牌找到用户,令牌需未过期
        return static::find()
            ->where(['and', ['access_token' => $token], ['>', 'access_token_expire', time()]])
            ->one();
    }

    /**
     * 实现获取用户ID接口
     * @return int|mixed|string
     */
    public function getId()
    {
        return $this->getPrimaryKey();
    }

    /**
     * 实现getAuthKey接口
     * @return null|string
     */
    public function getAuthKey()
    {
        return '';
    }

    /**
     * 实现验证authKey接口
     * @param string $authKey
     * @return bool|null
     */
    public function validateAuthKey($authKey)
    {
        return false;
    }

    /**
     * 实现请求速率限制接口
     * 返回允许的请求速率最大数目及限制时间
     * @param \yii\web\Request $request 当前请求
     * @param \yii\base\Action $action 执行的动作
     * @return array
     */
    public function getRateLimit($request, $action)
    {
        // 表示在 3 秒内最多 500 次的 API 调用
        return [500, 3];
    }

    /**
     * 实现请求速率限制接口
     * 返回当前允许的请求次数和最后一次速率限制检查时 相应的 UNIX 时间戳数
     * @param \yii\web\Request $request 当前请求
     * @param \yii\base\Action $action 执行的动作
     * @return array. 数组第一个元素为当前允许请求的次数, 第二个元素为相应的时间戳.
     */
    public function loadAllowance($request, $action)
    {
        // 从缓存中获取用户允许的请求次数 和 最后一次速率限制检查时间戳
        $limit = (int) Yii::$app->getCache()->get('rateLimiter_allowance_' . $this->id);
        if ($limit === false) { // 如果不存在 返回默认速率
            $limit = 500;
            Yii::$app->getCache()->set('rateLimiter_allowance_' . $this->id, $limit);
        }

        $time = Yii::$app->getCache()->get('rateLimiter_allowance_updated_at_' . $this->id);
        if ($time === false) {
            $time = time();
            Yii::$app->getCache()->set('rateLimiter_allowance_updated_at_' . $this->id, $time);
        }
        return [$limit, $time];
    }

    /**
     * 实现保存用户剩余请求数接口
     * @param \yii\web\Request $request 当前请求
     * @param \yii\base\Action $action 执行的动作
     * @param int $allowance 剩余的允许的请求次数.
     * @param int $timestamp 最后一次速率限制检查时间戳.
     */
    public function saveAllowance($request, $action, $allowance, $timestamp)
    {
        Yii::$app->getCache()->set('rateLimiter_allowance_' . $this->id, $allowance);
        Yii::$app->getCache()->set('rateLimiter_allowance_updated_at_' . $this->id, $timestamp);
    }

    /**
     * 登录成功后事件处理器
     * @param object $event 事件对象
     * @return bool
     */
    public static function afterLoginHandler(&$event)
    {
        return true; // todo
    }
}