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: [#280] Implement Create and DropIfExists methods of Schema #646

Merged
merged 12 commits into from
Sep 22, 2024
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
}
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
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Action: Define various fields, eg: ID, String, Date, they will be used in a migration file.

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
}
hwbrzzl marked this conversation as resolved.
Show resolved Hide resolved

func (r *Blueprint) Build(query ormcontract.Query, grammar migration.Grammar) error {
for _, sql := range r.ToSql(query, grammar) {
// TODO remove
fmt.Println("sql:", sql)
hwbrzzl marked this conversation as resolved.
Show resolved Hide resolved
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))
}
}
hwbrzzl marked this conversation as resolved.
Show resolved Hide resolved

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
Loading