"Create" dan "Upload" di Golang Postgres: Back-end
Tags: go, indonesia, postgres, backend
Published: 2023-06-15 09:26:01

## Cek file-file v2

Repository/source code: https://github.com/JaisySymuri/postgre

Buat folder baru "files" sebagai tempat untuk menampung file yang akan kita upload. Kemudian di main.go, sebelum func dbConn, buat struct Employee

> main.go
```js
type Employee struct {
	ID_Pegawai    int
	NIK           string
	Nama          string
	Username      string
	Password      string
	Alamat        string
	Tempat_Lahir  string
	Tanggal_Lahir string
	No_HP         string
	Pekerjaan     string
	Gender        string
	Email         string
	Nama_Ibu      string
	File          string
	SearchType    string
}
```

Di golang, posisi variable, constants, structure, dan functions tidak masalah. Cuma agar mudah saat diingat, fungsi-fungsi diletakkan di bagian paling bawah. 

Kemudiaan, setelah func dbConn, buat fungsi untuk menangani insert query. Isi dengan membuat variable db untuk membuka koneksi ke database dan cek metode POST

> main.go:Insert
```js
func Insert(w http.ResponseWriter, r *http.Request) {

	db := dbConn()
	if r.Method == "POST" {
	
...
```




Kemudian, atur penyimpanan file yang dikirim ke folder 'files" yang kita buat sebelumnya.
> main.go:Insert
```js
...
alias := r.FormValue("alias")
		uploadedFile, handler, err := r.FormFile("file")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer uploadedFile.Close()

		dir, err := os.Getwd()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		filename := handler.Filename
		if alias != "" {
			filename = fmt.Sprintf("%s%s", alias, filepath.Ext(handler.Filename))
		}

		fileLocation := filepath.Join(dir, "files", filename)
		targetFile, err := os.OpenFile(fileLocation, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, uploadedFile); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
...
```

Atur kode untuk menerima isi formulir yang dikirim dan mengubahnya sebagai query ke database
> main.go:Insert

```js
...
NIK := r.FormValue("NIK")
		Nama := r.FormValue("Nama")
		Username := r.FormValue("Username")
		Password := r.FormValue("Password")
		Alamat := r.FormValue("Alamat")
		Tempat_Lahir := r.FormValue("Tempat_Lahir")
		Tanggal_Lahir := r.FormValue("Tanggal_Lahir")
		No_HP := r.FormValue("No_HP")
		Pekerjaan := r.FormValue("Pekerjaan")
		Gender := r.FormValue("Gender")
		Email := r.FormValue("Email")
		Nama_Ibu := r.FormValue("Nama_Ibu")

		insertDynStmt := `insert into "employee"("nik", "nama", "username", "password", "alamat", "tempat_lahir", "tanggal_lahir", "no_hp", "pekerjaan", "gender", "email", "nama_ibu", "file") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`
		_, err = db.Exec(insertDynStmt, NIK, Nama, Username, Password, Alamat, Tempat_Lahir, Tanggal_Lahir, No_HP, Pekerjaan, Gender, Email, Nama_Ibu, filename)

		log.Println("INSERT INTO employee(NIK, Nama, Username, Password, Alamat, Tempat_Lahir, Tanggal_Lahir, No_HP, Pekerjaan, Gender, Email, Nama_Ibu, File) VALUES (" + NIK + ", " + Nama + ", " + Username + ", " + Password + ", " + Alamat + ", " + Tempat_Lahir + ", " + Tanggal_Lahir + ", " + No_HP + ", " + Pekerjaan + ", " + Gender + ", " + Email + ", " + Nama_Ibu + ", " + filename + ")")
...

```




Terakhir, tutup koneksi ke database dan redirect agar user kembali ke halaman utama ketika selesai mengisi formulir.

> main.go:Insert
```js
...
defer db.Close()
	http.Redirect(w, r, "/", 301)
}

```





