Skip to content

Commit

Permalink
feat: [#280] Implement Create and DropIfExists methods of Schema (#646)
Browse files Browse the repository at this point in the history
* Implement Create

* Add DropIfExist

* chore: update mocks

* Add unit tests

* add test cases

* add test cases

* ifx

* optimize code

* optimize test cases

* optimize test cases

---------

Co-authored-by: hwbrzzl <[email protected]>
  • Loading branch information
hwbrzzl and hwbrzzl authored Sep 22, 2024
1 parent 7097e40 commit a8d9d6c
Show file tree
Hide file tree
Showing 28 changed files with 3,015 additions and 376 deletions.
26 changes: 26 additions & 0 deletions contracts/database/migration/blueprint.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
package migration

import (
"github.com/goravel/framework/contracts/database/orm"
)

type Blueprint interface {
// Build Execute the blueprint to build / modify the table.
Build(query orm.Query, grammar Grammar) error
// Create Indicate that the table needs to be created.
Create()
// DropIfExists Indicate that the table should be dropped if it exists.
DropIfExists()
// GetAddedColumns Get the added columns.
GetAddedColumns() []ColumnDefinition
// GetTableName Get the table name with prefix.
GetTableName() string
// HasCommand Determine if the blueprint has a specific command.
HasCommand(command string) bool
// ID Create a new auto-incrementing big integer (8-byte) column on the table.
ID(column ...string) ColumnDefinition
// Integer Create a new integer (4-byte) column on the table.
Integer(column string) ColumnDefinition
// SetTable Set the table that the blueprint operates on.
SetTable(name string)
// String Create a new string column on the table.
String(column string, length ...int) ColumnDefinition
// ToSql Get the raw SQL statements for the blueprint.
ToSql(query orm.Query, grammar Grammar) []string
}
22 changes: 22 additions & 0 deletions contracts/database/migration/column.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package migration

type ColumnDefinition interface {
// AutoIncrement set the column as auto increment
AutoIncrement() ColumnDefinition
// GetAutoIncrement returns the autoIncrement value
GetAutoIncrement() bool
// GetChange returns the change value
GetChange() bool
// GetDefault returns the default value
GetDefault() any
// GetLength returns the length value
GetLength() int
// GetName returns the name value
GetName() string
// GetNullable returns the nullable value
GetNullable() bool
// GetType returns the type value
GetType() string
// Unsigned set the column as unsigned
Unsigned() ColumnDefinition
}
File renamed without changes.
22 changes: 22 additions & 0 deletions contracts/database/migration/grammar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package migration

import (
"github.com/goravel/framework/contracts/database/orm"
)

type Grammar interface {
// CompileCreate Compile a create table command.
CompileCreate(blueprint Blueprint, query orm.Query) string
// CompileDropIfExists Compile a drop table (if exists) command.
CompileDropIfExists(blueprint Blueprint) string
// GetAttributeCommands Get the commands for the schema build.
GetAttributeCommands() []string
// GetModifiers Get the column modifiers.
GetModifiers() []func(Blueprint, ColumnDefinition) string
// TypeBigInteger Create the column definition for a big integer type.
TypeBigInteger(column ColumnDefinition) string
// TypeInteger Create the column definition for an integer type.
TypeInteger(column ColumnDefinition) string
// TypeString Create the column definition for a string type.
TypeString(column ColumnDefinition) string
}
9 changes: 9 additions & 0 deletions contracts/database/migration/migrator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package migration

type Migrator interface {
//Run()
//RunUp()
//RunDown()
//Rollback()
//Reset()
}
26 changes: 26 additions & 0 deletions contracts/database/migration/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package migration

type Repository interface {
//// CreateRepository Create the migration repository data store.
//CreateRepository()
//// Delete Remove a migration from the log.
//Delete(migration string)
//// DeleteRepository Delete the migration repository data store.
//DeleteRepository()
//// GetLast Get the last migration batch.
//GetLast()
//// GetMigrationBatches Get the completed migrations with their batch numbers.
//GetMigrationBatches()
//// GetMigrations Get the list of migrations.
//GetMigrations(steps int)
//// GetMigrationsByBatch Get the list of the migrations by batch.
//GetMigrationsByBatch(batch int)
//// GetNextBatchNumber Get the next migration batch number.
//GetNextBatchNumber()
//// GetRan Get the completed migrations.
//GetRan()
//// Log that a migration was run.
//Log(file, batch string)
//// RepositoryExists Determine if the migration repository exists.
//RepositoryExists()
}
19 changes: 17 additions & 2 deletions contracts/database/migration/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package migration

type Schema interface {
// Create a new table on the schema.
//Create(table string, callback func(table Blueprint))
Create(table string, callback func(table Blueprint))
// Connection Get the connection for the schema.
Connection(name string) Schema
// DropIfExists Drop a table from the schema if exists.
//DropIfExists(table string)
DropIfExists(table string)
// Register migrations.
Register([]Migration)
// Sql Execute a sql directly.
Expand All @@ -25,3 +25,18 @@ type Migration interface {
// Down Reverse the migrations.
Down()
}

type Command struct {
Algorithm string
Column ColumnDefinition
Columns []string
From string
Index string
On string
OnDelete string
OnUpdate string
Name string
To string
References []string
Value string
}
218 changes: 218 additions & 0 deletions database/migration/blueprint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package migration

import (
"fmt"

"github.com/goravel/framework/contracts/database/migration"
ormcontract "github.com/goravel/framework/contracts/database/orm"
"github.com/goravel/framework/support/convert"
)

const (
commandAdd = "add"
commandChange = "change"
commandComment = "comment"
commandCreate = "create"
commandDropIfExists = "dropIfExists"
defaultStringLength = 255
)

type Blueprint struct {
columns []*ColumnDefinition
commands []*migration.Command
prefix string
schema string
table string
}

func NewBlueprint(prefix, schema string) *Blueprint {
return &Blueprint{
prefix: prefix,
schema: schema,
}
}

func (r *Blueprint) BigIncrements(column string) migration.ColumnDefinition {
return r.UnsignedBigInteger(column).AutoIncrement()
}

func (r *Blueprint) BigInteger(column string) migration.ColumnDefinition {
columnImpl := &ColumnDefinition{
name: &column,
ttype: convert.Pointer("bigInteger"),
}

r.addColumn(columnImpl)

return columnImpl
}

func (r *Blueprint) Build(query ormcontract.Query, grammar migration.Grammar) error {
for _, sql := range r.ToSql(query, grammar) {
// TODO remove
fmt.Println("sql:", sql)
if _, err := query.Exec(sql); err != nil {
return err
}
}

return nil
}

func (r *Blueprint) Create() {
r.addCommand(&migration.Command{
Name: commandCreate,
})
}

func (r *Blueprint) DropIfExists() {
r.addCommand(&migration.Command{
Name: commandDropIfExists,
})
}

func (r *Blueprint) GetAddedColumns() []migration.ColumnDefinition {
var columns []migration.ColumnDefinition
for _, column := range r.columns {
if column.change == nil || !*column.change {
columns = append(columns, column)
}
}

return columns
}

func (r *Blueprint) GetChangedColumns() []migration.ColumnDefinition {
var columns []migration.ColumnDefinition
for _, column := range r.columns {
if column.change != nil && *column.change {
columns = append(columns, column)
}
}

return columns
}

func (r *Blueprint) GetTableName() string {
// TODO Add schema for Postgres
return r.prefix + r.table
}

func (r *Blueprint) HasCommand(command string) bool {
for _, c := range r.commands {
if c.Name == command {
return true
}
}

return false
}

func (r *Blueprint) ID(column ...string) migration.ColumnDefinition {
if len(column) > 0 {
return r.BigIncrements(column[0])
}

return r.BigIncrements("id")
}

func (r *Blueprint) Integer(column string) migration.ColumnDefinition {
columnImpl := &ColumnDefinition{
name: &column,
ttype: convert.Pointer("integer"),
}

r.addColumn(columnImpl)

return columnImpl
}

func (r *Blueprint) SetTable(name string) {
r.table = name
}

func (r *Blueprint) String(column string, length ...int) migration.ColumnDefinition {
defaultLength := defaultStringLength
if len(length) > 0 {
defaultLength = length[0]
}

columnImpl := &ColumnDefinition{
length: &defaultLength,
name: &column,
ttype: convert.Pointer("string"),
}
r.addColumn(columnImpl)

return columnImpl
}

func (r *Blueprint) ToSql(query ormcontract.Query, grammar migration.Grammar) []string {
r.addImpliedCommands(grammar)

var statements []string
for _, command := range r.commands {
switch command.Name {
case commandCreate:
statements = append(statements, grammar.CompileCreate(r, query))
case commandDropIfExists:
statements = append(statements, grammar.CompileDropIfExists(r))
}
}

return statements
}

func (r *Blueprint) UnsignedBigInteger(column string) migration.ColumnDefinition {
return r.BigInteger(column).Unsigned()
}

func (r *Blueprint) addAttributeCommands(grammar migration.Grammar) {
attributeCommands := grammar.GetAttributeCommands()
for _, column := range r.columns {
for _, command := range attributeCommands {
if command == "comment" && column.comment != nil {
r.addCommand(&migration.Command{
Column: column,
Name: commandComment,
})
}
}
}
}

func (r *Blueprint) addColumn(column *ColumnDefinition) {
r.columns = append(r.columns, column)
}

func (r *Blueprint) addCommand(command *migration.Command) {
r.commands = append(r.commands, command)
}

func (r *Blueprint) addImpliedCommands(grammar migration.Grammar) {
var commands []*migration.Command
if len(r.GetAddedColumns()) > 0 && !r.isCreate() {
commands = append(commands, &migration.Command{
Name: commandAdd,
})
}
if len(r.GetChangedColumns()) > 0 && !r.isCreate() {
commands = append(commands, &migration.Command{
Name: commandChange,
})
}
if len(commands) > 0 {
r.commands = append(commands, r.commands...)
}
r.addAttributeCommands(grammar)
}

func (r *Blueprint) isCreate() bool {
for _, command := range r.commands {
if command.Name == commandCreate {
return true
}
}

return false
}
Loading

0 comments on commit a8d9d6c

Please sign in to comment.