Go の Gin と GORM で API を作る(1)

Qiita に書いた記事「Go の Gin と GORM で最低限の掲示板を作るチュートリアル」で作った掲示板を改修していきたいと思います。今回は articles テーブルにCRUDするAPIを作っていきたいと思います。この記事では、DB articles テーブルに対するCRUD処理を書く repository/article.go 修正をします。

package repository

import (
    "gorm.io/gorm"
    "time"
)

// Article 記事用の struct
// gorm.Model 構造体を使えば ID, CreatedAt, UpdatedAt, DeletedAt の記述は省略できる
// nullable なカラムについてはポインタ型を指定
type Article struct {
    ID        int
    Name      *string
    Body      string
    CreatedAt *time.Time
    UpdatedAt *time.Time
    DeletedAt gorm.DeletedAt // 論理削除できるよう型を gorm.DeletedAt に変更
    //gorm.Model
}

// GetArticles DBから記事を取得する
func GetArticles(limit int, offset int) ([]Article, *gorm.DB, error) {
    // gorm.DB を取得
    db, err := CreateDB()
    if err != nil {
        return nil, nil, err
    }

    var articles []Article
    // 実行
    // 最新の投稿から表示したいので id の降順で並べ替えておく
    // limit, offset を追加してページネーションに対応
    result := db.Limit(limit).Offset(offset).Order("id desc").Find(&articles)
    if result.Error != nil {
        return nil, nil, result.Error
    }

    return articles, result, nil
}

// CreateArticle DBに記事を保存する
func CreateArticle(name string, body string) (*Article, *gorm.DB, error) {
    // 将来的に Article の中身(カラム)が増えてくれば、引数は中身をひとまとめにした struct が良さそう

    // gorm.DB を取得
    db, err := CreateDB()
    if err != nil {
        return nil, nil, err
    }

    // DBへ投入するデータを作成 エスケープしたものを入れる
    name = template.HTMLEscapeString(name)
    body = template.HTMLEscapeString(body)
    article := Article{
        Name: &name,
        Body: body,
    }

    // 実行
    result := db.Create(&article)
    if result.Error != nil {
        return nil, nil, result.Error
    }

    return &article, result, nil
}

// UpdateArticle 記事を更新する
func UpdateArticle(id int, name string, body string) (*Article, *gorm.DB, error) {
    // gorm.DB を取得
    db, err := CreateDB()
    if err != nil {
        return nil, nil, err
    }

    // 更新するデータを作成 エスケープしたものを使う
    name = template.HTMLEscapeString(name)
    body = template.HTMLEscapeString(body)
    article := Article{
        ID:   id,
        Name: &name,
        Body: body,
    }

    // 実行
    result := db.Save(&article)
    if result.Error != nil {
        return nil, nil, result.Error
    }

    return &article, result, nil
}

// DeleteArticle 記事を削除する
func DeleteArticle(id int) (*gorm.DB, error) {
    // gorm.DB を取得
    db, err := CreateDB()
    if err != nil {
        return nil, err
    }

    // 実行
    article := Article{ID: id}
    result := db.Delete(&article)
    if result.Error != nil {
        return nil, result.Error
    }

    return result, nil
}

UpdateArticle() DeleteArticle() 関数を追加しました。GOAMの書き方そのままですね。削除は論理削除です。次回は handler 側を編集して repository/article.go に作った関数を使いAPIを作成していきます。

【2023/06/18 追記】
エラーハンドリングを修正、Name と Body の内容はエスケープしてから保存するように修正しました。