Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 54e64e0

Browse files
committed
shorter way of many-to-many
1 parent bb6d470 commit 54e64e0

File tree

4 files changed

+105
-11
lines changed

4 files changed

+105
-11
lines changed

‎db/sqlc/handle.go‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func Register(r *chi.Mux, db *sqlx.DB, dbType string) {
2828
router.Post("/", h.Create)
2929
router.Get("/", h.List)
3030
router.Get("/m2m", h.ListM2M)
31+
router.Get("/m2mOneQuery", h.ListM2MOneQuery)
3132
router.Get("/{userID}", h.Get)
3233
router.Put("/{userID}", h.Update)
3334
router.Delete("/{userID}", h.Delete)
@@ -39,7 +40,7 @@ func Register(r *chi.Mux, db *sqlx.DB, dbType string) {
3940
}
4041

4142
func (h *handler) Create(w http.ResponseWriter, r *http.Request) {
42-
request :=db.NewUserRequest()
43+
varrequest db.CreateUserRequest
4344
err := json.NewDecoder(r.Body).Decode(&request)
4445
if err != nil {
4546
respond.Error(w, http.StatusBadRequest, message.ErrBadRequest)
@@ -52,7 +53,7 @@ func (h *handler) Create(w http.ResponseWriter, r *http.Request) {
5253
return
5354
}
5455

55-
user, err := h.db.Create(r.Context(), request, hash)
56+
user, err := h.db.Create(r.Context(), &request, hash)
5657
var customErr *db.Err
5758
if err != nil {
5859
switch {
@@ -118,6 +119,8 @@ func (h *handler) Get(w http.ResponseWriter, r *http.Request) {
118119
}
119120

120121
func (h *handler) Update(w http.ResponseWriter, r *http.Request) {
122+
f := db.Filters(r.URL.Query())
123+
121124
userID, err := param.Int64(r, "userID")
122125
if err != nil {
123126
respond.Error(w, http.StatusBadRequest, param.ErrParam)
@@ -131,13 +134,13 @@ func (h *handler) Update(w http.ResponseWriter, r *http.Request) {
131134
return
132135
}
133136

134-
if req.FirstName == "" || req.MiddleName==""||req.LastName == "" ||
137+
if req.FirstName == "" || req.LastName == "" ||
135138
req.Email == "" || req.FavouriteColour == "" {
136139
respond.Error(w, http.StatusBadRequest, errors.New("required field(s) is/are empty"))
137140
return
138141
}
139142

140-
updated, err := h.db.Update(r.Context(), userID, &req)
143+
updated, err := h.db.Update(r.Context(), userID, f, &req)
141144
if err != nil {
142145
respond.Error(w, http.StatusInternalServerError, err)
143146
return
@@ -187,3 +190,13 @@ func (h *handler) ListM2M(w http.ResponseWriter, r *http.Request) {
187190

188191
respond.Json(w, http.StatusOK, users)
189192
}
193+
194+
func (h *handler) ListM2MOneQuery(w http.ResponseWriter, r *http.Request) {
195+
users, err := h.db.ListM2MOneQuery(r.Context())
196+
if err != nil {
197+
respond.Error(w, http.StatusBadRequest, err)
198+
return
199+
}
200+
201+
respond.Json(w, http.StatusOK, users)
202+
}

‎db/sqlc/manyToMany.go‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@ package sqlc
22

33
import (
44
"context"
5+
"database/sql"
6+
"errors"
57
"fmt"
8+
"log"
9+
"net/http"
610

711
"godb/db"
812
"godb/db/sqlc/pg"
13+
"godb/respond/message"
914
)
1015

1116
func (r *database) ListM2M(ctx context.Context) ([]*db.UserResponseWithAddressesSqlx, error) {
@@ -64,6 +69,34 @@ func (r *database) ListM2M(ctx context.Context) ([]*db.UserResponseWithAddresses
6469
return all, nil
6570
}
6671

72+
func (r *database) ListM2MOneQuery(ctx context.Context) ([]*db.UserResponseWithAddressesSqlxSingleQuery, error) {
73+
dbResponse, err := r.db.ListM2MOneQuery(ctx)
74+
if err != nil {
75+
if errors.Is(err, sql.ErrNoRows) {
76+
return nil, &db.Err{Msg: message.ErrRecordNotFound.Error(), Status: http.StatusNotFound}
77+
}
78+
log.Println(err)
79+
return nil, &db.Err{Msg: message.ErrInternalError.Error(), Status: http.StatusInternalServerError}
80+
}
81+
82+
resp := make([]*db.UserResponseWithAddressesSqlxSingleQuery, 0)
83+
84+
for _, dbRow := range dbResponse {
85+
row := &db.UserResponseWithAddressesSqlxSingleQuery{
86+
ID: uint(dbRow.ID),
87+
FirstName: dbRow.FirstName,
88+
MiddleName: dbRow.MiddleName.String,
89+
LastName: dbRow.LastName,
90+
Email: dbRow.Email,
91+
FavouriteColour: string(dbRow.FavouriteColour),
92+
Address: dbRow.Addresses,
93+
}
94+
resp = append(resp, row)
95+
}
96+
97+
return resp, nil
98+
}
99+
67100
func getUserIDs(users []pg.SelectUsersRow) (ids []int32) {
68101
seen := make(map[int]bool)
69102
for _, user := range users {

‎db/sqlx/handler.go‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func Register(r *chi.Mux, db *sqlx.DB, dbType string) {
3737
router.Post("/", h.Create)
3838
router.Get("/", h.List)
3939
router.Get("/m2m", h.ListM2M)
40+
router.Get("/m2mOneQuery", h.ListM2MOneQuery)
4041
router.Get("/{userID}", h.Get)
4142
router.Get("/{userID}/address", h.Countries)
4243
router.Put("/{userID}", h.Update)
@@ -52,22 +53,22 @@ func (h *handler) Create(w http.ResponseWriter, r *http.Request) {
5253
// 1. real-world application must perform request validation
5354

5455
// 2. Transform into request struct
55-
request :=db.NewUserRequest()
56+
varrequest db.CreateUserRequest
5657
err := json.NewDecoder(r.Body).Decode(&request)
5758
if err != nil {
5859
respond.Error(w, http.StatusBadRequest, message.ErrBadRequest)
5960
return
6061
}
6162

62-
// Some business logic
63+
// Some business logic, preferably in its own layer.
6364
hash, err := argon2id.CreateHash(request.Password, argon2id.DefaultParams)
6465
if err != nil {
6566
respond.Error(w, http.StatusInternalServerError, message.ErrInternalError)
6667
return
6768
}
6869

69-
// 3. Call data access layer.
70-
u, err := h.db.Create(r.Context(), request, hash)
70+
// 3. Call data access layer. Preferably called from business logic layer.
71+
u, err := h.db.Create(r.Context(), &request, hash)
7172
if err != nil {
7273
var errStruct *db.Err
7374
if errors.As(err, &errStruct) {
@@ -144,7 +145,7 @@ func (h *handler) Update(w http.ResponseWriter, r *http.Request) {
144145
return
145146
}
146147

147-
if req.FirstName == "" || req.MiddleName==""||req.LastName == "" ||
148+
if req.FirstName == "" || req.LastName == "" ||
148149
req.Email == "" || req.FavouriteColour == "" {
149150
respond.Error(w, http.StatusBadRequest, errors.New("required field(s) is/are empty"))
150151
return
@@ -211,3 +212,13 @@ func (h *handler) ListM2M(w http.ResponseWriter, r *http.Request) {
211212

212213
respond.Json(w, http.StatusOK, users)
213214
}
215+
216+
func (h *handler) ListM2MOneQuery(w http.ResponseWriter, r *http.Request) {
217+
users, err := h.db.ListM2MOneQuery(r.Context())
218+
if err != nil {
219+
respond.Error(w, http.StatusBadRequest, err)
220+
return
221+
}
222+
223+
respond.Json(w, http.StatusOK, users)
224+
}

‎db/sqlx/manyToMany.go‎

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ package sqlx
33
import (
44
"context"
55
"database/sql"
6+
"errors"
67
"fmt"
8+
"log"
9+
"net/http"
710

811
"github.com/jmoiron/sqlx"
912

1013
"godb/db"
14+
"godb/respond/message"
1115
)
1216

1317
const (
@@ -19,7 +23,7 @@ const (
1923
WHERE u.id = 1ドル`
2024

2125
QueryUsers = `
22-
SELECT u.id, u.first_name, u.middle_name, u.last_name, u.email
26+
SELECT u.id, u.first_name, u.middle_name, u.last_name, u.email, u.updated_at
2327
FROM "users" u
2428
LIMIT 30;
2529
`
@@ -51,7 +55,7 @@ func (r *repository) ListM2M(ctx context.Context) ([]*db.UserResponseWithAddress
5155
var all []*db.UserResponseWithAddressesSqlx
5256
for users.Next() {
5357
var u db.UserDB
54-
if err := users.Scan(&u.ID, &u.FirstName, &u.MiddleName, &u.LastName, &u.Email); err != nil {
58+
if err := users.Scan(&u.ID, &u.FirstName, &u.MiddleName, &u.LastName, &u.Email, &u.UpdatedAt); err != nil {
5559
return nil, fmt.Errorf("db scanning error")
5660
}
5761
all = append(all, &db.UserResponseWithAddressesSqlx{
@@ -133,6 +137,39 @@ func (r *repository) ListM2M(ctx context.Context) ([]*db.UserResponseWithAddress
133137
return all, nil
134138
}
135139

140+
func (r *repository) ListM2MOneQuery(ctx context.Context) ([]*db.UserResponseWithAddressesSqlxSingleQuery, error) {
141+
m2mQuery := `SELECT u.id,
142+
u.first_name,
143+
u.middle_name,
144+
u.last_name,
145+
u.email,
146+
u.favourite_colour,
147+
array_to_json(array_agg(row_to_json(a.*))) AS addresses
148+
FROM addresses a
149+
INNER JOIN user_addresses ua ON ua.address_id = a.id
150+
INNER JOIN users u on u.id = ua.user_id
151+
GROUP BY u.id;
152+
`
153+
var res []*db.UserResponseWithAddressesSqlxSingleQuery
154+
155+
rows, err := r.db.QueryContext(ctx, m2mQuery)
156+
if err != nil {
157+
return nil, err
158+
}
159+
defer rows.Close()
160+
161+
err = r.db.SelectContext(ctx, &res, m2mQuery)
162+
if err != nil {
163+
if errors.Is(err, sql.ErrNoRows) {
164+
return nil, &db.Err{Msg: message.ErrRecordNotFound.Error(), Status: http.StatusNotFound}
165+
}
166+
log.Println(err)
167+
return nil, &db.Err{Msg: message.ErrInternalError.Error(), Status: http.StatusInternalServerError}
168+
}
169+
170+
return res, nil
171+
}
172+
136173
func getAddressIDs(uas []*userAddress) (ids []int) {
137174
seen := make(map[int]bool)
138175
for _, item := range uas {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /