跳转到内容

从 v1.16 升级到 v1.17 ​

令人兴奋的新功能 🎉 ​

功能增强 🚀 ​

破坏性变化 🛠 ​

v1.17.1 ​

v1.17.2 ​

v1.17.3 ​

升级指南 ​

随着 Golang v1.23 不再被维护,Goravel 支持的 Golang 版本已从 1.23 升级到 1.24。

goravel/example 项目从 v1.16 升级到 v1.17 的 PR 可作为升级参考:goravel/example#93。

1. 更新依赖 ​

shell
go get github.com/goravel/framework@latest

// If using gin
go get github.com/goravel/gin@latest

// If using fiber
go get github.com/goravel/fiber@latest

// If using redis
go get github.com/goravel/redis@latest

// If using S3
go get github.com/goravel/s3@latest

// If using Oss
go get github.com/goravel/oss@latest

// If using Cos
go get github.com/goravel/cos@latest

// If using Minio
go get github.com/goravel/minio@latest

go mod tidy

// If using installer
goravel upgrade

2. 添加新的服务提供者 ​

go
// config/app.go
import (
  ...
  "github.com/goravel/framework/process"
  "github.com/goravel/framework/view"
)

"providers": []foundation.ServiceProvider{
  ...
  &process.ServiceProvider{}, 
  &view.ServiceProvider{}, 
},

3. 修改 http 客户端配置 ​

在 config/http.go 中添加新配置:

go
"client": map[string]any{ 
  "base_url":                config.GetString("HTTP_CLIENT_BASE_URL"), 
  "timeout":                 config.GetDuration("HTTP_CLIENT_TIMEOUT"), 
  "max_idle_conns":          config.GetInt("HTTP_CLIENT_MAX_IDLE_CONNS"), 
  "max_idle_conns_per_host": config.GetInt("HTTP_CLIENT_MAX_IDLE_CONNS_PER_HOST"), 
  "max_conns_per_host":      config.GetInt("HTTP_CLIENT_MAX_CONN_PER_HOST"), 
  "idle_conn_timeout":       config.GetDuration("HTTP_CLIENT_IDLE_CONN_TIMEOUT"), 
}, 
"default_client": config.Env("HTTP_CLIENT_DEFAULT", "default"), 
"clients": map[string]any{ 
  "default": map[string]any{ 
    "base_url": config.Env("HTTP_CLIENT_BASE_URL", ""), 
    "timeout": config.Env("HTTP_CLIENT_TIMEOUT", "30s"), 
    "max_idle_conns": config.Env("HTTP_CLIENT_MAX_IDLE_CONNS", 100), 
    "max_idle_conns_per_host": config.Env("HTTP_CLIENT_MAX_IDLE_CONNS_PER_HOST", 2), 
    "max_conns_per_host": config.Env("HTTP_CLIENT_MAX_CONN_PER_HOST", 0), 
    "idle_conn_timeout": config.Env("HTTP_CLIENT_IDLE_CONN_TIMEOUT", "90s"), 
  }, 
}, 

4. 修改 gRPC 配置 ​

需相应修改:修改 gRPC 配置

5. 如果想通过模板发送邮件 ​

在 config/mail.go 中添加新配置:

go
// Template Configuration
//
// This controls template rendering for email views. Template engines are cached
// globally and support both built-in drivers and custom implementations via factories.
//
// Available Drivers: "html", "custom"
"template": map[string]any{
  "default": config.Env("MAIL_TEMPLATE_ENGINE", "html"),
  "engines": map[string]any{
    "html": map[string]any{
      "driver": "html",
      "path":   config.Env("MAIL_VIEWS_PATH", "resources/views/mail"),
    },
  },
},

6. 如果你开发了自定义包并通过 package:install 命令安装它 ​

需相应修改:修改包安装方法

7. 如果正在使用 machinery 队列驱动 ​

需相应修改:移除 machinery 队列驱动

8. 如果创建了自定义日志驱动 ​

需相应修改:切换日志驱动

9. 如果正在使用 Http.Request.Bind 函数 ​

需相应修改:移除 Http.Request.Bind 函数

10. 如果正在使用 validation.Make 函数或自定义规则/过滤器 ​

需相应修改:验证支持传递上下文

