【#46 エンジニア転職学習】GinTutorialのWebApp改造 HTMLに連動させる

# はじめに
富山県に住んでいるChikaといいます。
毎日投稿を目標に、バックエンドエンジニア転職に向けた学習内容をアウトプットします。

GoのフレームワークであるGinを中心に、
webアプリ開発学習をしていきます。

バックエンドエンジニアになるまでの学習内容は以前投稿した以下の記事を基にしています。


https://qiita.com/Chika110/items/ef54dddd565a0193ef44

# 本日の学習内容
GinのTutorialで作成したAPIを改造して、簡素なWebアプリを作成しようと思います

* **GinとGORMを使用したAPIをHTMLに連動　←Topics!!**


# GinとGORMを使用したAPIをHTMLに連動
先日GORMを使用してDBと連動させたAPIをHTMLファイルとつなげて、
Webから操作できるようにしました。

db.Closeが書かれているコードもありますが、
POST後のRedirect時などに正常に反応しなかったのでCloseはしてません。
正常に反応する修正方法等、アドバイスあればいただきたいです。

~~~main.go
package main

import (
	"ginapp-albums/app/controllers"
	"ginapp-albums/app/models"
)

func main() {
	dbConn := models.DbInit()
	controllers.NewRoutes(dbConn)
}
}
~~~
### app/models
~~~albumdb.go
package models

import (
	"github.com/jinzhu/gorm"
)

// Connstruct Album struct
type Album struct {
	gorm.Model
	Title  string  `json:"title" form:"title"`
	Artist string  `json:"artist" form:"artist"`
	Price  float64 `json:"price" form:"price"`
}

~~~
~~~base.go
package models

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

//Initialized DB
func DbInit() *gorm.DB {
	var dsn = "user:password@/DBNAME?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open("mysql", dsn)
	if err != nil {
		panic("failed connecting DB")
	}
	db.LogMode(true)
	db.AutoMigrate(&Album{})
	return db
}
~~~
### app/controllers
~~~webserver.go
package controllers

