1. Memasang alat ent

Untuk memasang alat generasi kode ent, Anda perlu mengikuti langkah-langkah berikut:

Pertama, pastikan sistem Anda sudah memiliki lingkungan bahasa Go terpasang. Kemudian, jalankan perintah berikut untuk mendapatkan alat ent:

go get -d entgo.io/ent/cmd/ent

Perintah ini akan mengunduh kode untuk alat ent, namun tidak akan segera mengkompilasi dan memasangnya. Jika Anda ingin memasang ent ke direktori $GOPATH/bin agar dapat menggunakannya di mana saja, Anda juga perlu menjalankan perintah berikut:

go install entgo.io/ent/cmd/ent

Setelah pemasangan selesai, Anda dapat memeriksa apakah alat ent terpasang dengan benar dan melihat perintah-perintah yang tersedia dengan menjalankan ent -h.

2. Menginisialisasi Skema

2.1 Menginisialisasi Templat Menggunakan ent init

Membuat file skema baru adalah langkah pertama untuk mulai menggunakan ent untuk generasi kode. Anda dapat menginisialisasi templat skema dengan menjalankan perintah berikut:

go run -mod=mod entgo.io/ent/cmd/ent new User Pet

Perintah ini akan membuat dua file skema baru: user.go dan pet.go, dan menempatkannya di direktori ent/schema. Jika direktori ent belum ada, perintah ini juga akan membuatnya secara otomatis.

Menjalankan perintah ent init di direktori akar proyek adalah praktik yang baik, karena membantu menjaga struktur direktori proyek agar jelas.

2.2 Struktur File Skema

Di dalam direktori ent/schema, setiap skema sesuai dengan sebuah file sumber bahasa Go. File skema merupakan tempat di mana Anda mendefinisikan model database, termasuk bidang-bidang dan relasi (hubungan).

Sebagai contoh, di file user.go, Anda mungkin akan mendefinisikan model pengguna, termasuk bidang seperti nama pengguna dan usia, serta mendefinisikan hubungan antara pengguna dan hewan peliharaan. Begitu pula di file pet.go, Anda akan mendefinisikan model hewan peliharaan beserta bidang-bidang terkait, seperti nama hewan peliharaan, jenisnya, dan hubungan antara hewan peliharaan dan pengguna.

File-file ini pada akhirnya akan digunakan oleh alat ent untuk menghasilkan kode Go yang sesuai, termasuk kode klien untuk operasi-operasi database dan CRUD (Create, Read, Update, Delete).

// ent/schema/user.go
package schema

import (
    "entgo.io/ent"
    "entgo.io/ent/schema/field"
)

// User mendefinisikan skema untuk entitas Pengguna.
type User struct {
    ent.Schema
}

// Metode Fields digunakan untuk mendefinisikan bidang-bidang entitas.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").Unique(),
        field.Int("age").Positive(),
    }
}

// Metode Edges digunakan untuk mendefinisikan asosiasi-asosiasi entitas.
func (User) Edges() []ent.Edge {
    // Asosiasi akan dijelaskan lebih detail pada bagian selanjutnya.
}

File skema ent menggunakan tipe dan fungsi bahasa Go untuk mendeklarasikan struktur model database, termasuk bidang dan hubungan antara model, serta menggunakan API yang disediakan oleh kerangka kerja ent untuk mendefinisikan struktur-struktur ini. Pendekatan ini membuat ent sangat intuitif dan mudah untuk diperluas, sambil juga memanfaatkan ketahanan tipe dari bahasa Go.

3.1 Menjalankan Generasi Kode

Menjalankan ent generate untuk menghasilkan kode adalah langkah penting dalam kerangka kerja ent. Dengan perintah ini, ent akan menghasilkan file kode Go yang sesuai berdasarkan skema-skhema yang telah didefinisikan, memudahkan pekerjaan pengembangan selanjutnya. Perintah untuk menjalankan generasi kode sangatlah mudah:

go generate ./ent

Perintah di atas perlu dijalankan di dalam direktori akar proyek. Saat go generate dipanggil, ia akan mencari semua file Go yang berisi anotasi-anotasi spesifik dan menjalankan perintah-perintah yang ditentukan dalam anotasi tersebut. Dalam contoh kita, perintah ini menentukan generator kode untuk ent.

Pastikan inisialisasi skema dan penambahan bidang yang diperlukan telah selesai sebelum menjalankannya. Barulah kode yang dihasilkan akan mencakup semua bagian yang diperlukan.

3.2 Memahami Aset Kode yang Dihasilkan

