Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [#358] Add facades.DB, to provider an original sql flow #869

Merged
merged 5 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
BindingSchema = "goravel.schema"
BindingSeeder = "goravel.seeder"
BindingSession = "goravel.session"
BindingDB = "goravel.db"
BindingTesting = "goravel.testing"
BindingTranslation = "goravel.translation"
BindingValidation = "goravel.validation"
Expand Down
26 changes: 16 additions & 10 deletions contracts/database/config.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package database

type Config struct {
Connection string
Database string
Driver string
Host string
Password string
Port int
Prefix string
Schema string
Username string
Version string
Connection string
DSN string
Database string
Driver string
Host string
Password string
Port int
Prefix string
Schema string
Username string
Version string
PlaceholderFormat PlaceholderFormat
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different drivers have different placeholders.

}

type PlaceholderFormat interface {
ReplacePlaceholders(sql string) (string, error)
}
10 changes: 10 additions & 0 deletions contracts/database/db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package db

type DB interface {
Table(name string) Query
}

type Query interface {
Where(query any, args ...any) Query
Get(dest any) error
}
3 changes: 3 additions & 0 deletions contracts/database/driver/driver.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package driver

import (
"database/sql"

"gorm.io/gorm"
"gorm.io/gorm/clause"

Expand All @@ -11,6 +13,7 @@ import (

type Driver interface {
Config() database.Config
DB() (*sql.DB, error)
Docker() (docker.DatabaseDriver, error)
Gorm() (*gorm.DB, GormQuery, error)
Grammar() schema.Grammar
Expand Down
6 changes: 3 additions & 3 deletions database/console/seed_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (r *SeedCommand) Handle(ctx console.Context) error {
}

if err := r.seeder.Call(seeders); err != nil {
ctx.Error(errors.DBFailToRunSeeder.Args(err).Error())
ctx.Error(errors.DatabaseFailToRunSeeder.Args(err).Error())
return nil
}
ctx.Success("Database seeding completed successfully.")
Expand All @@ -83,7 +83,7 @@ func (r *SeedCommand) ConfirmToProceed(force bool) error {
return nil
}

return errors.DBForceIsRequiredInProduction
return errors.DatabaseForceIsRequiredInProduction
}

// GetSeeders returns a seeder instances
Expand All @@ -95,7 +95,7 @@ func (r *SeedCommand) GetSeeders(names []string) ([]contractsseeder.Seeder, erro
for _, name := range names {
seeder := r.seeder.GetSeeder(name)
if seeder == nil {
return nil, errors.DBSeederNotFound.Args(name)
return nil, errors.DatabaseSeederNotFound.Args(name)
}
seeders = append(seeders, seeder)
}
Expand Down
8 changes: 4 additions & 4 deletions database/console/seed_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (s *SeedCommandTestSuite) TestHandle() {
setup: func() {
s.mockContext.EXPECT().OptionBool("force").Return(false).Once()
s.mockConfig.EXPECT().GetString("app.env").Return("production").Once()
s.mockContext.EXPECT().Error(errors.DBForceIsRequiredInProduction.Error()).Once()
s.mockContext.EXPECT().Error(errors.DatabaseForceIsRequiredInProduction.Error()).Once()
},
},
{
Expand All @@ -64,7 +64,7 @@ func (s *SeedCommandTestSuite) TestHandle() {
s.mockConfig.EXPECT().GetString("app.env").Return("development").Once()
s.mockContext.EXPECT().OptionSlice("seeder").Return([]string{"mock"}).Once()
s.mockFacade.EXPECT().GetSeeder("mock").Return(nil).Once()
s.mockContext.EXPECT().Error(errors.DBSeederNotFound.Args("mock").Error()).Once()
s.mockContext.EXPECT().Error(errors.DatabaseSeederNotFound.Args("mock").Error()).Once()
},
},
{
Expand All @@ -86,7 +86,7 @@ func (s *SeedCommandTestSuite) TestHandle() {
s.mockFacade.EXPECT().GetSeeder("mock").Return(&MockSeeder{}).Once()
s.mockFacade.EXPECT().GetSeeder("mock2").Return(&MockSeeder2{}).Once()
s.mockFacade.EXPECT().Call([]seeder.Seeder{&MockSeeder{}, &MockSeeder2{}}).Return(assert.AnError).Once()
s.mockContext.EXPECT().Error(errors.DBFailToRunSeeder.Args(assert.AnError).Error()).Once()
s.mockContext.EXPECT().Error(errors.DatabaseFailToRunSeeder.Args(assert.AnError).Error()).Once()
},
},
}
Expand All @@ -107,7 +107,7 @@ func (s *SeedCommandTestSuite) TestConfirmToProceed() {

s.mockConfig.EXPECT().GetString("app.env").Return("production").Once()
err = s.seedCommand.ConfirmToProceed(false)
s.ErrorIs(err, errors.DBForceIsRequiredInProduction)
s.ErrorIs(err, errors.DatabaseForceIsRequiredInProduction)
}

func (s *SeedCommandTestSuite) TestGetSeeders() {
Expand Down
12 changes: 12 additions & 0 deletions database/db/conditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package db

type Conditions struct {
table string
where []Where
}

type Where struct {
query any
args []any
// or bool
}
45 changes: 45 additions & 0 deletions database/db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package db

import (
"fmt"

"github.com/jmoiron/sqlx"

"github.com/goravel/framework/contracts/config"
"github.com/goravel/framework/contracts/database"
"github.com/goravel/framework/contracts/database/db"
contractsdriver "github.com/goravel/framework/contracts/database/driver"
"github.com/goravel/framework/errors"
)

type DB struct {
config database.Config
instance *sqlx.DB
}

func NewDB(config database.Config, instance *sqlx.DB) db.DB {
return &DB{config: config, instance: instance}
}

func BuildDB(config config.Config, connection string) (db.DB, error) {
driverCallback, exist := config.Get(fmt.Sprintf("database.connections.%s.via", connection)).(func() (contractsdriver.Driver, error))
if !exist {
return nil, errors.DatabaseConfigNotFound
}

driver, err := driverCallback()
if err != nil {
return nil, err
}

instance, err := driver.DB()
if err != nil {
return nil, err
}

return NewDB(driver.Config(), sqlx.NewDb(instance, driver.Config().Driver)), nil
}

func (r *DB) Table(name string) db.Query {
return NewQuery(r.config, r.instance, name)
}
67 changes: 67 additions & 0 deletions database/db/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package db

import (
"fmt"

sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"

"github.com/goravel/framework/contracts/database"
"github.com/goravel/framework/contracts/database/db"
"github.com/goravel/framework/errors"
)

type Query struct {
conditions Conditions
config database.Config
instance *sqlx.DB
}

func NewQuery(config database.Config, instance *sqlx.DB, table string) *Query {
return &Query{
conditions: Conditions{
table: table,
},
config: config,
instance: instance,
}
}

func (r *Query) Where(query any, args ...any) db.Query {
r.conditions.where = append(r.conditions.where, Where{
query: query,
args: args,
})

return r
}

func (r *Query) Get(dest any) error {
sql, args, err := r.buildSelect()
// TODO: use logger instead of println
fmt.Println(sql, args, err)
if err != nil {
return err
}

return r.instance.Select(dest, sql, args...)
}

func (r *Query) buildSelect() (sql string, args []any, err error) {
if r.conditions.table == "" {
return "", nil, errors.DatabaseTableIsRequired
}

builder := sq.Select("*")
if r.config.PlaceholderFormat != nil {
builder = builder.PlaceholderFormat(r.config.PlaceholderFormat)
}

builder = builder.From(r.conditions.table)

for _, where := range r.conditions.where {
builder = builder.Where(where.query, where.args...)
}

return builder.ToSql()
}
2 changes: 1 addition & 1 deletion database/gorm/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func NewQuery(
func BuildQuery(ctx context.Context, config config.Config, connection string, log log.Log, modelToObserver []contractsorm.ModelToObserver) (*Query, contractsdatabase.Config, error) {
driverCallback, exist := config.Get(fmt.Sprintf("database.connections.%s.via", connection)).(func() (driver.Driver, error))
if !exist {
return nil, contractsdatabase.Config{}, errors.OrmDatabaseConfigNotFound
return nil, contractsdatabase.Config{}, errors.DatabaseConfigNotFound
}

driver, err := driverCallback()
Expand Down
18 changes: 17 additions & 1 deletion database/service_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/goravel/framework/contracts/foundation"
"github.com/goravel/framework/database/console"
consolemigration "github.com/goravel/framework/database/console/migration"
"github.com/goravel/framework/database/db"
"github.com/goravel/framework/database/migration"
databaseorm "github.com/goravel/framework/database/orm"
databaseschema "github.com/goravel/framework/database/schema"
Expand Down Expand Up @@ -48,6 +49,21 @@ func (r *ServiceProvider) Register(app foundation.Application) {

return orm, nil
})

app.Singleton(contracts.BindingDB, func(app foundation.Application) (any, error) {
config := app.MakeConfig()
if config == nil {
return nil, errors.ConfigFacadeNotSet.SetModule(errors.ModuleDB)
}

connection := config.GetString("database.default")
if connection == "" {
return nil, nil
}

return db.BuildDB(config, connection)
})

app.Singleton(contracts.BindingSchema, func(app foundation.Application) (any, error) {
config := app.MakeConfig()
if config == nil {
Expand All @@ -67,7 +83,7 @@ func (r *ServiceProvider) Register(app foundation.Application) {

driverCallback, exist := config.Get(fmt.Sprintf("database.connections.%s.via", orm.Name())).(func() (driver.Driver, error))
if !exist {
return nil, errors.OrmDatabaseConfigNotFound
return nil, errors.DatabaseConfigNotFound
}

driver, err := driverCallback()
Expand Down
9 changes: 5 additions & 4 deletions errors/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ var (
CryptMissingIVKey = New("decrypt payload error: missing iv key")
CryptMissingValueKey = New("decrypt payload error: missing value key")

DBForceIsRequiredInProduction = New("application in production use --force to run this command")
DBSeederNotFound = New("not found %s seeder")
DBFailToRunSeeder = New("fail to run seeder: %v")
DatabaseConfigNotFound = New("not found database configuration")
DatabaseTableIsRequired = New("table is required")
DatabaseForceIsRequiredInProduction = New("application in production use --force to run this command")
DatabaseSeederNotFound = New("not found %s seeder")
DatabaseFailToRunSeeder = New("fail to run seeder: %v")

DockerUnknownContainerType = New("unknown container type")
DockerInsufficientDatabaseContainers = New("the number of database container is not enough, expect: %d, got: %d")
Expand Down Expand Up @@ -83,7 +85,6 @@ var (
MigrationResetFailed = New("migration reset failed: %v")
MigrationRollbackFailed = New("migration rollback failed: %v")

OrmDatabaseConfigNotFound = New("not found database configuration")
OrmDriverNotSupported = New("invalid driver: %s, only support mysql, postgres, sqlite and sqlserver")
OrmFailedToGenerateDNS = New("failed to generate DSN, please check the database configuration")
OrmFactoryMissingAttributes = New("failed to get raw attributes")
Expand Down
1 change: 1 addition & 0 deletions errors/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var (
ModuleCache = "cache"
ModuleConsole = "console"
ModuleCrypt = "crypt"
ModuleDB = "db"
ModuleEvent = "event"
ModuleFacade = "facade"
ModuleFilesystem = "filesystem"
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.7
toolchain go1.23.6

require (
github.com/Masterminds/squirrel v1.5.4
github.com/brianvoe/gofakeit/v7 v7.2.1
github.com/charmbracelet/huh v0.6.0
github.com/charmbracelet/huh/spinner v0.0.0-20250210232304-9ef0c496babc
Expand All @@ -18,6 +19,7 @@ require (
github.com/google/uuid v1.6.0
github.com/gookit/validate v1.5.4
github.com/goravel/file-rotatelogs/v2 v2.4.2
github.com/jmoiron/sqlx v1.4.0
github.com/pterm/pterm v0.12.80
github.com/redis/go-redis/v9 v9.7.0
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
Expand Down Expand Up @@ -61,6 +63,8 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.9 // indirect
Expand Down
Loading
Loading