跳转到内容

从 v1.16 升级到 v1.17

令人兴奋的新功能 🎉

功能增强 🚀

破坏性变化 🛠

升级指南

随着 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:ArtisanConfigProcess,减少体积并提高性能。
  • 其他 facades 可通过 package:installpackage:uninstall 命令按需安装和卸载。
  • goravel/goravel == goravel/goravel-lite + 所有 facades。 对 goravel/goravel 的任何改进都应提交到 goravel/goravel-lite,然后 GitHub Action 会将更改同步到 goravel/goravelgoravel/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:installpackage: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_atupdated_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() 函数检查项目是否使用新代码结构,然后应用不同的修改。

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.Providers()).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.Providers()).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()
}

基于 MIT 许可发布