Aset kode yang dihasilkan berisi beberapa komponen, masing-masing dengan fungsi yang berbeda:

  • Objek Klien dan Tx: Digunakan untuk berinteraksi dengan grafik data. Klien menyediakan metode untuk membuat transaksi (Tx) atau langsung melakukan operasi database.

  • CRUD builders: Untuk setiap tipe skema, ia menghasilkan pembuat untuk membuat, membaca, memperbarui, dan menghapus, menyederhanakan logika operasi entitas yang sesuai.

  • Objek Entitas (Go struct): Ini menghasilkan Go struct yang sesuai untuk setiap tipe dalam skema, memetakan struct ini ke tabel-tabel dalam database.

  • Package Konstan dan Predikat: Berisi konstan dan predikat untuk berinteraksi dengan pembuat.

  • Package Migrasi: Sebuah package untuk migrasi database, cocok untuk dialek-dialek SQL.

  • Package Hook: Menyediakan fungsionalitas untuk menambahkan middleware perubahan, memungkinkan logika kustom dieksekusi sebelum atau sesudah membuat, memperbarui, atau menghapus entitas.

Dengan memeriksa kode yang dihasilkan, Anda dapat memperoleh pemahaman yang lebih dalam tentang bagaimana kerangka kerja ent mengotomatisasi kode akses data untuk skema Anda.

4. Opsi Pembangkitan Kode

Perintah ent generate mendukung berbagai opsi untuk menyesuaikan proses pembangkitan kode. Anda dapat memeriksa semua opsi pembangkitan yang didukung melalui perintah berikut:

ent generate -h

Berikut adalah beberapa opsi baris perintah yang sering digunakan:

  • --fitur strings: Memperluas pembangkitan kode, menambahkan fungsionalitas tambahan.
  • --header string: Melakukan override pada file header pembangkitan kode.
  • --storage string: Menentukan driver penyimpanan yang didukung dalam pembangkitan kode, defaultnya adalah "sql".
  • --target string: Menentukan direktori target untuk pembangkitan kode.
  • --template strings: Menjalankan templat Go tambahan. Mendukung berkas, direktori, dan jalur wildcard, misalnya: --template file="path/to/file.tmpl".

Opsi-opsi ini memungkinkan pengembang untuk menyesuaikan proses pembangkitan kode mereka sesuai dengan berbagai kebutuhan dan preferensi.

5. Konfigurasi Opsi Penyimpanan

ent mendukung pembangkitan aset kode untuk dialek SQL dan Gremlin, dengan defaultnya adalah dialek SQL. Jika proyek memerlukan koneksi ke database Gremlin, dialek database yang sesuai perlu dikonfigurasi. Berikut adalah cara untuk menentukan opsi penyimpanan:

ent generate --storage gremlin ./ent/schema

Perintah di atas memerintahkan ent untuk menggunakan dialek Gremlin saat melakukan pembangkitan kode. Hal ini memastikan bahwa aset yang dihasilkan disesuaikan dengan persyaratan database Gremlin, memastikan kompatibilitas dengan database grafik tertentu.

6. Penggunaan Lanjutan: Paket entc

6.1 Penggunaan entc sebagai Paket dalam Proyek

entc adalah paket inti yang digunakan untuk pembangkitan kode dalam kerangka kerja ent. Selain dari alat baris perintah, entc juga dapat diintegrasikan ke dalam proyek sebagai sebuah paket, memungkinkan pengembang untuk mengendalikan dan menyesuaikan proses pembangkitan kode dalam kode itu sendiri.

Untuk menggunakan entc sebagai paket dalam proyek, Anda perlu membuat sebuah berkas bernama entc.go dan menambahkan konten berikut ke dalam berkas tersebut:

// +build ignore

package main

import (
    "log"
    "entgo.io/ent/entc"
    "entgo.io/ent/entc/gen"
)

func main() {
    if err := entc.Generate("./schema", &gen.Config{}); err != nil {
        log.Fatal("running ent codegen:", err)
    }
}

Dengan pendekatan ini, Anda dapat memodifikasi struktur gen.Config dalam fungsi main untuk menerapkan berbagai opsi konfigurasi. Dengan memanggil fungsi entc.Generate sesuai kebutuhan, Anda dapat mengendalikan proses pembangkitan kode secara fleksibel.

6.2 Konfigurasi Detail dari entc

entc menyediakan beragam opsi konfigurasi yang memungkinkan pengembang untuk menyesuaikan kode yang dihasilkan. Misalnya, hook kustom dapat dikonfigurasi untuk memeriksa atau memodifikasi kode yang dihasilkan, dan dependensi eksternal dapat dimasukkan menggunakan dependency injection.

