Skip to content

Latest commit

 

History

History
239 lines (172 loc) · 4.62 KB

README.md

File metadata and controls

239 lines (172 loc) · 4.62 KB

STORM

STORM is Semantically Terse Object Relational Mapping for Go

WARNING!

This software is NOT ready for production use. It is currently an experimental proof of concept. The code is fragile, volatile and not reliable!

That said, please contribute your ideas and feedback!

Getting Started

  1. go get github.com/brendensoares/storm
  2. import "github.com/brendensoares/storm"
  3. Add storm.Model to a Go struct (e.g. type ModelName struct {...})
  4. Create a func NewModelName() *ModelName that returns storm.Factory(&ModelName{}).(*ModelName)
  5. import _ "github.com/brendensoares/storm/driver/mysql"
  6. modelName := NewModelName() and start using your new model!

Roadmap Features By Example

Map Database Schema To Models

STORM's object generator is important because it allows a developer to use the database schema as the authority for the design of the models. You can consider the generator as a tool to create a schema cache that would otherwise be done dynamically.

Data containers (e.g. SQL table) are mapped to Go Structs and container fields (e.g. SQL columns) are mapped to struct fields on each generated struct.

To generate a cache of your MySQL database's schema, simply run: storm generate mysql -host localhost -user dbuser -p dbpass -name dbname

From there, you can customize the object's definition as long as the meta data remains so that STORM can properly translate object data to the database.

Define Model Struct

A generated STORM model is composed of typical Go data types along with tag-based meta data. A model could look something like this:

type Post struct {
	storm.Model           `container:"posts" driver:"mysql"`

	Id int64              `alias:"post_id"`
	Title string          `length:"100" nullable:"true"`
	TransientTitle string `ignore:"yes"`
	Content string        `type:"text"`

	CreatedAt time.Time
	UpdateAt time.Time
	DeletedAt time.Time

	Categories *Category  `relation:"hasMany" through:"categories_posts/post_id"`
	Comments *Comment     `relation:"hasMany" key:"post_id"`
	Author *Author        `relation:"belongsTo" key:"author_id"`
	ParentPost *Post      `relation:"hasOne" key:"parent_post_id"`
}

func NewPost() *Post {
	return storm.Factory(&Post{}).(*Post)
}

Validate Fields

func (self *Post)Validate() bool {
	if self.len(Content) < 10000 {
		return true
	}
	return false
}

Create

post := NewPost()
post.Title = "Hello, World!"
saveError := post.Save()

Update

if post.IsLoaded() {
	post.Title = "Hello, Earth!"
	if saveError := post.Save(); saveError == nil {
    // Do stuff!
	}
}

Read

Single Object By Identifier

post1 := NewPost()
// For numeric `Id`
post1.Get(1)
post2 := NewPost()
// For string `Id`
post2.Get("2")

Single Matching Object By Criteria

post := NewPost()
post.Where("title", "like", "Hello%").And("title", "not like", "%earth%").Or("title").Get()
fmt.Println(post.Title)

Many Matching Objects By Criteria

post := NewPost()
matchedPosts := post.Where("title", "like", "hello%").Limit(5).Order("title", "asc").All()
for _, post := range matchedPosts {
	fmt.Println(post.Title)
}

Delete

Loaded Object

post.Delete()

Many Objects By Criteria

NewPost().Has("author", commentAuthor).Delete()

Relationships

Read

Lazy Load "hasMany"
if post.Load("Categories") {
	categoryTitle := post.Categories[0].Title
}
if post.Load("Comments") {
	commentAuthor := post.Comments[0].Author.DisplayName
}
"hasOne"
if post.ParentPost != nil {
	parentTitle := post.ParentPost.Title
}
"belongsTo"
if post.Author != nil {
	authorName := post.Author.DisplayName
}

Add

"hasMany"
category1 := NewCategory()
category1.Title = "Test Category 1"
category2 := NewCategory()
category2.Title = "Test Category 2"
categoryCountBefore := len(post.Categories)
addError := post.Add("Categories", category1, category2)
categoryCountAfter := len(post.Categories)
if addError != nil || categoryCountAfter - categoryCountBefore != 2 {
	// Something smells fishy.
}
"hasOne"
author := NewAuthor()
author.DisplayName = "John Smith"
post.Author = author
"belongsTo"
parentPost := NewPost()
parentPost.Get(20)
post.ParentPost = parentPost

Remove

"hasMany"
category := NewCategory()
category.Get(10)
if removeError := post.Remove("Categories", category); removeError != nil {
	// Error...The game is afoot!
}
"hasOne"
post.Author = nil
"belongsTo"
post.ParentPost = nil

Trigger Events

Save
Delete