ボクダイモリ

Life is like a Game

Gin + gormで簡単なWebアプリを作ってみた

自分でサービスを立ち上げたかったので、Go言語で簡単なWebアプリを作ってみた。

https://github.com/anraku/gin-sample

Go言語を選んだのは今一番気になっている言語だから

WebフレームワークはどれがいいのかよくわからなかったのでとりあえずGinを選択。

理由は速度が早そうだから(Martiniという同じWebフレームワークの40倍早いらしい)

 

あとはデータベースも使いたかったのでORMのライブラリもxormとgormを使ってみた。

最初はxormがいいような気がしたが、ドキュメントがあまり整備されていない印象。

また、実際にデータベースアクセスの処理が間違っていた時どのようなSQL文が発行されたのかが

分からずトラブルシューティングが難しいと感じた。

それにgithubの更新が少ないのも気になる。。

 

最終的にはgormの方を選択。ドキュメントもわかりやすく、ちょっとしたCRUDは1行程度でシンプルに書ける。

以下gormのドキュメント

http://jinzhu.me/gorm/

 

全体的に黒いデザインなのもいいね!

Webアプリの概要

とりあえずGinとgormを使って簡単にCRUDができるアプリを作ってみた。

アプリはGOPATHの配下に配置する前提です。

フロントの方はドットインストールの何かの入門で作ったコードを使いました。

また、Ginを使う際は以下のコマンドを実行してGinをインストールする必要がある。

go get github.com/gin-gonic/gin

main.go

func main() {
    router := gin.Default()
    router.LoadHTMLGlob("templates/*") // 事前にテンプレートをロード

    // トップページ
    router.GET("/index", func(c *gin.Context) {
        // データを処理する
        ctrl := controllers.NewPost()
        result := ctrl.GetAllPost()
        // テンプレートを使って、値を置き換えてHTMLレスポンスを応答
        c.HTML(http.StatusOK, "index.tmpl", gin.H{
            "posts": result,
        })
    })
}

最初にrouterを定義してHTMLテンプレートのロードを行う

router.GET("/index", func(c *gin.Context)で/indexへのGETアクセスを処理するための メソッドを作成している。

ctrl := controllers.NewPost()はModelのメソッドにアクセスするためのstructを返し、

result := ctrl.GetAllPost()でデータベースにアクセスし、値を取得している。

 

最後にindex.tmplをレスポンスとして返し、"posts"という名前のパラメータに取得したresultをセットする。

Controller

Controllerは基本データベースから値を取得し、必要であればデータの加工を行ってから 呼び出し元に返すというような実装が良いでしょう。 今回作ったのは簡単なCRUDしかないので、データベースから受け取った値をそのまま呼び出し元に返しています。

以下、Controllerの全文です。

package controllers

import (
    "../models"
)

// Post Model
type Post struct {
}

// Post Modelを返す
func NewPost() Post {
    return Post{}
}

// idに合致する記事の情報を返す
func (c Post) GetId(n int) interface{} {
    repo := models.NewPostRepository()
    post := repo.GetByPostID(n)
    return post
}

// 全記事の情報
func (c Post) GetAllPost() interface{} {
    repo := models.NewPostRepository()
    posts := repo.GetAllPost()
    return posts
}

// 全記事の件名
func (c Post) GetAllHeader() interface{} {
    repo := models.NewPostRepository()
    posts := repo.GetAllHeader()
    return posts
}

// 記事を投稿
func (c Post) CreatePost(header string, body string, author string) bool {
    repo := models.NewPostRepository()
    ok := repo.CreatePost(header, body, author)
    return ok
}

// 記事を更新
func (c Post) UpdatePost(id int, header string, body string) interface{} {
    repo := models.NewPostRepository()
    ok := repo.UpdatePost(id, header, body)
    return ok
}

// 記事を削除
func (c Post) DeletePost(n int) interface{} {
    repo := models.NewPostRepository()
    result := repo.DeletePost(n)
    return result
}

Model

gormを使う際は以下のライブラリをgo getして、importする必要がある

"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"

gormを利用するために以下のようにgormのオブジェクトとデータベースを初期化

var db *gorm.DB

func init() {
  conn, err := gorm.Open("mysql", 
    "root:password@/gin_sample?charset=utf8&parseTime=True&loc=Local")
  if err != nil {
    panic(err)
  }
  db = conn
  //DB Migrate
  if !db.HasTable("posts") {
    db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&Post{})
  }
}

gorm.DBのオブジェクトをグローバル変数で保持するようにして、実行時にDBへ接続するようにする。 init()はアプリの実行時に呼ばれる。

gormでは

db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&Post{})

のようにDBのマイグレーションを行える。 マイグレーションのコードも書いておけば開発時にテーブル定義をいちいち自分で作る必要がない。 Insertする処理も一緒に書いておくともっと楽ができるだろう。

マイグレーションは定義したstructをAutoMigrateの引数に渡すことで、それに対応するテーブルの作成を行う。 今回定義したstructは以下の通り

// 投稿情報の構造体
type Post struct {
    ID     int            `gorm:"AUTO_INCREMENT;primary_key"`
    Header string         `gorm:"not null;size:255"`
    Body   string         `gorm:"not null;size:13000"`
    Author string         `gorm:"not null;size:30"`
    CreatedAt time.Time   `gorm:"not null"`
}

上記のstructをマイグレーションすることで「posts」というテーブルが作成される。 最後にCRUDの処理をざっと記載する。

// idに合致する記事を取得
func (m PostRepository) GetByPostID(id int) *Post {
    var post Post
    db.Where(Post{ID: id}).Find(&post)
    return &post
}

// 記事を全検索
func (m PostRepository) GetAllPost() []Post {
    var post []Post
    db.Select("*").Find(&post)
    return post
}

// 記事を投稿する
func (m PostRepository) CreatePost(header string, body string, author string) bool {
    post := Post {
        Header: header,
        Body:   body,
    }
    db.Create(&post)
    return true
}

// 記事の件名と本文を更新する
func (m PostRepository) UpdatePost(id int, header string, body string) interface{} {
    post := Post{ID: id}
    db.Model(&post).Updates(map[string]interface{}{
        "Header":header, 
        "Body":  body,
        })
    return true
}

// 記事を削除する
func (m PostRepository) DeletePost(id int) interface{} {
    post := Post{ID: id}
    db.Delete(post)
    return nil
}

まとめ

Gin + gormで割りとすぐにWebアプリが作れた(簡単なものだけど)

ORMライブラリの使い勝手としてはxormよりもgormの方が若干 CRUDの処理がシンプルに書ける印象。

ドキュメントも分かりやすく、CRUDが簡単に実装できたので 好きになりました。

 

また、テーブル名が構造体の名前に依存する(Post構造体にはpostsというテーブルが紐づく)ので、

テーブル名には気をつける必要がある。

 

Gin + gormは思ってたよりも記述量が少なく、記述もシンプルなので

個人的にはWebアプリを作る時にGoを使うという選択肢はありなように感じました。