Melanjutkan dari modul sebelumnya, pada modul ini kita akan latihan membuat fungsi untuk mengambil seluruh data anime.
Tentu saja jika data anime besar, pendekatan ini kurang tepat. Nanti akan kita perbaiki di modul lainnya. Untuk saat ini coba buat fungsi sederhana untuk retrieve all anime data.
Solusi
Buka file cmd/api/animeModel.go lalu tambahkan fungsi berikut
func (m *AnimeModel) All() ([]*Anime, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
query := `SELECT id, title, description, year
FROM animes ORDER BY title
`
rows, err := m.DB.QueryContext(ctx, query)
if err != nil {
return nil, err
}
defer rows.Close()
var animes []*Anime
for rows.Next() {
var anime Anime
err = rows.Scan(
&anime.Id,
&anime.Title,
&anime.Desc,
&anime.Year,
)
if err != nil {
return nil, err
}
query2 := `SELECT ag.id, ag.anime_id, ag.genre_id, g.genre_name
FROM animes_genres ag
LEFT JOIN genres g ON (g.id = ag.id)
WHERE ag.anime_id = $1
`
rows2, _ := m.DB.QueryContext(ctx, query2, anime.Id)
genre := make(map[int]string)
for rows2.Next() {
var ag animeGenre
err = rows2.Scan(
&ag.Id,
&ag.AnimeId,
&ag.GenreId,
&ag.Genre.GenreName,
)
if err != nil {
return nil, err
}
genre[ag.Id] = ag.Genre.GenreName
}
rows2.Close()
anime.AnimeGenre = genre
animes = append(animes, &anime)
}
return animes, nil
}
Kemudian buka file cmd/api/animesHandlers.go dan tambahkan fungsi berikut
func (app *application) getAnimes(rw http.ResponseWriter, r *http.Request) {
animes, err := app.models.Animes.All()
if err != nil {
app.errorJSON(rw, err)
return
}
err = app.writeJSON(rw, http.StatusOK, animes, "animes")
if err != nil {
app.errorJSON(rw, err)
}
}
Jalankan aplikasi go, lalu buka route http://localhost:4000/v1/animes untuk menampilkan seluruh data anime. Sesuai ekspektasi, seluruh data anime berhasil di retrieve.
{
"animes":[
{
"id":2,
"title":"Barakamon",
"desc":"Young temperamental caligrapher",
"year":2019,
"genre":{
}
},
{
"id":3,
"title":"New Game",
"desc":"New Game Anime",
"year":2008,
"genre":{
}
},
{
"id":4,
"title":"Nichijou",
"desc":"Daily life ",
"year":2000,
"genre":{
}
},
{
"id":1,
"title":"Yuru Camp",
"desc":"All about luxury camping",
"year":2020,
"genre":{
"1":"SOL"
}
}
]
}
Berikut isi lengkap cmd/api/animeModel.go
package models
import (
"context"
"database/sql"
"time"
)
type AnimeModel struct {
DB *sql.DB
}
func (m *AnimeModel) Get(id int) (*Anime, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
query := `SELECT id, title, description, year
FROM animes WHERE id = $1
`
row := m.DB.QueryRowContext(ctx, query, id)
var anime Anime
err := row.Scan(
&anime.Id,
&anime.Title,
&anime.Desc,
&anime.Year,
)
if err != nil {
return nil, err
}
query = `SELECT ag.id, ag.anime_id, ag.genre_id, g.genre_name
FROM animes_genres ag
LEFT JOIN genres g ON (g.id = ag.id)
WHERE ag.anime_id = $1
`
rows, _ := m.DB.QueryContext(ctx, query, id)
genre := make(map[int]string)
for rows.Next() {
var ag animeGenre
err := rows.Scan(
&ag.Id,
&ag.AnimeId,
&ag.GenreId,
&ag.Genre.GenreName,
)
if err != nil {
return nil, err
}
genre[ag.Id] = ag.Genre.GenreName
}
anime.AnimeGenre = genre
return &anime, nil
}
func (m *AnimeModel) All() ([]*Anime, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
query := `SELECT id, title, description, year
FROM animes ORDER BY title
`
rows, err := m.DB.QueryContext(ctx, query)
if err != nil {
return nil, err
}
defer rows.Close()
var animes []*Anime
for rows.Next() {
var anime Anime
err = rows.Scan(
&anime.Id,
&anime.Title,
&anime.Desc,
&anime.Year,
)
if err != nil {
return nil, err
}
query2 := `SELECT ag.id, ag.anime_id, ag.genre_id, g.genre_name
FROM animes_genres ag
LEFT JOIN genres g ON (g.id = ag.id)
WHERE ag.anime_id = $1
`
rows2, _ := m.DB.QueryContext(ctx, query2, anime.Id)
genre := make(map[int]string)
for rows2.Next() {
var ag animeGenre
err = rows2.Scan(
&ag.Id,
&ag.AnimeId,
&ag.GenreId,
&ag.Genre.GenreName,
)
if err != nil {
return nil, err
}
genre[ag.Id] = ag.Genre.GenreName
}
rows2.Close()
anime.AnimeGenre = genre
animes = append(animes, &anime)
}
return animes, nil
}
isi lengkap file cmd/api/animesHandlers.go
package main
import (
"errors"
"net/http"
"strconv"
"github.com/julienschmidt/httprouter"
)
func (app *application) getAnime(rw http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context())
id, err := strconv.Atoi(params.ByName("id"))
if err != nil {
app.logger.Println(errors.New("invalid id"))
app.errorJSON(rw, err)
return
}
app.logger.Println(id)
anime, err := app.models.Animes.Get(id)
if err != nil {
app.errorJSON(rw, err)
return
}
err = app.writeJSON(rw, http.StatusOK, anime, "anime")
if err != nil {
app.errorJSON(rw, err)
return
}
}
func (app *application) getAnimes(rw http.ResponseWriter, r *http.Request) {
animes, err := app.models.Animes.All()
if err != nil {
app.errorJSON(rw, err)
return
}
err = app.writeJSON(rw, http.StatusOK, animes, "animes")
if err != nil {
app.errorJSON(rw, err)
}
}