はじめに
前記事の続きでデータの作成、更新、削除の処理を追加しました。
実装
各処理の実装内容について記述します。
削除(Delete)
まずはroutes.go
にルートハンドラーを追加します。
router.HandlerFunc(http.MethodGet, "/v1/admin/deletemovie/:id", app.deleteMovie)
処理の内容をmovie-handlers.go
に記述します。
パスパラメータを取得するとき、strconv.Atoi(params.ByName("id"))
でidの型をStringからIntに変換することに注意します。
func (app *application) deleteMovie(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context())
// パスパラメータのidをIntに変換する
id, err := strconv.Atoi(params.ByName("id"))
if err != nil {
app.errorJSON(w, err)
return
}
// データの削除処理を行う
err = app.models.DB.DeleteMovie(id)
if err != nil {
app.errorJSON(w, err)
return
}
ok := jsonResp{
OK: true,
}
err = app.writeJSON(w, http.StatusOK, ok, "response")
if err != nil {
app.errorJSON(w, err)
return
}
}
DBデータの削除処理DeleteMovie()
はmovies-db.go
に記述します。
データの取得処理にはQueryContext()
を使用していましたが、データを変更する処理(削除、更新など)を行う場合にはexecContext()
を使用します。
func (m *DBModel) DeleteMovie(id int) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
stmt := "delete from movies where id = $1"
_, err := m.DB.ExecContext(ctx, stmt, id)
if err != nil {
return err
}
return nil
}
作成(Create)/更新(Update)
データの作成と更新については、既存のデータを編集するかどうかの違いだけで処理自体はほぼ同じです。
そのため、ルートハンドラーは一つにまとめてしまいます。
router.HandlerFunc(http.MethodPost, "/v1/admin/editmovie", app.editMovie)
削除のときと同様に、処理の中身はmovie-handler.go
に記述します。
まずはリクエストデータの型をMoviePayload
という構造体で定義します。
payload
という構造体を定義し、json.NewDecoder(r.Body).Decode(&payload)
でリクエストデータの中身r.Body
をpayload
に入れます。
リクエストデータのレコードをDBに登録(作成)するために、movie.ID, _ = strconv.Atoi(payload.ID)
のように、payload
のプロパティをmovieテーブルのカラムと同じ型に変換していきます。
そして、movie.ID == 0
のときにレコード作成処理InsertMovie()
、それ以外のときに更新処理UpdateMovie()
を行うようにします。
// JSONと同じ構造の構造体の型を定義する(入力値の型とDBのカラムの型は異なる)
type MoviePayload struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Year string `json:"year"`
ReleaseDate string `json:"release_date"`
Runtime string `json:"runtime"`
Rating string `json:"rating"`
MPAARating string `json:"mpaa_rating"`
}
func (app *application) editMovie(w http.ResponseWriter, r *http.Request) {
// リクエストデータの型をもつ構造体を定義する
var payload MoviePayload
// JSONオブジェクトを読み込んでpayloadに代入する
err := json.NewDecoder(r.Body).Decode(&payload)
if err != nil {
log.Println(err)
app.errorJSON(w, err)
return
}
// DBデータの型をもつ構造体を定義する
var movie models.Movie
// データ更新時にUpdatedAtを更新する
if payload.ID != "0" {
id, _ := strconv.Atoi(payload.ID)
m, _ := app.models.DB.Get(id)
movie = *m
movie.UpdatedAt = time.Now()
}
// payloadの各プロパティの型を変換してmovieに代入する
movie.ID, _ = strconv.Atoi(payload.ID)
movie.Title = payload.Title
movie.Description = payload.Description
movie.ReleaseDate, _ = time.Parse("2006-01-02", payload.ReleaseDate)
movie.Year = movie.ReleaseDate.Year()
movie.Runtime, _ = strconv.Atoi(payload.Runtime)
movie.Rating, _ = strconv.Atoi(payload.Rating)
movie.MPAARating = payload.MPAARating
movie.CreatedAt = time.Now()
movie.UpdatedAt = time.Now()
if movie.ID == 0 { // データ作成時の処理
err = app.models.DB.InsertMovie(movie)
if err != nil {
app.errorJSON(w, err)
return
}
} else { // データ更新時の処理
err = app.models.DB.UpdateMovie(movie)
if err != nil {
app.errorJSON(w, err)
return
}
}
ok := jsonResp{
OK: true,
}
err = app.writeJSON(w, http.StatusOK, ok, "response")
if err != nil {
app.errorJSON(w, err)
return
}
}
各処理の中身は以下のようになります。
func (m *DBModel) InsertMovie(movie Movie) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
stmt := `insert into movies (title, description, year, release_date, runtime, rating, mpaa_rating, created_at, updated_at) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)`
_, err := m.DB.ExecContext(ctx, stmt,
movie.Title,
movie.Description,
movie.Year,
movie.ReleaseDate,
movie.Runtime,
movie.Rating,
movie.MPAARating,
movie.CreatedAt,
movie.UpdatedAt,
)
if err != nil {
return err
}
return nil
}
func (m *DBModel) UpdateMovie(movie Movie) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
stmt := `update movies set title = $1, description = $2, year = $3, release_date = $4,
runtime = $5, rating = $6, mpaa_rating = $7,
updated_at = $8 where id = $9`
_, err := m.DB.ExecContext(ctx, stmt,
movie.Title,
movie.Description,
movie.Year,
movie.ReleaseDate,
movie.Runtime,
movie.Rating,
movie.MPAARating,
movie.UpdatedAt,
movie.ID,
)
if err != nil {
return err
}
return nil
}
コード全文
以下をご覧ください。
参考資料