Go製フレームワーク（BEEGO）の使い方

#Beego 

### 目次
1. インストール方法・環境構築
2. logの出力方法
3. マイグレーション

自分がいろいろ忘れないように覚書程度に記載
基本的にマニュアルどおりに作っていけばよい。ハチが可愛い
[公式サイト](https://beego.me/)

前提：goが使える。
- インストール（環境設定）
- プロジェクト作成
- ルーティング

##インストール
```
go get -u github.com/beego/bee
go get -u github.com/astaxie/beego
```
以上

cliツールがあり、今回はAPIとしてプロジェクトを作成

```
#GOPATH/srcのディレクトリの下で下記コマンド
bee api testapi
#自動的に必要なファイルがtestapi下に生成される
cd testapi
#サーバを起動
bee run -downdoc=true -gendoc=true
```
[http://127.0.0.1:8080/swagger/]
APIドキュメントもいっしょに作成される

## 設定は下記ファイルにて行う
`testapi\conf\app.conf`

ただし、アプリ名はなぜかディレクトリ名となってしまう。解決方法がわかる方いたら教えてほしい

### 環境別の設定の仕方
ここではまった。
ドキュメントには

```/conf/app.conf
[dev]
httpport = 8080
[prod]
httpport = 8088
[test]
httpport = 8888
```
こんな感じで書いてあるが
`bee run -runmode=prod` とか書いてもデフォルト値が設定され動かない。
すると、[config.go](https://github.com/astaxie/beego/blob/develop/config/config.go)にこんな記述が…

```config.go
if os.Getenv("BEEGO_RUNMODE") != "" {
  filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
}}
```
なるほど、`-runmode=prod` で起動するときは ファイル名を`prod.app.conf`にすればよいみたい。

```/conf/dev.app.conf
[dev]
httpport = 8080
```
```/conf/prod.app.conf
[dev]
httpport = 8088
```

動いた！ただbee run で実行するときは`/conf/app.conf`が必要



#logの出力の仕方
下記を実行
`go get github.com/astaxie/beego/logs`

```
import (
    "github.com/astaxie/beego/logs"
)
func main() {    
    //　一般的 
    l := logs.GetLogger()
    l.Println("this is a message of http")
    
　　//ORMのログ
    logs.GetLogger("ORM").Println("this is a message of orm")
　　　　　　　　
　　//デバッグのログ　
    logs.Debug("my book is bought in the year of ", 2016)
    logs.Info("this %s cat is %v years old", "yellow", 3)
    logs.Warn("json is a type of kv like", map[string]int{"key": 2016})
    logs.Error(1024, "is a very", "good game")
    logs.Critical("oh,crash")
}

```

# Migration：マイグレーション

```
MySQLの設定
mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | mysql_native_password |
+------------------+-----------+-----------------------+
4 rows in set (0.00 sec)

#やらないとこのエラーが出る
Could not show migrations table: The authentication plugin is not supported.
```
###モデルファイルの作成
beegoのCLIツールで簡単に作れる
`bee generate model テーブル名 -fields="name:int"`
※-fieldsは適当でよい。後でカラム等は簡単に修正できるため。

```sample_mstモデルを作成
bee generate model sample_mst -fields="name:int"`
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2019/04/13 04:08:39 INFO     ▶ 0001 Using 'Sample_mst' as model name
2019/04/13 04:08:39 INFO     ▶ 0002 Using 'models' as package name
	create	 /go/src/xxx/models/sample_mst.go
2019/04/13 04:08:39 SUCCESS  ▶ 0003 Model successfully generated!
```

作成されたファイル↓
(ID)は勝手に挿入される。テーブルのカラムは `type Sample_mst struct` の部分で再定義すれば問題ない。

```/go/src/xxx/models/sample_mst.go
package models

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/astaxie/beego/orm"
)
//モデル定義
type Sample_mst struct {
	Id   int64 `orm:"auto"`
	Name int
}

func init() {
	orm.RegisterModel(new(Sample_mst))
}

// AddSample_mst insert a new Sample_mst into database and returns
// last inserted Id on success.
func AddSample_mst(m *Sample_mst) (id int64, err error) {
	o := orm.NewOrm()
	id, err = o.Insert(m)
	return
}

// GetSample_mstById retrieves Sample_mst by Id. Returns error if
// Id doesn't exist
func GetSample_mstById(id int64) (v *Sample_mst, err error) {
	o := orm.NewOrm()
	v = &Sample_mst{Id: id}
	if err = o.QueryTable(new(Sample_mst)).Filter("Id", id).RelatedSel().One(v); err == nil {
		return v, nil
	}
	return nil, err
}

// GetAllSample_mst retrieves all Sample_mst matches certain condition. Returns empty list if
// no records exist
func GetAllSample_mst(query map[string]string, fields []string, sortby []string, order []string,
	offset int64, limit int64) (ml []interface{}, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(Sample_mst))
	// query k=v
	for k, v := range query {
		// rewrite dot-notation to Object__Attribute
		k = strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(k, v)
	}
	// order by:
	var sortFields []string
	if len(sortby) != 0 {
		if len(sortby) == len(order) {
			// 1) for each sort field, there is an associated order
			for i, v := range sortby {
				orderby := ""
				if order[i] == "desc" {
					orderby = "-" + v
				} else if order[i] == "asc" {
					orderby = v
				} else {
					return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
			qs = qs.OrderBy(sortFields...)
		} else if len(sortby) != len(order) && len(order) == 1 {
			// 2) there is exactly one order, all the sorted fields will be sorted by this order
			for _, v := range sortby {
				orderby := ""
				if order[0] == "desc" {
					orderby = "-" + v
				} else if order[0] == "asc" {
					orderby = v
				} else {
					return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
		} else if len(sortby) != len(order) && len(order) != 1 {
			return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
		}
	} else {
		if len(order) != 0 {
			return nil, errors.New("Error: unused 'order' fields")
		}
	}

	var l []Sample_mst
	qs = qs.OrderBy(sortFields...).RelatedSel()
	if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
		if len(fields) == 0 {
			for _, v := range l {
				ml = append(ml, v)
			}
		} else {
			// trim unused fields
			for _, v := range l {
				m := make(map[string]interface{})
				val := reflect.ValueOf(v)
				for _, fname := range fields {
					m[fname] = val.FieldByName(fname).Interface()
				}
				ml = append(ml, m)
			}
		}
		return ml, nil
	}
	return nil, err
}

// UpdateSample_mst updates Sample_mst by Id and returns error if
// the record to be updated doesn't exist
func UpdateSample_mstById(m *Sample_mst) (err error) {
	o := orm.NewOrm()
	v := Sample_mst{Id: m.Id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Update(m); err == nil {
			fmt.Println("Number of records updated in database:", num)
		}
	}
	return
}

// DeleteSample_mst deletes Sample_mst by Id and returns error if
// the record to be deleted doesn't exist
func DeleteSample_mst(id int64) (err error) {
	o := orm.NewOrm()
	v := Sample_mst{Id: id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Delete(&Sample_mst{Id: id}); err == nil {
			fmt.Println("Number of records deleted in database:", num)
		}
	}
	return
}


```

### 接続
接続情報は直書してもいいが、`/conf/app.conf`に記載する

```/conf/app.conf
//mySQLの設定
mysql_driver = "mysql"
mysql_user = "root"
mysql_password = "password"
mysql_host = "127.0.0.1"
mysql_db = "sample-db"
```

```main.go
package main

import (
	"fmt"
	_ "xxx/routers" //自分のルータがあれば
	"strings"
	"time"

	"github.com/astaxie/beego"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	beego.Run()
}

func init() {
	orm.RegisterDriver("mysql", orm.DRMySQL)
	driver := beego.AppConfig.String("mysql_driver")
	user := beego.AppConfig.String("mysql_user")
	pass := beego.AppConfig.String("mysql_password")
	host := beego.AppConfig.String("mysql_host")
	port := beego.AppConfig.String("mysql_host")
	db := beego.AppConfig.String("mysql_db")
	datasource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8", user, pass, host, port, db)
	orm.RegisterDataBase("default", driver, datasource, 30)
}
```

### DBを作成するためにやることは２つ。
モデルにorm.RegisterModel(new(モデル))を追加
mainにorm.RunSyncdb("default", false, true)を追加

RegisterModelはテーブルやカラムがなかった場合、作成してくれる


# 参考にさせていただいたサイト
[Go言語のWAF Beegoの紹介](https://developers.wano.co.jp/1594/)