Browse Source

better hierarchy and better way to make a CRUD

master
Nicolas Dinquel 7 years ago
parent
commit
874fdd0e25
  1. 4
      .gitignore
  2. 39
      driver/driver.go
  3. 98
      handler/http/handler.go
  4. 154
      main.go
  5. 8
      models/errors.go
  6. 8
      models/post.go
  7. 118
      repository/post/post_mysql.go
  8. 16
      repository/repository.go

4
.gitignore

@ -1,7 +1,7 @@
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
vendor/*
blog

39
driver/driver.go

@ -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
}

98
handler/http/handler.go

@ -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})
}

154
main.go

@ -1,146 +1,42 @@
package main
import (
"database/sql"
"encoding/json"
"blog/driver"
ph "blog/handler/http"
"fmt"
"net/http"
"os"
"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"
"github.com/go-chi/chi"
)
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)
func main() {
dbName := os.Getenv("DB_NAME")
dbPass := os.Getenv("DB_PASS")
dbHost := os.Getenv("DB_HOST")
dbPort := os.Getenv("DB_PORT")
err := row.Scan(
&payload.ID,
&payload.Title,
&payload.Content,
)
println("this is db")
connection, err := driver.ConnectSQL(dbHost, dbPort, "root", dbPass, dbName)
if err != nil {
respondWithError(w, http.StatusNotFound, "no rows in result set")
return
fmt.Println(err)
os.Exit(-1)
}
respondwithJSON(w, http.StatusOK, payload)
}
r := chi.NewRouter()
r.Use(middleware.Recoverer)
r.Use(middleware.Logger)
//
func main() {
routers()
http.ListenAndServe(":8005", 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)
}

8
models/errors.go

@ -0,0 +1,8 @@
package models
import "errors"
var (
// ErrNotFound ...
ErrNotFound = errors.New("requested item is not found")
)

8
models/post.go

@ -0,0 +1,8 @@
package models
// Post ...
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}

118
repository/post/post_mysql.go

@ -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
}

16
repository/repository.go

@ -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)
}
Loading…
Cancel
Save