Go言語を真剣に勉強してみた〜番外編〜

#はじめに
こんにちは。某学校でプログラミング等の勉強中のサーバーサイドのプログラマーのワタタクです。:relaxed:
さて今回は「Go言語を真剣に勉強してみた〜番外編〜」と言うことでこれまで紹介してきたもので簡単なWEBアプリケーションを作れるようになったので作ってみたいと思います。
今回はGoとMysqlを使って簡単なWEBアプリケーションを作っていきます。
では、早速いってみましょう:point_up:
#完成形
<img width="555" alt="スクリーンショット 2018-10-16 9.50.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/116696/f3670e79-7d15-be37-c7bb-4a8adfcc8f6e.png">
※ここから各種ボタンを押すことによって、各種の適切な動きが出てくるアプリケーション。
#テーブル作成
```
CREATE TABLE dept (
	deptno INT(2), 
	dname TEXT, 
	loc TEXT,
	PRIMARY KEY(deptno)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```
次にカラムを追加します。

```
INSERT INTO dept (deptno, dname, loc) VALUES (10,'ACCOUNTING','NEW YORK');
INSERT INTO dept (deptno, dname, loc) VALUES (20,'RESEARCH','DALLAS');
INSERT INTO dept (deptno, dname, loc) VALUES (30,'SALES','CHICAGO');
INSERT INTO dept (deptno, dname, loc) VALUES (40,'OPERATIONS','BOSTON');
```
#ディレクトリ構成