11. 如果 Orm 模型配置了全局作用域 ​

需相应修改:Orm 添加 WithoutGlobalScopes 函数

功能介绍 ​

轻量版 Goravel ​

Goravel Lite 正式上线,这是 Goravel 框架的轻量级版本,专为希望以最小依赖和更小占用空间构建应用程序的开发者设计。 它提供了 Goravel 的核心功能,同时允许开发者按需添加其他模块。

  • 仅包含必要的 facades:Artisan、Config、Process,减少体积并提高性能。
  • 其他 facades 可通过 package:install 和 package:uninstall 命令按需安装和卸载。
  • goravel/goravel == goravel/goravel-lite + 所有 facades。 对 goravel/goravel 的任何改进都应提交到 goravel/goravel-lite,然后 GitHub Action 会将更改同步到 goravel/goravel。 goravel/goravel 仍然是 Goravel 框架的主要仓库。

简化代码结构 ​

简化代码结构,以提高可维护性和可读性,并与 Laravel 保持一致。 旧结构仍可使用,这是一个向后兼容的更改。 从旧结构迁移到新结构有点复杂,如果想要迁移,可以参考示例 PR:goravel/example#91。

  1. 删除所有 kenel.go 和默认的 *_service_providers.go 文件。 将 *_service_providers.go 文件中的配置移动到 bootstrap 文件夹。
  2. 将所有 facades 从框架内部移动到 app/facades 文件夹,用户可以根据需要自定义或扩展它们。
  3. 简化外观启动过程,不再需要在 main.go 中运行 facades,更多详情请参阅 服务提供者运行器。
  4. 支持通过 WithPath() 函数自定义目录结构,更多详情请参阅 自定义目录结构。

进程 Facade ​

Goravel 现在围绕 Go 标准 os/exec 包提供了一个富有表现力且优雅的 API,允许你从应用程序中无缝调用外部命令。

详情请 查看文档

复数包 ​

Goravel 现在包含一个复数化包,可根据语言规则帮助在单词的单数和复数形式之间进行转换。 这对于需要处理不同语言及其语法规则的应用程序非常有用。

详情请 查看文档

服务提供者运行器 ​

服务提供者现在可以实现 Runners 接口,在应用程序启动时运行代码。 这通常用于启动或关闭服务提供者中定义的服务,例如路由、调度、队列等。 你不再需要在 main.go 中手动启动或关闭这些服务,因为 Goravel 会为你处理。

此功能仅在使用 新的简化代码结构 时可用。

查看文档

日志支持打印 JSON 格式 ​

Goravel 的日志系统现在支持以 JSON 格式输出日志,使其更容易与日志管理系统集成并执行结构化日志记录。

go
"channels": map[string]any{
  "daily": map[string]any{
    "driver":    "daily",
    "path":      "storage/logs/goravel.log",
    "level":     config.Env("LOG_LEVEL", "debug"),
    "days":      7,
    "print":     true,
    // the value can be "text" or "json"
    "formatter": "json", 
  },
},

HTTP 支持多个客户端 ​

Goravel 的 HTTP 客户端模块现在支持多个客户端,允许你定义和使用具有各自配置的不同 HTTP 客户端,以满足应用程序内的各种用途。

查看文档

验证支持传递上下文 ​

Goravel 的验证模块现在支持将上下文传递给验证函数,从而能够基于请求或操作的上下文实现更灵活和动态的验证场景。

go
validator, err := facades.Validation().Make(input, rules) 
validator, err := facades.Validation().Make(ctx, input, rules) 

自定义规则和过滤器也支持上下文参数:

go
type Rule interface {
  Signature() string
  Passes(data Data, val any, options ...any) bool
  Message() string
  Passes(ctx context.Context, data Data, val any, options ...any) bool
  Message(ctx context.Context) string
}

type Filter interface {
  Signature() string
  Handle() any
  Handle(ctx context.Context) any
}

Orm 添加 WithoutGlobalScopes 函数 ​

Orm 新增了一个 WithoutGlobalScopes 函数,允许你从查询中排除所有全局作用域。 当你想检索不受全局作用域约束的记录时这非常有用。

