没有不值得去解决的问题,也没有不值得去学习的技术!

基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,UUID 的实现

执行数据库迁移,查看表结构,基于 Gii 重新生成模型文件

Yii2 Advanced 实战系列:RESTful API + RPC 服务 + 国际化 + 日志与测试

新建 api 目录、配置和环境、测试、Vagrant 等的支持。

(1) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),新建api目录、配置和环境、测试、Vagrant等的支持

(2) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),实现模型分层:数据层、逻辑层,明确公共目录、应用、模块的继承、引用关系

在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users ,200响应,message为简体中文

(3) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),实现 RESTful Web 服务,支持国际化(动态地设置目标语言,默认为简体中文)

运行测试,获取详细的输出,可看到一步一步的行为报告,符合预期

(4) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),在 api 的 tests 目录中准备用户相关操作的一些自动化测试的样例(API 测试),确保应用程序在改变或增加新的功能时不会影响现有的功能

在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/logs?filter[level]=4&filter[category][like]=api\behaviors\RequestLogBehavior&filter[prefix][like]=app-api&filter[log_time][gte]=1528090828&filter[log_time][lte]=1529564924.6648 ,200响应,空数组已经被自动转换为空对象,以保证字段格式的统一,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/logs/3

(5) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),调整默认字符集为:utf8mb4,接口响应格式的调整,空数组自动转换为空对象,在接口应用中收集请求日志消息(1个请求对应1条日志消息)至数据库,且实现日志功能的相应接口:日志列表(设置数据过滤器以启用筛选器处理)、日志详情

在开发环境中,执行数据库迁移命令失败:1071 Specified key was too long; max key length is 767 bytes

(6) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),调整默认字符集为:utf8mb4,接口响应格式的调整,空数组自动转换为空对象,在接口应用中收集请求日志消息(1个请求对应1条日志消息)至数据库,且实现日志功能的相应接口:日志列表(设置数据过滤器以启用筛选器处理)、日志详情(二)

浏览 user 表数据,status、updated_at 已经更新

(7) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),实现 ActiveRecord 的软删除,生成 ActiveQuery,自定义查询类

执行数据库迁移命令,查看数据库表 user 结构,符合预期

(8) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),实现 ActiveRecord 的软删除,生成 ActiveQuery,自定义查询类(二)

在 Postman 中 POST 请求,http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users ,输入格式:application/json

(9) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),application/x-www-form-urlencoded 和 multipart/form-data 输入格式,默认支持,新增 application/json 输入格式的支持

打开 Windows PowerShell,执行 init 命令并选择 dev 作为环境,api应用所需环境配置文件自动生成

(10) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建远程过程调用应用(实现基于 Hprose 2.0 for PHP 的 RPC 服务端),新建 rpc 目录、配置和环境、测试、Vagrant等的支持

出现以上原因是因为 Windows 下 nginx php 环境,不支持并发的原因,当同时访问多个域名,并且同时指向你本地服务的时候,就不支持并发了。nginx.conf 里面 对不同 server 修改 fastcgi_pass 的端口号,启动多个 php-cgi。

(11) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建远程过程调用应用(实现基于 Hprose 2.0 for PHP 的 RPC 服务端),在 rpc 目录中实现 页面 的相应 RPC 服务,且在 api 目录中的 API 实现 RPC 客户端

执行数据库迁移,查看表结构,基于 Gii 重新生成模型文件

(12) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,UUID 的实现

由于日志消息是存储于 MySQL 的 log 表中,在生产环境中,累积了大量的数据,log 表的数据长度已经达到 数百 GB ,因此,决定基于控制台命令行实现日志消息的定时删除,开发环境的 log 表的数据长度为 14.64 GB

(13) 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,日志消息的自动定时删除

1、在 Github 上搜索 yii2 uuid,选择 wartron/yii2-uuid,如图1

在 Github 上搜索 yii2 uuid,选择 wartron/yii2-uuid
图1

2、基于 Composer 安装,报错

Bash
composer require wartron/yii2-uuid "*"

Plaintext
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

Problem 1
- The requested package wartron/yii2-uuid * is satisfiable by wartron/yii2-uuid[dev-master] but these conflict with
your requirements or minimum-stability.

Installation failed, reverting ./composer.json to its original content.

3、基于 Composer 安装,安装成功,如图2

基于 Composer 安装,报错
图2

Bash
composer require wartron/yii2-uuid "dev-master"

Plaintext
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
  - Installing paragonie/random_compat (v2.0.17): Downloading (100%)
  - Installing ramsey/uuid (2.9.0): Downloading (100%)
  - Installing wartron/yii2-uuid (dev-master 2202443): Cloning 2202443ac7 from cache