Contoh berikut ini menunjukkan bagaimana menyediakan hook kustom untuk fungsi entc.Generate:

func main() {
    err := entc.Generate("./schema", &gen.Config{
        Hooks: []gen.Hook{
            HookFunction,
        },
    })
    if err != nil {
        log.Fatalf("error saat menjalankan ent codegen: %v", err)
    }
}

// HookFunction merupakan fungsi hook kustom
func HookFunction(next gen.Generator) gen.Generator {
    return gen.GenerateFunc(func(g *gen.Graph) error {
        // Dapat menangani mode graf yang diwakili oleh g di sini
        // Misalnya, memvalidasi keberadaan bidang atau memodifikasi struktur
        return next.Generate(g)
    })
}

Selain itu, dependensi eksternal dapat ditambahkan menggunakan entc.Dependency:

func main() {
    opts := []entc.Option{
        entc.Dependency(
            entc.DependencyType(&http.Client{}),
        ),
        entc.Dependency(
            entc.DependencyName("Writer"),
            entc.DependencyTypeInfo(&field.TypeInfo{
                Ident:   "io.Writer",
                PkgPath: "io",
            }),
        ),
    }
    if err := entc.Generate("./schema", &gen.Config{}, opts...); err != nil {
        log.Fatalf("error saat menjalankan ent codegen: %v", err)
    }
}

Dalam contoh ini, kami menyuntikkan http.Client dan io.Writer sebagai dependensi ke objek klien yang dihasilkan.

7. Output dari Deskripsi Skema

Dalam kerangka kerja ent, perintah ent describe dapat digunakan untuk menghasilkan deskripsi skema dalam format grafis. Hal ini dapat membantu pengembang memahami dengan cepat entitas dan hubungan yang ada.

Jalankan perintah berikut ini untuk mendapatkan deskripsi skema:

go run -mod=mod entgo.io/ent/cmd/ent describe ./ent/schema

Perintah di atas akan menghasilkan tabel yang mirip dengan berikut, menampilkan informasi seperti bidang, tipe, hubungan, dll. untuk setiap entitas:

User:
    +-------+---------+--------+-----------+ ...
    | Bidang|  Tipe   | Unik   | Opsional  | ...
    +-------+---------+--------+-----------+ ...
    | id    | int     | false  | false     | ...
    | name  | string  | true   | false     | ...
    +-------+---------+--------+-----------+ ...
    +-------+--------+---------+-----------+ ...
    | Edge  |  Tipe  | Invers  | Relasi    | ...
    +-------+--------+---------+-----------+ ...
    | pets  | Pet    | false   | O2M       | ...
    +-------+--------+---------+-----------+ ...

8. Hook untuk Generasi Kode

8.1 Konsep Hook

Hook adalah fungsi middleware yang dapat dimasukkan ke dalam proses generasi kode ent, memungkinkan logika kustom dimasukkan sebelum dan sesudah menghasilkan kode. Hook dapat digunakan untuk memanipulasi abstract syntax tree (AST) dari kode yang dihasilkan, melakukan validasi, atau menambahkan potongan kode tambahan.

8.2 Contoh Penggunaan Hook

Berikut adalah contoh penggunaan hook untuk memastikan bahwa semua bidang mengandung tag struktur tertentu (misalnya, json):

func main() {
    err := entc.Generate("./schema", &gen.Config{
        Hooks: []gen.Hook{
            EnsureStructTag("json"),
        },
    })
    if err != nil {
        log.Fatalf("error saat menjalankan ent codegen: %v", err)
    }
}

// EnsureStructTag memastikan bahwa semua bidang dalam graf mengandung tag struktur spesifik
func EnsureStructTag(name string) gen.Hook {
    return func(next gen.Generator) gen.Generator {
        return gen.GenerateFunc(func(g *gen.Graph) error {
            for _, node := range g.Nodes {
                for _, field := range node.Fields {
                    tag := reflect.StructTag(field.StructTag)
                    if _, ok := tag.Lookup(name); !ok {
                        return fmt.Errorf("tag struktur %q tidak ada untuk bidang %s.%s", name, node.Name, field.Name)
                    }
                }
            }
            return next.Generate(g)
        })
    }
}

Dalam contoh ini, sebelum menghasilkan kode, fungsi EnsureStructTag memeriksa setiap bidang untuk tag json. Jika suatu bidang tidak memiliki tag ini, maka generasi kode akan dihentikan dan menghasilkan error. Ini merupakan cara yang efektif untuk menjaga kebersihan dan konsistensi kode.