跳轉到內容

Upgrading To v1.17 From v1.16

Exciting New Features 🎉

Enhancements 🚀

Breaking Changes 🛠

Upgrade Guide

As Golang v1.23 is no longer maintained, the Golang version of Goravel supports has been upgraded from 1.23 to 1.24.

goravel/example project from v1.16 to v1.17 PR can be used as an upgrade reference: goravel/example#93.

1. Update dependencies

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. Add new service providers

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

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

3. Modify http client configuration

Add new configuration in 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. Modify gRPC configuration

Need to modify accordingly: Modify gRPC configuration

5. If you want to send mail via template

Add new configuration in 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. If you developed a custom package and install it via package:install command

Need to modify accordingly: Modify package setup method

7. If you are using machinery queue driver

Need to modify accordingly: Remove machinery queue driver

8. If you created a custom log driver

Need to modify accordingly: Switch log driver

9. If you are using Http.Request.Bind function

Need to modify accordingly: Remove Http.Request.Bind function

10. If you are using validation.Make function or custom rules/filters

Need to modify accordingly: Validation supports pass context

11. If you are using global scopes in Orm

Need to modify accordingly: Add WithoutGlobalScopes function for Orm

Feature Introduction

Goravel Lite

We are excited to introduce Goravel Lite, a lightweight version of the Goravel framework, designed for developers who want to build applications with minimal dependencies and a smaller footprint. It provides the core features of Goravel while allowing developers to add only the components they need.

  • Only includes essential facades: Artisan, Config, reducing bloat and improving performance.
  • Other facades can be installed and uninstalled as needed via the package:install and package:uninstall commands.
  • goravel/goravel == goravel/goravel-lite + all facades. Any improvements for goravel/goravel should be submitted to goravel/goravel-lite, then a specify github action will sync the changes to goravel/goravel. And goravel/goravel is still the main repository for users to use Goravel framework.

Simplified code structure

The code structure has been simplified to improve maintainability and readability to consitent with Laravel. The old structure can still be used, it's a backward compatible change. It's a bit complicated to migrate from old structure to new structure, there is an example PR for reference if needed: goravel/example#91.

  1. Remove all kenel.go and default *_service_providers.go files. Move configurations in *_service_providers.go files to the bootstrap folder.
  2. Move all facades from framework intenal to the app/facades folder, users can customize or extend them as needed.
  3. Simplify the facades startup process, no need to run facades in main.go anymorem, please refer to Service Provider Runners for more details.
  4. Support customize directory structure via the WithPath() function, please refer to Customize Directory Structure for more details.

Process Facade

Goravel now provides an expressive and elegant API around Go's standard os/exec package, allowing you to invoke external commands from your application seamlessly.

View Document

Pluralizer package

Goravel now includes a pluralizer package that helps with converting words between singular and plural forms based on language rules. This is useful for applications that need to handle different languages and their grammatical rules.

View Document

Service Provider Runners

Service Providers can now implement the Runners interface to run code when the application starts. This is typically used to start or shutdown services defined in the Service Provider, such as Route, Schedule, Queue, etc. You no longer need to start or shutdown these services manually in main.go, as Goravel will handle it for you.

This feature is only available when using the new simplified code structure.

View Document

Log supports print json format

Goravel's logging system now supports outputting logs in JSON format, making it easier to integrate with log management systems and perform structured logging.

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 supports multiple clients

Goravel's HTTP client module now supports multiple clients, allowing you to define and use different HTTP clients with their own configurations for various purposes within your application.

View Document

Validation supports pass context

Goravel's validation module now supports passing context to validation functions, enabling more flexible and dynamic validation scenarios based on the context of the request or operation.

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

Custom rules and filters also support context parameter:

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
}

Add WithoutGlobalScopes function for Orm

A new WithoutGlobalScopes function has been added to the Orm package, allowing you to exclude all global scopes from a query. This is useful when you want to retrieve records without the constraints imposed by global scopes.

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

In order to support set global scope name, the GlobalScope interface of model has been modified:

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 can be installed and uninstalled as needed

Follow the release of Goravel Lite, now facades can be installed and uninstalled as needed via the package:install and package:uninstall commands. Notice: this feature is only available when you project is built with Goravel Lite or upgrades to the new simplified code structure.

View Document

Mail can be sent via template

Mail sending now supports templates, allowing you to send emails using predefined templates for better consistency and easier management. The template supports http/template and custom drivers.

View Document

The build command supports more flags

Add new --arch and --static flags to the build command, allowing you to specify the target architecture and whether to build a static binary.

Add and modify functions for DB and Orm