go
var user []User
err := facades.Orm().Model(&User{}).WithoutGlobalScopes().Get(&user)
err := facades.Orm().Model(&User{}).WithoutGlobalScopes("name").Get(&user)

为了支持设置全局作用域名称,模型的 GlobalScope 接口已被修改:

go
import "github.com/goravel/framework/contracts/orm"

type User struct {
  orm.Model
  Name string
}

func (r *User) GlobalScopes() []func(orm.Query) orm.Query { 
func (r *User) GlobalScopes() map[string]func(orm.Query) orm.Query { 
  return map[string]func(orm.Query) orm.Query{
    "name": func(query orm.Query) orm.Query {
      return query.Where("name", "goravel")
    },
  }
}

facades 可按需安装和卸载 ​

随着 轻量化 Goravel 的发布,现在可以通过 package:install 和 package:uninstall 命令根据需要安装和卸载外观。 注意:此功能仅在你的项目使用轻量化 Goravel 构建或升级到 新的简化代码结构 时可用。

查看文档

邮件可通过模板发送 ​

邮件发送支持模板,允许你使用预定义的模板发送电子邮件,以获得更好的一致性和更轻松的管理。 模板支持 http/template 和自定义驱动。

详情请 查看文档

build 命令支持更多选项 ​

向 build 命令添加新的 --arch 和 --static 选项,允许你指定目标架构以及是否构建静态二进制文件。

DB 和 Orm 添加和修改函数 ​

新函数:

  • Avg(column string, dest any) error:计算列的平均值。
  • Min(column string, dest any) error:查找列的最小值。
  • Max(column string, dest any) error:查找列的最大值。
  • WhereAll(columns []string, args ...any) Query:向查询添加“所有列匹配”子句。
  • WhereAny(columns []string, args ...any) Query:向查询添加“任意列匹配”子句。
  • WhereNone(columns []string, args ...any) Query:向查询添加“没有列匹配”子句。

修改的函数:

go
// 之前
Sum(column string) (int64, error)

// 现在
Sum(column string, dest any) error

Schema 添加新函数 ​

向 Schema 添加新函数,以增强数据库模式管理能力。

  • DateTimes:在表上创建 created_at 和 updated_at 列。
  • ForeignID:为外键创建一个 unsigned big integer 字段。
  • ForeignUlid:为外键创建一个 ULID 列。
  • ForeignUuid:为外键创建一个 UUID 列。

添加新命令 ​

  • make:provider:创建一个新的服务提供者类。
  • make:view:创建一个新的视图模板文件。

添加 CSRF 令牌中间件 ​

添加一个新的中间件,以防止跨站请求伪造(CSRF)攻击。 此中间件可应用于路由,以确保请求来自经过身份验证的来源。

查看文档

命令支持配置参数 ​

命令现在支持配置参数,允许你在自定义命令中更有效地定义和处理命令行参数。

详情请 查看文档

make:migration 支持通过模型创建迁移 ​

make:migration 命令已增强,支持基于现有模型创建迁移。 这允许通过利用模型中定义的结构来更快地创建迁移。

查看文档

优化 gRPC 客户端连接 ​

为 gRPC 客户端实现客户端连接池,以优化性能和资源利用率。 这允许复用现有连接,而不是为每个请求创建新连接,从而减少延迟并提高效率。

移除 machinery 队列驱动 ​

鉴于在 v1.16 中已弃用,machinery 队列驱动现已被完全移除。 如果你仍在使用它,请遵循 v1.16 升级文档 中的指南迁移到其他队列驱动。

修改 gRPC 配置 ​

gRPC 配置 grpc.clients 已修改为 grpc.servers,并且 facades.Grpc().Client() 函数已被弃用,将在 v1.18 中移除,请改用 facades.Grpc().Connect() 函数。

go
// config/grpc.go
"clients": map[string]any{ 
"servers": map[string]any{ 
go
facades.Grpc().Client(ctx, "name") 
facades.Grpc().Connect("name") 

切换日志驱动 ​

日志驱动已从 logrus 切换到 log/slog,以获得更好的性能和结构化日志记录能力。

自定义日志驱动接口已修改:

go
// framework/contracts/log/Logger
package log

type Logger interface {
  // Handle pass channel config path here
  Handle(channel string) (Hook, error) 
  Handle(channel string) (Handler, error) 
}

你可以使用 log.HookToHandler 函数来适配旧的自定义日志驱动:

go
type CustomLogger struct {}

func (logger *CustomLogger) Handle(channel string) (log.Handler, error) {
  hook := /* 您的旧自定义日志驱动逻辑 */
  
  return log.HookToHandler(hook), nil
}

优化 migrate:rollback 命令 ​

之前,migrate:rollback 命令一次只回滚一个迁移,这与 Laravel 的行为不一致。 现在该命令默认将回滚最后一批的所有迁移。 你仍然可以使用 --step 选项来指定要回滚的批次数。

移除 Http.Request.Bind 函数 ​

Http.Request 接口上的 Bind 函数已被移除。 你现在可以通过 response 使用 Bind 方法。

go
var user User
response, err := facades.Http().Bind(&user).Get("https://github.com") 
response, err := facades.Http().Get("https://github.com") 
err = response.Bind(&user) 

修改包安装方法 ​

以前,运行 package:install 命令时,包的 service provder 通过 setup.go 文件安装到 config/app.go 中。 现在,由于简化代码结构,service provder 将被安装到 bootstrap/providers.go 文件中。

如果只想支持新代码结构:

go
// setup/setup.go
package main

import (
  "os"

  "github.com/goravel/framework/packages"
  "github.com/goravel/framework/packages/match"
  "github.com/goravel/framework/packages/modify"
  "github.com/goravel/framework/support/path"
)

func main() {
  // 初始化 setup 以获取路径,始终应在开头调用。
  setup := packages.Setup(os.Args) 

  appConfigPath := path.Config("app.go")
  postgresServiceProvider := "&postgres.ServiceProvider{}"

  // 在新代码结构中,GetModulePath 函数已被移除,请改用 setup.Paths().Module().Import()
  modulePath := packages.GetModulePath() 
  moduleImport := setup.Paths().Module().Import() 

  packages.Setup(os.Args).Install( 
  setup.Install( 
      // 注册 postgres service provider
      modify.GoFile(appConfigPath). 
        Find(match.Imports()).Modify(modify.AddImport(modulePath)). 
        Find(match.Providers()).Modify(modify.Register(postgresServiceProvider)), 
      modify.RegisterProvider(moduleImport, postgresServiceProvider), 
      ...
    ).
    Uninstall(
      // 移除 postgres service provider
      modify.GoFile(appConfigPath). 
        Find(match.Providers()).Modify(modify.Unregister(postgresServiceProvider)). 
        Find(match.Imports()).Modify(modify.RemoveImport(modulePath)), 
      modify.UnregisterProvider(moduleImport, postgresServiceProvider), 
      ...
    ).
    Execute()
}

如果你想在包中同时支持新旧代码结构,请使用以下方法设置。 简单来说,你需要通过 env.IsBootstrapSetup() 函数检查项目是否使用新代码结构,然后应用不同的修改。 注意:适配旧代码结构时,match.Providers() 应替换为 match.ProvidersInConfig()。

go
package main

import (
  "os"

  "github.com/goravel/framework/packages"
  "github.com/goravel/framework/packages/match"
  "github.com/goravel/framework/packages/modify"
  "github.com/goravel/framework/support/env"
  "github.com/goravel/framework/support/path"
)

func main() {
  // 初始化 setup 以获取路径,始终应在开头调用。
  setup := packages.Setup(os.Args)

  appConfigPath := path.Config("app.go")
  postgresServiceProvider := "&postgres.ServiceProvider{}"

  // 使用新的 setup.Paths().Module().Import() 方法获取模块导入路径
  moduleImport := setup.Paths().Module().Import()

  setup.Install(
    // 如果使用旧代码结构,将 postgres service provider 添加到 app.go
    modify.When(func(_ map[string]any) bool {
      return !env.IsBootstrapSetup()
    }, modify.GoFile(appConfigPath).
      Find(match.Imports()).Modify(modify.AddImport(moduleImport)).
      Find(match.ProvidersInConfig()).Modify(modify.Register(postgresServiceProvider))),

    // 如果使用新代码结构,将 postgres service provider 添加到 providers.go
    modify.When(func(_ map[string]any) bool {
      return env.IsBootstrapSetup()
    }, modify.RegisterProvider(moduleImport, postgresServiceProvider)),

  ).Uninstall(
    // 如果使用旧代码结构,从 app.go 中移除 postgres service provider
    modify.When(func(_ map[string]any) bool {
      return !env.IsBootstrapSetup()
    }, modify.GoFile(appConfigPath).
      Find(match.ProvidersInConfig()).Modify(modify.Unregister(postgresServiceProvider)).
      Find(match.Imports()).Modify(modify.RemoveImport(moduleImport))),

    // 如果使用新代码结构,从 providers.go 中移除 postgres service provider
    modify.When(func(_ map[string]any) bool {
      return env.IsBootstrapSetup()
    }, modify.UnregisterProvider(moduleImport, postgresServiceProvider)),
  ).Execute()
}

修复带静态标志的构建命令失败问题 ​

goravel/framework: v1.17.1

由于 Go 版本升级至 1.24,带有 --static 标志的 ./artisan build -s 命令执行失败。

修复部分 make 命令未适配旧代码结构的问题 ​

goravel/framework: v1.17.1

make:rule、make:filter、make:migration、make:package 等命令在生成文件时未适配旧代码结构。 现在这些命令可以通过 env.IsBootstrapSetup() 函数检测项目是否使用新的简化代码结构,并相应地生成文件到正确的目录。

优化在旧版本项目中安装包时的设置 ​

goravel/framework: v1.17.1

如果你构建了自定义包,setup.go 文件中的 Providers 函数已修改以适配新代码结构。 因此,如果想支持旧代码结构,应将其替换为 ProvidersInConfig 函数。 更多详情请参阅修改包安装方法。

修复队列并发数大于 1 时的延迟任务问题 ​

goravel/framework: v1.17.1

goravel/redis: v1.17.1

当队列工作器并发数大于 1 时,延迟任务会被执行多次。 这是因为当延迟任务变为可用时,多个 worker 可能会获取到同一个延迟任务。 现在使用分布式锁来确保一次只有一个 worker 可以执行延迟任务。

修复 migrate:rollback 命令 ​

goravel/framework: v1.17.2

之前, migrate:rollback 命令默认将 batch 设置为 1, 在回滚时会引起问题。 现在命令将 batch 默认设置为最后一个迁移。

修复 Schema 指定驱动无效的问题 ​

goravel/framework: v1.17.3

之前,facades.Schema().Connection(name) 使用了指定的连接,但仍然会使用默认连接的驱动来编译 schema 查询。 现在,HasTable 和 db:wipe --database 等操作将使用所选择连接的驱动。

Issue: #942

修复当使用 WithMiddleware() 时 ServiceProvider.Boot() 中注册的路由被静默丢弃的问题 ​

goravel/framework: v1.17.3

以前,当通过 bootstrap/app.go 中的 WithMiddleware() 注册全局中间件时,configureMiddleware() 在 Boot() 之后运行,这会重建路由引擎并丢弃服务提供者在 Boot() 期间注册的任何路由。 现在,configureMiddleware() 被移到 Register() 和 Boot() 之间运行,确保提供者注册的路由在引擎重建后仍然存在。

Issue: #975

修复 Request InputArray 将单个值按空格分割的问题 ​

goravel/gin: v1.17.1

goravel/fiber: v1.17.1

以前,ctx.Request().InputArray 可能会将包含空格的单个表单或url编码的数组值分割成多个条目。 现在,单个提交的值(如 123 456 789)保持为一个数组元素,而多值数组则保持现有行为。

Issue: #961

修复模块路径导入错误的问题 ​

goravel/framework: v1.17.3

以前,像 make:migration、make:test 和包只使用多段 go.mod 模块路径的最后一段来生成导入,例如 github.com/example/project/app/facades 只会导入 facades。 现在生成的导入使用完整的模块路径。

Issue: #973

修复 LOG_LEVEL 无效的问题 ​

goravel/framework: v1.17.3

以前,设置 LOG_LEVEL=error 没有效果,Info 和 Debug 消息仍然被写入,因为 Writer.log() 绕过了级别检查。 现在,在写入任何日志条目之前,会正确检查配置的 LOG_LEVEL。

Issue: #977

基于 MIT 许可发布