Hasilnya adalah seperti ini:
> main.go
```js
func Insert(w http.ResponseWriter, r *http.Request) {

	db := dbConn()
	if r.Method == "POST" {
		//File Upload
		alias := r.FormValue("alias")
		uploadedFile, handler, err := r.FormFile("file")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer uploadedFile.Close()

		dir, err := os.Getwd()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		filename := handler.Filename
		if alias != "" {
			filename = fmt.Sprintf("%s%s", alias, filepath.Ext(handler.Filename))
		}

		fileLocation := filepath.Join(dir, "files", filename)
		targetFile, err := os.OpenFile(fileLocation, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, uploadedFile); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		NIK := r.FormValue("NIK")
		Nama := r.FormValue("Nama")
		Username := r.FormValue("Username")
		Password := r.FormValue("Password")
		Alamat := r.FormValue("Alamat")
		Tempat_Lahir := r.FormValue("Tempat_Lahir")
		Tanggal_Lahir := r.FormValue("Tanggal_Lahir")
		No_HP := r.FormValue("No_HP")
		Pekerjaan := r.FormValue("Pekerjaan")
		Gender := r.FormValue("Gender")
		Email := r.FormValue("Email")
		Nama_Ibu := r.FormValue("Nama_Ibu")

		insertDynStmt := `insert into "employee"("nik", "nama", "username", "password", "alamat", "tempat_lahir", "tanggal_lahir", "no_hp", "pekerjaan", "gender", "email", "nama_ibu", "file") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`
		_, err = db.Exec(insertDynStmt, NIK, Nama, Username, Password, Alamat, Tempat_Lahir, Tanggal_Lahir, No_HP, Pekerjaan, Gender, Email, Nama_Ibu, filename)

		log.Println("INSERT INTO employee(NIK, Nama, Username, Password, Alamat, Tempat_Lahir, Tanggal_Lahir, No_HP, Pekerjaan, Gender, Email, Nama_Ibu, File) VALUES (" + NIK + ", " + Nama + ", " + Username + ", " + Password + ", " + Alamat + ", " + Tempat_Lahir + ", " + Tanggal_Lahir + ", " + No_HP + ", " + Pekerjaan + ", " + Gender + ", " + Email + ", " + Nama_Ibu + ", " + filename + ")")
	}
	defer db.Close()
	http.Redirect(w, r, "/", 301)
}
```

Ctrl+S, untuk import package yang diperlukan.



Kemudian, kita atur kode agar template kita berjalan. Buat variable tmpl sebelum func dbConn:

> main.go

```js
var tmpl = template.Must(template.ParseGlob("form/*"))

```

Buat fungsi untuk menjalankan template 'New'.
 
> main.go

```js
func New(w http.ResponseWriter, r *http.Request) {

	tmpl.ExecuteTemplate(w, "New", nil)

}
```

Akhirnya, buat fungsi utama untuk mengatur semua handler:
> main.go

```js
func main() {
	log.Println("Server started on: http://localhost:9999")
	http.HandleFunc("/new", New)
	http.HandleFunc("/insert", Insert)
	http.ListenAndServe(":9999", nil)
}

```

Disini kita buat log agar saat kita jalankan aplikasi, terminal akan memberitahukan kita bahwa server berhasil berjalan di port yang kita inginkan. Atur handler dengan menetapkan alamat internetnya dan fungsi yang dipanggil. Terakhir, kode untuk menjalankan server. 

Oke, jalankan server melalui perintah terminal 'go run main.go'. Jika berhasil, maka terminal akan membalas dengan log

> Terminal

```js
PS D:\Work\Tutorial\postgre> go run main.go
2023/06/15 15:44:44 Server started on: http://localhost:9999
```

Buka browser, dan pergi ke alamat http://localhost:9999/new. Jika berhasil, maka hasilnya akan sama dengan tes html yang kita buat di front-end. Isi formulir dan klik 'save user'. Kita akan menemukan '404 not found'. Tapi ini kita tidak masalah, karena kita memang belum mengatur halaman home yang otomatis dituju ketika kita selesai mengisi formulir. Yang penting itu log terminal, dia akan menunjukkan database berhasil terisi atau tidak. Jika berhasil, maka dia akan membalas dengan:

> Terminal

```js
2023/06/15 15:36:23 INSERT INTO employee(NIK, Nama, Username, Password, Alamat, Tempat_Lahir, Tanggal_Lahir, No_HP, Pekerjaan, Gender, Email, Nama_Ibu, File) VALUES (33, 333, 333, 352353, 33, 33, 2023-06-11, 33, 33, 33, 33, 33, 33.jpg)

```

Untuk mengecek apakah database benar-benar terisi, anda bisa cek TablePlus (refresh).


![Cek insert](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofehe98119sg97qsqu9y.png)

Masih bingung apa yang sebenarnya terjadi? Mari saya jelaskan!
Pertama, fungsi main menjalankan server dengan perintah 'ListenAndServe'. 'http.HandleFunc("/new", New)' akan membuat 'http://localhost:9999/new' bisa diakses dengan memanggil fungsi 'New'. Reaksi berantai pun terjadi:

![Insert func](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ja0seal48fqzdybd5dg0.png)

Demikian, fungsi 'Create' berhasil kita buat! 


Terakhir, untuk mempermudah transfer knowledge dan rollback, maka main.go akan saya copy, rename menjadi main-v2.go, select all kecuali package kemudian ctrl+/ untuk merubahnya menjadi komentar. Jadi jika anda baru mempelajari tahap ini, anda tidak akan bingung saat anda mengecek repository yang sudah lengkap hingga tahap akhir. Cukup cek file-file v2. Proses rollback juga akan jadi lebih mudah


Repository/source code: https://github.com/JaisySymuri/postgre