import (
	"ginapp-albums/app/models"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

type AlbumHandler struct {
	Db *gorm.DB
}

func (h *AlbumHandler) GetAlbums() []models.Album {
	var albums []models.Album

	h.Db.Find(&albums)

	return albums
}

func (h *AlbumHandler) InsertAlbums(c *gin.Context) {
	var albumRequest models.Album

	if err := c.Bind(&albumRequest); err != nil {
		albums := h.GetAlbums()
		c.HTML(http.StatusBadRequest, "index.html", gin.H{"albums": albums, "err": err})
		c.Abort()
	} else {
		title, _ := c.GetPostForm("title")
		artist, _ := c.GetPostForm("artist")
		price, _ := c.GetPostForm("price")
		floatprice, _ := strconv.ParseFloat(price, 64)

		h.Db.Create(&models.Album{Title: title, Artist: artist, Price: floatprice})

		c.Redirect(http.StatusMovedPermanently, "/albums")
	}
}

func (h *AlbumHandler) GetAlbumByID(c *gin.Context) {
	id := c.Param("id")
	checkId, err := strconv.Atoi(id)
	if err != nil {
		panic(err)
	}
	album := models.Album{}

	h.Db.First(&album, checkId)

	c.HTML(http.StatusOK, "update.html", gin.H{"album": album})
}

func (h *AlbumHandler) UpdateAlbumByID(c *gin.Context) {
	id := c.Param("id")
	checkId, err := strconv.Atoi(id)
	if err != nil {
		panic(err)
	}
	var album models.Album
	var albumRequest models.Album

	if err := c.Bind(&albumRequest); err != nil {
		albums := h.GetAlbums()
		c.HTML(http.StatusBadRequest, "index.html", gin.H{"albums": albums, "err": err})
		c.Abort()
	} else {
		title, _ := c.GetPostForm("title")
		artist, _ := c.GetPostForm("aritst")
		price, _ := c.GetPostForm("price")
		floatprice, _ := strconv.ParseFloat(price, 64)

		h.Db.First(&album, checkId)
		album.Title = title
		album.Artist = artist
		album.Price = floatprice
		h.Db.Save(&album)

		c.Redirect(http.StatusMovedPermanently, "/albums")
	}
}

func (h *AlbumHandler) DeleteAlbumByID(c *gin.Context) {
	id := c.Param("id")
	checkId, err := strconv.Atoi(id)
	if err != nil {
		panic(err)
	}
	var album models.Album

	h.Db.First(&album, checkId)
	h.Db.Delete(&album)

	c.Redirect(http.StatusMovedPermanently, "/albums")
}
~~~
~~~route.go
package controllers

import (
	"net/http"
	"time"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

func NewRoutes(dbConn *gorm.DB) {
	albumHandler := AlbumHandler{
		Db: dbConn,
	}
	router := gin.Default()

	router.Use(cors.New(cors.Config{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"PUT", "PATCH", "DELETE", "POST", "GET"},
		AllowHeaders:     []string{"Origin"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
		AllowOriginFunc: func(origin string) bool {
			return origin == "*"
		},
		MaxAge: 12 * time.Hour,
	}))

	router.LoadHTMLGlob("app/views/*.html")
	router.GET("/albums", func(c *gin.Context) {
		albums := albumHandler.GetAlbums()
		c.HTML(http.StatusOK, "index.html", gin.H{"albums": albums})
	})
	router.GET("/albums/:id", albumHandler.GetAlbumByID)
	router.POST("/albums/new", albumHandler.InsertAlbums)
	router.POST("/albums/update/:id", albumHandler.UpdateAlbumByID)
	router.POST("/albums/delete/:id", albumHandler.DeleteAlbumByID)

	router.Run("localhost:8080")
}
~~~
### app/views
~~~index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>AlbumsList</title>
</head>
<body>
    <h1><a href="/albums">AlbumListApp</a></h1>
    <h2>Add Album</h2>
    <form method="POST" action="/albums/new">
        <p>Title <input type="text" name="title" value="{{.album.Title}}" size="30" placeholder="Input Title"></p>
        <p>Artist <input type="text" name="artist" value="{{.album.Artist}}" size="30" placeholder="Input Artist"></p>
        <p>Price <input type="text" name="price" value="{{.album.Price}}" size="30" placeholder="Input Price"></p>
        <p><input type="submit" value="Send"></p>
    </form>

    <ul>
        {{ range .albums }}
            <li>ID:{{ .ID }} Title:{{ .Title }} Artist:{{ .Artist }} Price:{{ .Price }}
                <label><a href="/albums/{{.ID}}">UPDATE</a></label>
                <label><form action="/albums/delete/{{.ID}}" method="POST"><input type="submit" value="DELETE"></form></label>
            </li>
        {{end}}
    </ul>
</body>
</html>
~~~
~~~update.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>UpdateAlbum</title>
</head>
<body>
    <h1><a href="/albums">AlbumListApp</a></h1>
    <h2>Update Album</h2>
    <p>ID:{{ .album.ID }}</p>
    <p>Title:{{ .album.Title }}</p>
    <p>Artist:{{ .album.Artist }}</p>
    <p>Price:{{ .album.Price }}</p>

    <h3>Edit</h3>
    <form method="POST" action="/albums/update/{{.album.ID}}">
        <p>Title <input type="text" name="title" value="{{.album.Title}}" size="30" placeholder="Input Title"></p>
        <p>Artist <input type="text" name="artist" value="{{.album.Artist}}" size="30" placeholder="Input Artist"></p>
        <p>Price <input type="text" name="price" value="{{.album.Price}}" size="30" placeholder="Input Price"></p>
        <p><input type="submit" value="Send"></p>
    </form>
</body>
</html>
~~~

##### 使用している教材はこちら↓


https://go.dev/doc/tutorial/web-service-gin


# おわりに
最後までお読みいただきありがとうございました。
アドバイス・応援コメント等いただけますと幸いです。