New functions:

  • Avg(column string, dest any) error: Calculate the average value of a column.
  • Min(column string, dest any) error: Find the minimum value of a column.
  • Max(column string, dest any) error: Find the maximum value of a column.
  • WhereAll(columns []string, args ...any) Query: Adds a "where all columns match" clause to the query.
  • WhereAny(columns []string, args ...any) Query: Adds a "where any column matches" clause to the query.
  • WhereNone(columns []string, args ...any) Query: Adds a "where none of the columns match" clause to the query.

Modified functions:

go
// Before
Sum(column string) (int64, error)

// After
Sum(column string, dest any) error

Add new functions for Schema

New functions have been added to the Schema package to enhance database schema management capabilities.

  • DateTimes: Create created_at and updated_at columns on the table.
  • ForeignID: Create an unsigned big integer column for foreign keys.
  • ForeignUlid: Create a ULID column for foreign keys.
  • ForeignUuid: Create a UUID column for foreign keys.

Add new commands

  • make:provider: Create a new service provider class.
  • make:view: Create a new view template file.

Add CSRF token middleware

A new middleware has been added to protect against Cross-Site Request Forgery (CSRF) attacks. This middleware can be applied to routes to ensure that requests are coming from authenticated sources.

View Document

Command supports configure Arguments

Commands now support configuring arguments, allowing you to define and handle command-line arguments more effectively within your custom commands.

View Document

make:migration supports create migration via model

The make:migration command has been enhanced to support creating migrations based on existing models. This allows for quicker migration creation by leveraging the structure defined in your models.

View Document

Optimize gRPC client connections

A client connection pool has been implemented for gRPC clients to optimize performance and resource utilization. This allows for reusing existing connections rather than creating new ones for each request, reducing latency and improving efficiency.

Remove machinery queue driver

The machinery queue driver has been removed totally given it's deprecated in v1.16. Please follow the guidelines in the v1.16 upgrade document to migrate to other queue drivers if you are still using it.

Modify gRPC configuration

The grpc configuration grpc.clients has been modified to grpc.servers, and the facades.Grpc().Client() function has been deprecated and will be removed in v1.18, please use the facades.Grpc().Connect() function instead.

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

Switch log driver

The log driver has been switched from logrus to log/slog for better performance and structured logging capabilities.

The custom log driver interface has been modified:

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) 
}

You can use log.HookToHandler function to adapt old custom log drivers:

go
type CustomLogger struct {}

func (logger *CustomLogger) Handle(channel string) (log.Handler, error) {
  hook := /* your old custom log driver logic */
  
  return log.HookToHandler(hook), nil
}

Optimize migrate:rollback command

Previously, the migrate:rollback command only rolls back one migration at a time, it's not consistent with Laravel's behavior. Now the command will roll back all migrations from the last batch by default. You can still use the --step option to specify the number of batches to roll back.

Remove Http.Request.Bind function

The Bind function on the Http.Request interface has been removed. You can now use the Bind method via response.

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) 

Modify package setup method

Previously, the package service provider can be installed to config/app.go via the setup.go file when running the package:install command. Now the service provider is supported to be installed to the bootstrap/providers.go file given Simplified Code Structure is launched. Hence, the package setup method has been modified to support both old and new code structures. In the future, the old code structures will be deprecated totally. For example:

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() {
  // Initialize setup to get paths, this should be called at the beginning.
  setup := packages.Setup(os.Args) 

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

  // In the new code structures, the GetModulePath function is removed, use setup.Paths().Module().Import() instead
  modulePath := packages.GetModulePath() 
  moduleImport := setup.Paths().Module().Import() 

  packages.Setup(os.Args).Install( 
  setup.Install( 
      // Add 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(
      // Remove 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()
}

If you want to support both old and new code structures in your package, please use the following setup method. In summary, you need to check whether the project is using the new code structure via the env.IsBootstrapSetup() function, then apply different modifications accordingly.

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() {
  // Initialize setup to get paths, this should be called at the beginning.
  setup := packages.Setup(os.Args)

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

  // Use the new setup.Paths().Module().Import() method to get the module import path
  moduleImport := setup.Paths().Module().Import()

  setup.Install(
    // Add postgres service provider to app.go if using the old code structure
    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))),

    // Add postgres service provider to providers.go if using the new code structure
    modify.When(func(_ map[string]any) bool {
      return env.IsBootstrapSetup()
    }, modify.RegisterProvider(moduleImport, postgresServiceProvider)),

  ).Uninstall(
    // Remove postgres service provider from app.go if using the old code structure
    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))),

    // Remove postgres service provider from providers.go if using the new code structure
    modify.When(func(_ map[string]any) bool {
      return env.IsBootstrapSetup()
    }, modify.UnregisterProvider(moduleImport, postgresServiceProvider)),
  ).Execute()
}

基於 MIT 许可發佈