better hierarchy and better way to make a CRUD
This commit is contained in:
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
# ---> VisualStudioCode
|
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
vendor/*
|
||||||
|
blog
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DB ...
|
||||||
|
type DB struct {
|
||||||
|
SQL *sql.DB
|
||||||
|
// Mgo *mgo.database
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbConn = &DB{}
|
||||||
|
|
||||||
|
// ConnectSQL ...
|
||||||
|
func ConnectSQL(host, port, uname, pass, dbname string) (*DB, error) {
|
||||||
|
dbSource := fmt.Sprintf(
|
||||||
|
"%s:%s@tcp(%s:%s)/%s?charset=utf8",
|
||||||
|
uname,
|
||||||
|
pass,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
dbname,
|
||||||
|
)
|
||||||
|
d, err := sql.Open("mysql", dbSource)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dbConn.SQL = d
|
||||||
|
return dbConn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// connectMongo ...
|
||||||
|
func connectMongo(host, port, uname, pass string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/driver"
|
||||||
|
models "blog/models"
|
||||||
|
repository "blog/repository"
|
||||||
|
post "blog/repository/post"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPostHandler ...
|
||||||
|
func NewPostHandler(db *driver.DB) *Post {
|
||||||
|
return &Post{
|
||||||
|
repo: post.NewSQLPostRepo(db.SQL),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post ...
|
||||||
|
type Post struct {
|
||||||
|
repo repository.PostRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch ...
|
||||||
|
func (p *Post) Fetch(w http.ResponseWriter, r *http.Request) {
|
||||||
|
payload, _ := p.repo.Fetch(r.Context(), 25)
|
||||||
|
|
||||||
|
respondwithJSON(w, http.StatusOK, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new post
|
||||||
|
func (p *Post) Create(w http.ResponseWriter, r *http.Request) {
|
||||||
|
post := models.Post{}
|
||||||
|
json.NewDecoder(r.Body).Decode(&post)
|
||||||
|
|
||||||
|
newID, err := p.repo.Create(r.Context(), &post)
|
||||||
|
fmt.Println(newID)
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, http.StatusInternalServerError, "Server Error")
|
||||||
|
}
|
||||||
|
respondwithJSON(w, http.StatusCreated, map[string]string{"message": "Successfully Created"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update a post by id
|
||||||
|
func (p *Post) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, _ := strconv.Atoi(chi.URLParam(r, "id"))
|
||||||
|
data := models.Post{ID: int(id)}
|
||||||
|
json.NewDecoder(r.Body).Decode(&data)
|
||||||
|
payload, err := p.repo.Update(r.Context(), &data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, http.StatusInternalServerError, "Server Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
respondwithJSON(w, http.StatusOK, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByID returns a post details
|
||||||
|
func (p *Post) GetByID(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, _ := strconv.Atoi(chi.URLParam(r, "id"))
|
||||||
|
payload, err := p.repo.GetByID(r.Context(), int64(id))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, http.StatusNoContent, "Content not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
respondwithJSON(w, http.StatusOK, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a post
|
||||||
|
func (p *Post) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, _ := strconv.Atoi(chi.URLParam(r, "id"))
|
||||||
|
_, err := p.repo.Delete(r.Context(), int64(id))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
respondWithError(w, http.StatusInternalServerError, "Server Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
respondwithJSON(w, http.StatusMovedPermanently, map[string]string{"message": "Delete Successfully"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// respondwithJSON write json response format
|
||||||
|
func respondwithJSON(w http.ResponseWriter, code int, payload interface{}) {
|
||||||
|
response, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(code)
|
||||||
|
w.Write(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// respondwithError return error message
|
||||||
|
func respondWithError(w http.ResponseWriter, code int, msg string) {
|
||||||
|
respondwithJSON(w, code, map[string]string{"message": msg})
|
||||||
|
}
|
||||||
@@ -1,146 +1,42 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"blog/driver"
|
||||||
"encoding/json"
|
ph "blog/handler/http"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/middleware"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/go-chi/chi/middleware"
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var router *chi.Mux
|
|
||||||
var db *sql.DB
|
|
||||||
|
|
||||||
const (
|
|
||||||
dbName = "go-mysql-crud"
|
|
||||||
dbPass = ""
|
|
||||||
dbHost = "localhost"
|
|
||||||
dbPort = "3306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func routers() *chi.Mux {
|
|
||||||
router.Get("/posts", AllPosts)
|
|
||||||
router.Get("/posts/{id}", DetailPost)
|
|
||||||
router.Post("/posts", CreatePost)
|
|
||||||
router.Put("/posts/{id}", UpdatePost)
|
|
||||||
router.Delete("/posts/{id}", DeletePost)
|
|
||||||
|
|
||||||
return router
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
router = chi.NewRouter()
|
|
||||||
router.Use(middleware.Recoverer)
|
|
||||||
|
|
||||||
dbSource := fmt.Sprintf("root:%s@tcp(%s:%s)/%s?charset=utf8", dbPass, dbHost, dbPort, dbName)
|
|
||||||
fmt.Println(dbSource)
|
|
||||||
var err error
|
|
||||||
db, err = sql.Open("mysql", dbSource)
|
|
||||||
|
|
||||||
catch(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post ...
|
|
||||||
type Post struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreatePost ...
|
|
||||||
func CreatePost(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var post Post
|
|
||||||
json.NewDecoder(r.Body).Decode(&post)
|
|
||||||
|
|
||||||
query, err := db.Prepare("Insert posts SET title=?, content=?")
|
|
||||||
catch(err)
|
|
||||||
|
|
||||||
_, er := query.Exec(post.Title, post.Content)
|
|
||||||
catch(er)
|
|
||||||
defer query.Close()
|
|
||||||
|
|
||||||
respondwithJSON(w, http.StatusCreated, map[string]string{"message": "successfully created"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePost ...
|
|
||||||
func UpdatePost(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var post Post
|
|
||||||
id := chi.URLParam(r, "id")
|
|
||||||
json.NewDecoder(r.Body).Decode(&post)
|
|
||||||
|
|
||||||
query, err := db.Prepare("Update posts set title=?, content=? where id=?")
|
|
||||||
catch(err)
|
|
||||||
_, er := query.Exec(post.Title, post.Content, id)
|
|
||||||
catch(er)
|
|
||||||
|
|
||||||
defer query.Close()
|
|
||||||
|
|
||||||
respondwithJSON(w, http.StatusOK, map[string]string{"message": "update successfully"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeletePost ...
|
|
||||||
func DeletePost(w http.ResponseWriter, r *http.Request) {
|
|
||||||
id := chi.URLParam(r, "id")
|
|
||||||
|
|
||||||
query, err := db.Prepare("delete from post where id=?")
|
|
||||||
catch(err)
|
|
||||||
_, er := query.Exec(id)
|
|
||||||
catch(er)
|
|
||||||
query.Close()
|
|
||||||
|
|
||||||
respondwithJSON(w, http.StatusOK, map[string]string{"message": "deleted successfully"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllPosts ...
|
|
||||||
func AllPosts(w http.ResponseWriter, r *http.Request) {
|
|
||||||
errors := []error{}
|
|
||||||
payload := []Post{}
|
|
||||||
|
|
||||||
rows, err := db.Query("Select id, title, content From posts")
|
|
||||||
catch(err)
|
|
||||||
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
data := Post{}
|
|
||||||
|
|
||||||
er := rows.Scan(&data.ID, &data.Title, &data.Content)
|
|
||||||
|
|
||||||
if er != nil {
|
|
||||||
errors = append(errors, er)
|
|
||||||
}
|
|
||||||
payload = append(payload, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
respondwithJSON(w, http.StatusOK, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetailPost ...
|
|
||||||
func DetailPost(w http.ResponseWriter, r *http.Request) {
|
|
||||||
payload := Post{}
|
|
||||||
id := chi.URLParam(r, "id")
|
|
||||||
|
|
||||||
row := db.QueryRow("Select if, title, content From posts where id=?", id)
|
|
||||||
|
|
||||||
err := row.Scan(
|
|
||||||
&payload.ID,
|
|
||||||
&payload.Title,
|
|
||||||
&payload.Content,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
respondWithError(w, http.StatusNotFound, "no rows in result set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
respondwithJSON(w, http.StatusOK, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
func main() {
|
func main() {
|
||||||
routers()
|
dbName := os.Getenv("DB_NAME")
|
||||||
http.ListenAndServe(":8005", Logger())
|
dbPass := os.Getenv("DB_PASS")
|
||||||
|
dbHost := os.Getenv("DB_HOST")
|
||||||
|
dbPort := os.Getenv("DB_PORT")
|
||||||
|
|
||||||
|
println("this is db")
|
||||||
|
|
||||||
|
connection, err := driver.ConnectSQL(dbHost, dbPort, "root", dbPass, dbName)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(middleware.Recoverer)
|
||||||
|
r.Use(middleware.Logger)
|
||||||
|
|
||||||
|
pHandler := ph.NewPostHandler(connection)
|
||||||
|
r.Get("/posts", pHandler.Fetch)
|
||||||
|
r.Get("/posts/{id}", pHandler.GetByID)
|
||||||
|
r.Post("/posts", pHandler.Create)
|
||||||
|
r.Put("/posts/{id}", pHandler.Update)
|
||||||
|
r.Delete("/posts/{id}", pHandler.Delete)
|
||||||
|
|
||||||
|
fmt.Println("Server listen at :8005")
|
||||||
|
http.ListenAndServe(":8005", r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNotFound ...
|
||||||
|
ErrNotFound = errors.New("requested item is not found")
|
||||||
|
)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
// Post ...
|
||||||
|
type Post struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package post
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
models "blog/models"
|
||||||
|
pRepo "blog/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSQLPostRepo ...
|
||||||
|
func NewSQLPostRepo(Conn *sql.DB) pRepo.PostRepo {
|
||||||
|
return &mysqlPostRepo{
|
||||||
|
Conn: Conn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mysqlPostRepo struct {
|
||||||
|
Conn *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch ...
|
||||||
|
func (m *mysqlPostRepo) fetch(ctx context.Context, query string, args ...interface{}) ([]*models.Post, error) {
|
||||||
|
rows, err := m.Conn.QueryContext(ctx, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
payload := make([]*models.Post, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
data := new(models.Post)
|
||||||
|
err := rows.Scan(
|
||||||
|
&data.ID,
|
||||||
|
&data.Title,
|
||||||
|
&data.Content,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload = append(payload, data)
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mysqlPostRepo) Fetch(ctx context.Context, num int64) ([]*models.Post, error) {
|
||||||
|
query := "Select id, title, content From posts limit ?"
|
||||||
|
|
||||||
|
return m.fetch(ctx, query, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mysqlPostRepo) GetByID(ctx context.Context, id int64) (*models.Post, error) {
|
||||||
|
query := "Select id, title, content From posts where id=?"
|
||||||
|
|
||||||
|
rows, err := m.fetch(ctx, query, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload := &models.Post{}
|
||||||
|
if len(rows) > 0 {
|
||||||
|
payload = rows[0]
|
||||||
|
} else {
|
||||||
|
return nil, models.ErrNotFound
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mysqlPostRepo) Create(ctx context.Context, p *models.Post) (int64, error) {
|
||||||
|
query := "Insert posts SET title=?, content=?"
|
||||||
|
|
||||||
|
stmt, err := m.Conn.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := stmt.ExecContext(ctx, p.Title, p.Content)
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return res.LastInsertId()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mysqlPostRepo) Update(ctx context.Context, p *models.Post) (*models.Post, error) {
|
||||||
|
query := "Update posts set title=?, content=? where id=?"
|
||||||
|
|
||||||
|
stmt, err := m.Conn.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = stmt.ExecContext(
|
||||||
|
ctx,
|
||||||
|
p.Title,
|
||||||
|
p.Content,
|
||||||
|
p.ID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mysqlPostRepo) Delete(ctx context.Context, id int64) (bool, error) {
|
||||||
|
query := "Delete From posts Where id=?"
|
||||||
|
|
||||||
|
stmt, err := m.Conn.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
_, err = stmt.ExecContext(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"blog/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PostRepo explain...
|
||||||
|
type PostRepo interface {
|
||||||
|
Fetch(ctx context.Context, num int64) ([]*models.Post, error)
|
||||||
|
GetByID(ctx context.Context, id int64) (*models.Post, error)
|
||||||
|
Create(ctx context.Context, p *models.Post) (int64, error)
|
||||||
|
Update(ctx context.Context, p *models.Post) (*models.Post, error)
|
||||||
|
Delete(ctx context.Context, id int64) (bool, error)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user