Upgrading To v1.17 From v1.16
Exciting New Features 🎉
Enhancements 🚀
- facades can be installed and uninstalled as needed
- Mail can be sent via template
- The build command supports more flags
- Add and modify functions for DB and Orm
- Add new functions for Schema
- Add new commands
- Add CSRF token middleware
- Command supports configure Arguments
- make:migration supports create migration via model
- Optimize gRPC client connections
- Log supports print json format
- Http supports multiple clients
- Validation supports pass context
- Add WithoutGlobalScopes function for Orm
Breaking Changes 🛠
- Remove machinery queue driver
- Modify gRPC configuration
- Switch log driver
- Optimize migrate:rollback command
- Remove Http.Request.Bind function
- Modify package setup method
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
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 upgrade2. Add new service providers
// 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:
"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:
// 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:installandpackage:uninstallcommands. goravel/goravel==goravel/goravel-lite+ all facades. Any improvements forgoravel/goravelshould be submitted togoravel/goravel-lite, then a specify github action will sync the changes togoravel/goravel. Andgoravel/goravelis 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.
- Remove all
kenel.goand default*_service_providers.gofiles. Move configurations in*_service_providers.gofiles to thebootstrapfolder. - Move all facades from framework intenal to the
app/facadesfolder, users can customize or extend them as needed. - Simplify the facades startup process, no need to run facades in
main.goanymorem, please refer to Service Provider Runners for more details. - 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.
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.
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.
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.
"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.
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.
validator, err := facades.Validation().Make(input, rules)
validator, err := facades.Validation().Make(ctx, input, rules) Custom rules and filters also support context parameter:
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.
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:
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.
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.
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:
// Before
Sum(column string) (int64, error)
// After
Sum(column string, dest any) errorAdd new functions for Schema
New functions have been added to the Schema package to enhance database schema management capabilities.
DateTimes: Createcreated_atandupdated_atcolumns 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.
Command supports configure Arguments
Commands now support configuring arguments, allowing you to define and handle command-line arguments more effectively within your custom commands.
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.
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.
// config/grpc.go
"clients": map[string]any{
"servers": map[string]any{ 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:
// 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:
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.
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:
// 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.
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()
}