paragonie/random_compat suggests installing ext-libsodium (Provides a modern crypto API that can be used to generate ran
dom bytes.)
ramsey/uuid suggests installing moontoast/math (Support for converting UUID to 128-bit integer (in string form).)
ramsey/uuid suggests installing doctrine/dbal (Allow the use of a UUID as doctrine field type.)
Writing lock file
Generating autoload files

4、新建数据库迁移,添加 uuid 字段到 page 模型

Bash
.\yii migrate/create add_uuid_to_page

5、编辑数据库迁移文件,\console\migrations\m180807_032326_add_uuid_to_page.php,设置 uuid 为唯一索引

PHP
<?php
 
use yii\db\Migration;
 
/**
 * Class m180807_032326_add_uuid_to_page
 */
class m180807_032326_add_uuid_to_page extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->addColumn('{{%page}}', 'uuid', $this->string(64)->notNull()->comment('通用唯一识别码')->after('id'));
        $this->createIndex('uc_uuid', '{{%page}}', 'uuid', $unique = true);
    }
 
    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        $this->dropColumn('{{%page}}', 'uuid');
    }
 
    /*
    // Use up()/down() to run migration code without a transaction.
    public function up()
    {
 
    }
 
    public function down()
    {
        echo "m180807_032326_add_uuid_to_page cannot be reverted.\n";
 
        return false;
    }
    */
}
 
 

6、执行数据库迁移,查看表结构,基于 Gii 重新生成模型文件,如图3

执行数据库迁移,查看表结构,基于 Gii 重新生成模型文件
图3
Bash
.\yii migrate

7、附加行为 wartron\yii2uuid\behaviors\UUIDBehavior,编辑 \common\logics\Page.php

PHP
<?php
 
namespace common\logics;
 
use Yii;
use yii\behaviors\SluggableBehavior;
use yii\behaviors\TimestampBehavior;
use yii2tech\ar\softdelete\SoftDeleteBehavior;
use wartron\yii2uuid\behaviors\UUIDBehavior;
use yii\helpers\ArrayHelper;
 
class Page extends \common\models\Page
{
    const STATUS_DELETED = -1; //状态:删除
    const STATUS_DISABLED = 0; //状态:禁用
    const STATUS_DRAFT = 1; //状态:草稿
    const STATUS_PUBLISHED = 2; //状态:发布
 