```
scottadmin
 |-Pkg
 |  |-entitys
 |  |    |-dept.go
 |  |-dao
 |     |-deptDAO.go
 |-server.go
 |-static
    |-css
    |   |-main.css
    |-view
    |   |-deptList.html.tpl
    |-add
    |   |-goDeptadd.html.tpl
    |-edit
    |   |-deptEdit.html.tpl
    |-delete
        |-confirmDeptDelete.html.tpl
```
以上で準備完了なので早速作っていきましょう
##パッケージ(entitys、dao)
パッケージについてはこちらを参照[「Go言語を真剣に勉強してみた〜パッケージ編〜」](https://qiita.com/watataku/items/d3e0133d0c1574422c72)から。

```golang:dept.go
package entitys

type Dept struct {
	Deptno int
	Dname  string
	Loc    string
}

type DeptList []Dept
```
```golang:deptDAO.go
package dao

import (
	"../entitys"
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
)

/**
 *全件取得
 *引数：dbオブジェクト
 *戻り値：構造体のDeptList
 */
func FindAll(db *sql.DB) *entitys.DeptList {
	rows, err := db.Query("SELECT * FROM dept")
	if err != nil {
		log.Fatal(err);
	}

	defer rows.Close()
	var d entitys.Dept
	var deptList entitys.DeptList
	for rows.Next() {
		err := rows.Scan(&d.Deptno, &d.Dname, &d.Loc)
		if err != nil {
			log.Fatal(err)
		}
		deptList = append(deptList, d)//構造体のDeptを配列「DeptList」に入れている
		fmt.Println(deptList);
	}
	return &deptList
}
```
データベースの扱いについては[「Go言語を真剣に勉強してみた〜データベース接続(MySQL)編〜」](https://qiita.com/watataku/items/8c94f8f380f91aa3f3e5)から。

ついでにその他更新系などのメソッドも作っちゃいましょう。

```golang:deptList.go
/**
 *主キー検索
 *引数：dbオブジェクト、部門番号
 *戻り値：構造体Dept
 */
func FindByPk(db *sql.DB, deptno string) *entitys.Dept {
	stmt, err := db.Prepare("SELECT * FROM dept WHERE deptno = ?")
	if err != nil {
		log.Fatal(err)
	}
	var d entitys.Dept
	err = stmt.QueryRow(deptno).Scan(&d.Deptno, &d.Dname, &d.Loc)
	if err != nil {
		log.Fatal(err)
	}　
	return &d
}

/**
 *削除
 *引数：dbオブジェクト、部門番号
 *戻り値：なし
 */
func Delete(db *sql.DB, deptno string) {
	tr,err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := db.Prepare("DELETE FROM dept WHERE deptno=?")
	if err != nil {
		log.Fatal(err)
		tr.Rollback()
	}
	res, err := stmt.Exec(deptno)
	if err != nil {
		log.Fatal(err)
		tr.Rollback()
	}
	tr.Commit()
	fmt.Println(res)
	fmt.Println("部門番号" + deptno + "を削除しました")

}

/**
 *更新
 *引数：dbオブジェクト、部門名、所在地、部門番号
 *戻り値：なし
 */
func Update(db *sql.DB, dname, loc, deptno string) {
	tr,err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := db.Prepare("UPDATE dept SET dname=?,loc=? WHERE deptno=?")
	if err != nil {
		log.Fatal(err)
		tr.Rollback()
	}
	res, err := stmt.Exec(dname,loc, deptno)
	if err != nil {
		log.Fatal(err)
		tr.Rollback()
	}
	tr.Commit()
	fmt.Println(res)
	fmt.Println("部門番号" + deptno + "を更新しました")
}

/**
 *追加
 *引数：dbオブジェクト、部門番号、部門名、所在地
 *戻り値：なし
 */
func Insert(db *sql.DB, deptno string, dname string, loc string) {
	tr,err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := db.Prepare("INSERT INTO dept(deptno, dname, loc) VALUES(?, ?, ?)")
	if err != nil {
		log.Fatal(err)
		tr.Rollback()
	}
	res, err := stmt.Exec(deptno, dname, loc)
	if err != nil {
		log.Fatal(err)
		tr.Rollback()
	}
	fmt.Println(res)

}
```
#main処理
サーバの起動法などについては[「Go言語を真剣に勉強してみた〜WEBアプリケーション開発編〜」](https://qiita.com/watataku/items/23dafc566e7766599f77)

```golang:server.go
package main

import (
	"./Pkg/entitys"
	"./Pkg/dao"
	"database/sql"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
)

func deptList(w http.ResponseWriter, r *http.Request) {
	path, _ := os.Getwd()
    // テンプレートをパース
	t := template.Must(template.ParseFiles(path + "/static/view/deptList.html.tpl"))
	//DB処理
	db, err := sql.Open("mysql", "scott:tiger@tcp(127.0.0.1:8889)/wp32scott")//通常：ポート番号３３０６、＊manp:8889
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		fmt.Println("データベース接続失敗")
	}

	defer db.Close()
	 deptList := dao.FindAll(db)
	 if deptList == nil {
	 	fmt.Println("から")
	 }

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "deptList.html.tpl", deptList); err != nil {
      log.Fatal(err)
    }



}

func add(w http.ResponseWriter, r *http.Request)  {
	path, _ := os.Getwd()
	// テンプレートをパース
	t := template.Must(template.ParseFiles(path + "/static/add/goDeptAdd.html.tpl"))
	// テンプレートを描画
	if err := t.ExecuteTemplate(w, "goDeptAdd.html.tpl", nil); err != nil {
		log.Fatal(err)
	}
}

func addNow (w http.ResponseWriter, r *http.Request) {
	deptno := r.FormValue("addDeptDeptno")
	dname := r.FormValue("addDeptDname")
	loc := r.FormValue("addDeptLoc")
	db, err := sql.Open("mysql", "scott:tiger@tcp(127.0.0.1:8889)/wp32scott")//通常：ポート番号３３０６、＊manp:8889
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		fmt.Println("データベース接続失敗")
	}
	defer db.Close()
	
	dao.Insert(db, deptno, dname, loc)

	http.Redirect(w, r, "/view", http.StatusMovedPermanently)

}

func edit(w http.ResponseWriter, r *http.Request) {
	var deptno string
	deptno = r.FormValue("editDeptDeptno")

	path, _ := os.Getwd()
	// テンプレートをパース
	t := template.Must(template.ParseFiles(path + "/static/edit/deptEdit.html.tpl"))
	//DB処理
	db, err := sql.Open("mysql", "scott:tiger@tcp(127.0.0.1:8889)/wp32scott")//通常：ポート番号３３０６、＊manp:8889
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		fmt.Println("データベース接続失敗")
	}

	defer db.Close()
	deptList := dao.FindByPk(db, deptno)
	if deptList == nil {
		fmt.Println("空")
	}

	// テンプレートを描画
	if err := t.ExecuteTemplate(w, "deptEdit.html.tpl", deptList); err != nil {
		log.Fatal(err)
	}
}

func editNow(w http.ResponseWriter, r *http.Request)  {
	dname := r.FormValue("editDeptDname")
	loc := r.FormValue("editDeptLoc")
	deptno := r.FormValue("editDeptDeptno")
	db, err := sql.Open("mysql", "scott:tiger@tcp(127.0.0.1:8889)/wp32scott")//通常：ポート番号３３０６、＊manp:8889
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		fmt.Println("データベース接続失敗")
	}
	defer db.Close()

	dao.Update(db, dname, loc, deptno)

	http.Redirect(w, r, "/view", http.StatusMovedPermanently)

}


func delete(w http.ResponseWriter, r *http.Request) {
	var deptno string
	deptno = r.FormValue("deleteDeptDeptno")
	path, _ := os.Getwd()
	// テンプレートをパース
	t := template.Must(template.ParseFiles(path + "/static/delete/confirmDeptDelete.html.tpl"))
	//DB処理
	db, err := sql.Open("mysql", "scott:tiger@tcp(127.0.0.1:8889)/wp32scott")//通常：ポート番号３３０６、＊manp:8889
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		fmt.Println("データベース接続失敗")
	}

	defer db.Close()
	deptList := dao.FindByPk(db, deptno)
	if deptList == nil {
		fmt.Println("空")
	}

	// テンプレートを描画
	if err := t.ExecuteTemplate(w, "confirmDeptDelete.html.tpl", deptList); err != nil {
		log.Fatal(err)
	}
}

func deleteNow(w http.ResponseWriter, r *http.Request) {
	deptno := r.FormValue("deleteDeptDeptno")

	db, err := sql.Open("mysql", "scott:tiger@tcp(127.0.0.1:8889)/wp32scott")//通常：ポート番号３３０６、＊manp:8889
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		fmt.Println("データベース接続失敗")
	}
	defer db.Close()

	dao.Delete(db,deptno)

	http.Redirect(w, r, "/view", http.StatusMovedPermanently)

}

func main() {
	http.Handle("/", http.FileServer(http.Dir("static")))

	/**
	 *部門リスト
	 */
	http.HandleFunc("/view/", deptList)
	http.HandleFunc("/add/", add)
	http.HandleFunc("/add/now", addNow)
	http.HandleFunc("/edit", edit)
	http.HandleFunc("/edit/now", editNow)
	http.HandleFunc("/delete", delete)
	http.HandleFunc("/delete/now", deleteNow)

	http.ListenAndServe(":9000", nil)
}

```
#テンプレート
```html:deptList.html.tpl
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>部門情報リスト｜ScottAdmin</title>
    <link rel="stylesheet" type="text/css" href=" ../css/main.css">
</head>
<body>
<h1>部門情報リスト</h1>
<nav>
    <ul>
        <li><a href="/">TOP</a></li>
        <li>部門情報リスト</li>
    </ul>
</nav>
<section>
    <p>
        新規登録は<a href="/add">こちら</a>から
    </p>
</section>

<section>
    <table>
        <thead>
        <tr>
            <th>部門番号</th>
            <th>部門名</th>
            <th>所在地</th>
            <th colspan="2">操作</th>
        </tr>
        </thead>
        <tbody>
        {{ range .}}
        <tr>
            <td>{{.Deptno}}</td>
            <td>{{.Dname}}</td>
            <td>{{.Loc}}</td>
            <td>
                <form action=" /edit" method="post">
                    <input type="hidden" name="editDeptDeptno" id="editDeptDeptno" value="{{.Deptno}}">
                    <input type="submit" value="編集">
                </form>
            </td>
            <td>
                <form action=" /delete" method="post">
                    <input type="hidden" name="deleteDeptDeptno" id="deleteDeptDeptno" value="{{.Deptno}}">
                    <input type="submit" value="削除">
                </form>
            </td>
        </tr>
        {{ end }}
        </tbody>
    </table>
</section>
</body>
</html>
```

```html:goDeptAdd.html.tpl
<!DOCTYPE html>
<html lang="ja">
<head>
    <title>部門情報追加｜ScottAdmin</title>
    <link rel="stylesheet" type="text/css" href=" ../css/main.css">
    <meta charset="utf-8">
</head>
<body>
<h1>部門情報追加</h1>
<nav>
    <ul>
        <li><a href=" /">TOP</a></li>
        <li><a href=" /view">部門リスト</a></li>
        <li>部門情報追加</li>
    </ul>
</nav>

<section>
    <p>情報を入力し、登録ボタンをクリックしてください</p>

    <form action=" /add/now" method="post">
        <table>
            <tbody>
            <tr>
                <th>部門番号&nbsp;<span class="required">必須</span></th>
                <td><input type="text" name="addDeptDeptno" id="addDeptDeptno" value=""></td>
            </tr>
            <tr>
                <th>部門名&nbsp;<span class="required">必須</span></th>
                <td><input type="text" name="addDeptDname" id="addDeptDname" value="" ></td>
            </tr>
            <tr>
                <th>所在地</th>
                <td><input type="text" name="addDeptLoc" id="addDeptLoc" value=""></td>
            </tr>
            <tr>
                <td colspan="2" class="submit"><input type="submit" value="登録"></td>
            </tr>
            </tbody>
        </table>
    </form>
</section>
</body>
</html>
```
```html:deptEdit.html.tpl
<!DOCTYPE html>
<html lang="ja">
<head>
    <title>部門情報編集｜ScottAdmin</title>
    <link rel="stylesheet" type="text/css" href=" ../css/main.css">
    <meta charset="utf-8">
</head>
<body>
<h1>部門情報編集</h1>
<nav>
    <ul>
        <li><a href=" /">TOP</a></li>
        <li><a href=" /view">部門リスト</a></li>
        <li>部門情報編集</li>
    </ul>
</nav>
<section>
    <p>情報を入力し、更新ボタンをクリックしてください</p>

    <form action=" /edit/now" method="post">
        <table>
            <tbody>
            <tr>
                <th>部門番号</th>
                <td>{{ .Deptno }}<input type="hidden" name="editDeptDeptno" id="editDeptDeptno" value="{{ .Deptno}}"></td>
            </tr>
            <tr>
                <th>部門名&nbsp;<span class="required">必須</span></th>
                <td><input type="text" name="editDeptDname" id="editDeptDname" value="{{ .Dname}}" required></td>
            </tr>
            <tr>
                <th>所在地</th>
                <td><input type="text" name="editDeptLoc" id="editDeptLoc" value="{{ .Loc }}"></td>
            </tr>
            <tr>
                <td colspan="2" class="submit"><input type="submit" value="更新"></td>
            </tr>
            </tbody>
        </table>
    </form>
</section>
</body>
</html>
```
```html:confirmDeptDelete.html.tpl
<!DOCTYPE html>
<html lang="ja">
<head>
    <title>部門情報削除｜ScottAdmin</title>
    <link rel="stylesheet" type="text/css" href=" ../css/main.css">
    <meta charset="utf-8">
</head>
<body>
<h1>部門情報削除</h1>
<nav>
    <ul>
        <li><a href=" /">TOP</a></li>
        <li><a href=" /view">部門リスト</a></li>
        <li>部門情報削除</li>
    </ul>
</nav>

<section>
    <p>以下の部門情報を削除します。よろしければ、削除ボタンを押してください。</p>

    <form action=" /delete/now " method="post">
        <table border="1">
            <tbody>
            <tr>
                <th>部門番号</th>
                <td>{{ .Deptno }}<input type="hidden" name="deleteDeptDeptno" id="deleteDeptDeptno" value="{{ .Deptno}}"></td>
            </tr>
            <tr>
                <th>部門名</th>
                <td>{{ .Dname }}</td>
            </tr>
            <tr>
                <th>所在地</th>
                <td>{{ .Loc }}</td>
            </tr>
            <tr>
                <td colspan="2" class="submit"><input type="submit" value="削除"></td>
            </tr>
            </tbody>
        </table>
    </form>
</section>
</body>
</html>
```
#おまけ

```css:main.css
@charset "UTF-8";
table{
    border-collapse: collapse;
}
table th{
    border:solid 1px black;
}
table td{
    border:solid 1px black;
}
nav ul li{
    display: inline;
    list-style-type: none;
}
nav ul .current{
    color: red;
}
nav ul li:before{
    content:">";
}
nav ul li:first-child:before{
    content:none;
}
.submit{
    text-align: center;
}
.required{
    color: red;
    font-size: 20px;
}
#errorMsg{
    border:solid 1px orange;
    color: red;
    padding: 10px;
    margin:20px;
    width: 400px;
}
```
以上。
#最後に
もし何か間違っている等のご指摘があればご連絡ください。

※このサンプルで作ったアプリケーションはエラーメッセージやフラッシュメッセージ等が出ません。更にエラー処理などもしていません。
出し方などについてはまたこれから頑張ります。
まだまだ勉強しないとダメだな〜w

最後まで読んで頂きありがとうございました。

#追記（2018/10/17）
ご指摘があったので訂正。
パッケージ名entitysなのですが、entityの複数形はentitysではなくentitiesでした。申し訳ありません。