    const SCENARIO_CREATE = 'create';
 
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'timestampBehavior' => [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    self::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
                    self::EVENT_BEFORE_UPDATE => 'updated_at',
                    SoftDeleteBehavior::EVENT_BEFORE_SOFT_DELETE => 'updated_at',
                ]
            ],
            'slug' => [
                'class' => SluggableBehavior::className(),
                'attribute' => 'title',
                'ensureUnique' => true,
                'immutable' => true
            ],
            'uuid' => [
                'class' => UUIDBehavior::className(),
                'column' => 'uuid'
            ],
            'softDeleteBehavior' => [
                'class' => SoftDeleteBehavior::className(),
                'softDeleteAttributeValues' => [
                    'status' => self::STATUS_DELETED
                ],
            ],
        ];
    }
 
    /**
     * {@inheritdoc}
     */
    public function scenarios()
    {
        $scenarios = parent::scenarios();
        $scenarios[self::SCENARIO_CREATE] = ['slug', 'title', 'body', 'view', 'status'];
 
        return $scenarios;
    }
 
    /**
     * @inheritdoc
     */
    public function rules()
    {
        $rules = [
            [['title', 'body'], 'required'],
            [['slug'], 'unique'],
            ['view', 'default', 'value' => 0],
            ['status', 'default', 'value' => self::STATUS_DRAFT],
        ];
        $parentRules = parent::rules();
 
        unset($parentRules[0]);
 
        return ArrayHelper::merge($rules, $parentRules);
    }
 
    /**
     * {@inheritdoc}
     * @return PageQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new PageQuery(get_called_class());
    }
 
}

8、在 Postman 中 POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/pages ,报错:iconv(): Detected an illegal character in input string

JSON
{
    "name": "Exception",
    "message": "iconv(): Detected an illegal character in input string",
    "code": 0,
    "type": "Exception",
    "file": "E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\hprose\\hprose\\src\\Hprose\\Client.php",
    "line": 383,
    "stack-trace": [
        "#0 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\hprose\\hprose\\src\\Hprose\\Client.php(477): Hprose\\Client->decode('Es54\"iconv(): D...', Array, Object(stdClass))",
        "#1 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\hprose\\hprose\\src\\Hprose\\Client.php(489): Hprose\\Client->syncInvokeHandler('page_create', Array, Object(stdClass))",
        "#2 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\hprose\\hprose\\src\\Hprose\\Client.php(103): Hprose\\Client->invokeHandler('page_create', Array, Object(stdClass))",
        "#3 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\hprose\\hprose\\src\\Hprose\\Client.php(608): Hprose\\Client->Hprose\\{closure}('page_create', Array, Object(stdClass))",
        "#4 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\hprose\\hprose\\src\\Hprose\\Proxy.php(91): Hprose\\Client->invoke('page_create', Array)",
        "#5 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\common\\logics\\rpc\\Page.php(83): Hprose\\Proxy->__call('page_create', Array)",
        "#6 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\api\\rests\\page\\CreateAction.php(68): common\\logics\\rpc\\Page->create(Array, '0.0', 'zh-CN')",
        "#7 [internal function]: api\\rests\\page\\CreateAction->run()",
        "#8 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\yiisoft\\yii2\\base\\Action.php(94): call_user_func_array(Array, Array)",
        "#9 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\yiisoft\\yii2\\base\\Controller.php(157): yii\\base\\Action->runWithParams(Array)",
        "#10 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\yiisoft\\yii2\\base\\Module.php(528): yii\\base\\Controller->runAction('create', Array)",
        "#11 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\yiisoft\\yii2\\web\\Application.php(103): yii\\base\\Module->runAction('v1/page/create', Array)",
        "#12 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\vendor\\yiisoft\\yii2\\base\\Application.php(386): yii\\web\\Application->handleRequest(Object(yii\\web\\Request))",
        "#13 E:\\wwwroot\\github-shuijingwan-yii2-app-advanced\\api\\web\\index.php(17): yii\\base\\Application->run()",
        "#14 {main}"
    ]
}

9、实现行为 \common\behaviors\UUIDBehavior.php,继承至 \wartron\yii2uuid\behaviors\UUIDBehavior,将包含数据的二进制字符串转换为十六进制值,且转化为大写

PHP
<?php
 
namespace common\behaviors;
 
use wartron\yii2uuid\helpers\Uuid;
 
class UUIDBehavior extends \wartron\yii2uuid\behaviors\UUIDBehavior
{
    public function createUUID()
    {
        return Uuid::uuid2str(parent::createUUID());
    }
 
}

10、附加行为 common\behaviors\UUIDBehavior,编辑 \common\logics\Page.php

PHP
use common\behaviors\UUIDBehavior;

11、在 Postman 中 POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/pages ,响应成功

JSON
{
    "code": 10000,
    "message": "创建页面成功",
    "data": {
        "id": 1,
        "uuid": "8F73EF349A0911E8AB1B54EE75D2EBC1",
        "slug": "title-20180807-1",
        "title": "title-20180807-1",
        "body": "body-20180807-1",
        "view": 0,
        "status": 1,
        "created_at": 1533622648,
        "updated_at": 1533622648
    }
}

12、查看生成的 SQL 语句,符合预期

SQL
INSERT INTO `page` (`slug`, `title`, `body`, `view`, `status`, `created_at`, `updated_at`, `uuid`) VALUES ('title-20180807-1', 'title-20180807-1', 'body-20180807-1', 0, 1, 1533622648, 1533622648, '8F73EF349A0911E8AB1B54EE75D2EBC1')

基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建远程过程调用应用(实现基于 Hprose 2.0 for PHP 的 RPC 服务端),在 rpc 目录中实现 页面 的相应 RPC 服务,且在 api 目录中的 API 实现 RPC 客户端 基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,日志消息的自动定时删除

PHP / Yii2 老项目维护与长期技术支持

如果你的 Yii2 项目已经上线,但遇到原开发离职、Bug 长期无人修复、接口不稳定、性能下降、代码难以接手等问题,可以联系我做一次远程技术排查。

适合以下情况:
✅ Yii2 老项目无人维护
✅ 后台管理系统 Bug 修复
✅ RESTful API 接口排查
✅ 小功能迭代与历史代码接手
✅ MySQL / Redis / Nginx 性能问题
✅ 长期远程兼职维护

可先从一次小问题开始:
✅ 线上报错排查
✅ 接口异常分析
✅ 慢查询与性能瓶颈定位
✅ 代码结构初步评估
✅ 部署环境与日志检查

如需咨询,请联系我,并注明:Yii2 维护咨询

联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理