1. Установка инструмента ent

Для установки инструмента генерации кода ent вам необходимо выполнить следующие шаги:

Сначала убедитесь, что на вашей системе установлена среда языка Go. Затем выполните следующую команду, чтобы получить инструмент ent:

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

Эта команда загрузит код инструмента ent, но не будет немедленно его компилировать и устанавливать. Если вы хотите установить ent в каталог $GOPATH/bin, чтобы иметь возможность использовать его в любом месте, вам также нужно выполнить следующую команду:

go install entgo.io/ent/cmd/ent

Как только установка будет завершена, вы можете проверить, корректно ли установлен инструмент ent, и просмотреть доступные команды и инструкции, выполнив ent -h.

2. Инициализация схемы

2.1 Инициализация шаблона с помощью ent init

Создание нового файла схемы - первый шаг для начала использования ent для генерации кода. Вы можете инициализировать шаблон схемы, выполнив следующую команду:

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

Эта команда создаст два новых файла схемы: user.go и pet.go, и поместит их в каталог ent/schema. Если каталог ent не существует, эта команда также автоматически его создаст.

Запуск команды ent init в корневом каталоге проекта является хорошей практикой, так как это помогает поддерживать структуру и ясность каталога проекта.

2.2 Структура файла схемы

В каталоге ent/schema каждая схема соответствует исходному файлу на языке Go. Файлы схем - это место, где вы определяете модель базы данных, включая поля и связи (отношения).

Например, в файле user.go вы можете определить модель пользователя, включая поля, такие как имя пользователя и возраст, и определить отношение между пользователями и питомцами. Аналогично, в файле pet.go вы можете определить модель питомца и связанные с ним поля, такие как имя питомца, тип, и отношение между питомцами и пользователями.

Эти файлы в конечном итоге будут использоваться инструментом ent для генерации соответствующего кода на языке Go, включая клиентский код для операций с базой данных CRUD (Create, Read, Update, Delete).

// ent/schema/user.go
package schema

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

// User определяет схему для сущности User.
type User struct {
    ent.Schema
}

// Метод Fields используется для определения полей сущности.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").Unique(),
        field.Int("age").Positive(),
    }
}

// Метод Edges используется для определения ассоциаций сущности.
func (User) Edges() []ent.Edge {
    // Ассоциации будут подробно объяснены в следующем разделе.
}

Файлы схем ent используют типы и функции языка Go для объявления структуры модели базы данных, включая поля и отношения между моделями, и используют API, предоставляемый фреймворком ent, для определения этих структур. Такой подход делает ent очень интуитивно понятным и легким в расширении, используя при этом сильную типизацию языка Go.

3.1 Запуск генерации кода

Запуск ent generate для генерации кода является важным шагом в фреймворке ent. С помощью этой команды ent сгенерирует соответствующие файлы кода на языке Go на основе определенных схем, упрощая последующую разработку. Команда для выполнения генерации кода проста:

go generate ./ent

Вышеуказанную команду необходимо выполнять в корневом каталоге проекта. При вызове go generate он будет искать все файлы на языке Go, содержащие определенные аннотации, и выполнять команды, указанные в аннотациях. В нашем примере данная команда указывает генератор кода для ent.

Убедитесь, что инициализация схемы и необходимое добавление полей завершены перед выполнением. Только в этом случае сгенерированный код будет содержать все необходимые части.

3.2 Понимание сгенерированных кодовых ресурсов

Сгенерированные кодовые ресурсы содержат несколько компонентов, каждый из которых обладает различными функциями:

  • Объекты Client и Tx: Используются для взаимодействия с графом данных. Client предоставляет методы для создания транзакций (Tx) или непосредственного выполнения операций с базой данных.

  • Генераторы CRUD: Для каждого типа схемы генерируются средства создания, чтения, обновления и удаления, упрощая логику операций соответствующей сущности.

  • Объект сущности (Go struct): Для каждого типа в схеме генерируются соответствующие Go-структуры, отображающие эти структуры на таблицы в базе данных.

  • Пакеты констант и предикатов: Содержат константы и предикаты для взаимодействия с генераторами.

  • Пакет миграции: Пакет для миграции базы данных, подходящий для диалектов SQL.

  • Пакет хуков: Предоставляет функциональность для добавления промежуточного ПО изменений, позволяя выполнить пользовательскую логику перед или после создания, обновления или удаления сущностей.

При изучении сгенерированного кода вы можете получить более глубокое понимание того, как фреймворк ent автоматизирует доступ к данным для ваших схем.

4. Опции генерации кода

Команда ent generate поддерживает различные опции для настройки процесса генерации кода. Вы можете запросить все поддерживаемые опции генерации с помощью следующей команды:

ent generate -h

Вот некоторые часто используемые опции командной строки:

  • --feature strings: Расширяет генерацию кода, добавляя дополнительную функциональность.
  • --header string: Переопределяет файл заголовка генерации кода.
  • --storage string: Указывает используемый драйвер хранения в процессе генерации кода, по умолчанию используется "sql".
  • --target string: Указывает целевой каталог для генерации кода.
  • --template strings: Выполняет дополнительные шаблоны Go. Поддерживает файл, каталог и шаблон пути, например: --template file="путь/к/файлу.tmpl".

Эти опции позволяют разработчикам настраивать процесс генерации своего кода в соответствии с различными потребностями и предпочтениями.

5. Настройка опции хранения

ent поддерживает генерацию кодовых ресурсов как для диалектов SQL, так и для Gremlin, по умолчанию используется диалект SQL. Если проекту необходимо подключиться к базе данных Gremlin, необходимо настроить соответствующий диалект базы данных. Ниже приведено, как указать опции хранения:

ent generate --storage gremlin ./ent/schema

Эта команда указывает ent использовать диалект Gremlin при генерации кода. Это гарантирует, что сгенерированные ресурсы будут адаптированы к требованиям базы данных Gremlin, обеспечивая совместимость с конкретной графовой базой данных.

6. Расширенное использование: Пакет entc

6.1 Использование entc как пакета в проекте

entc - это основной пакет, используемый для генерации кода в фреймворке ent. Помимо инструмента командной строки, entc также может быть интегрирован в проект как пакет, позволяя разработчикам контролировать и настраивать процесс генерации кода непосредственно в коде.

Чтобы использовать entc как пакет в проекте, вам необходимо создать файл с именем entc.go и добавить следующее содержимое в файл:

// +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)
    }
}

При использовании этого подхода вы можете изменять структуру gen.Config внутри функции main, чтобы применить различные опции конфигурации. Вызывая функцию entc.Generate по мере необходимости, вы гибко управляете процессом генерации кода.

6.2 Подробная настройка entc

entc предоставляет обширные параметры настройки, позволяя разработчикам настраивать сгенерированный код. Например, можно настроить пользовательские хуки для проверки или изменения сгенерированного кода, а также внедрять внешние зависимости с использованием инъекции зависимостей.

В следующем примере показано, как создать пользовательские хуки для функции entc.Generate:

func main() {
    err := entc.Generate("./schema", &gen.Config{
        Hooks: []gen.Hook{
            HookFunction,
        },
    })
    if err != nil {
        log.Fatalf("Ошибка при запуске генерации ent code: %v", err)
    }
}

// HookFunction - пользовательская функция-хук
func HookFunction(next gen.Generator) gen.Generator {
    return gen.GenerateFunc(func(g *gen.Graph) error {
        // Можно обработать здесь представленный графический режим g
        // Например, проверить наличие полей или изменить структуру
        return next.Generate(g)
    })
}

Кроме того, внешние зависимости можно добавлять с помощью 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("./схема", &gen.Config{}, opts...); err != nil {
        log.Fatalf("Ошибка при запуске генерации ent code: %v", err)
    }
}

В этом примере мы внедряем http.Client и io.Writer в качестве зависимостей в сгенерированные объекты клиента.

7. Вывод описания схемы

В фреймворке ent команду ent describe можно использовать для вывода описания схемы в графическом формате. Это поможет разработчикам быстро понять существующие сущности и отношения.

Для получения описания схемы выполните следующую команду:

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

Вышеуказанная команда выведет таблицу, аналогичную следующей, отображая информацию, такую как поля, типы, отношения и т. д. для каждой сущности:

Пользователь:
    +-------+---------+--------+-----------+ ...
    | Поле  |  Тип    | Уникально | Опционально  | ...
    +-------+---------+--------+-----------+ ...
    | id    | int     | false  | false     | ...
    | name  | string  | true   | false     | ...
    +-------+---------+--------+-----------+ ...
    +-------+--------+---------+-----------+ ...
    | Ребро |  Тип   | Обратное | Отношение  | ...
    +-------+--------+---------+-----------+ ...
    | pets  | Pet    | false   | O2M       | ...
    +-------+--------+---------+-----------+ ...

8. Хуки генерации кода

8.1 Концепция хуков

Хуки - это промежуточные функции, которые можно вставлять в процесс генерации кода ent, позволяя вставлять пользовательскую логику до и после генерации кода. Хуки могут использоваться для манипуляции абстрактным синтаксическим деревом (AST) сгенерированного кода, выполнения проверок или добавления дополнительных фрагментов кода.

8.2 Пример использования хуков

Вот пример использования хука для обеспечения того, что все поля содержат определенный структурный тег (например, json):

func main() {
    err := entc.Generate("./schema", &gen.Config{
        Hooks: []gen.Hook{
            EnsureStructTag("json"),
        },
    })
    if err != nil {
        log.Fatalf("Ошибка при запуске генерации ent code: %v", err)
    }
}

// EnsureStructTag обеспечивает, что все поля в графе содержат определенный структурный тег
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("Структурный тег %q отсутствует для поля %s.%s", name, node.Name, field.Name)
                    }
                }
            }
            return next.Generate(g)
        })
    }
}

В этом примере перед генерацией кода функция EnsureStructTag проверяет каждое поле на наличие тега json. Если поле не содержит этот тег, то процесс генерации кода завершится ошибкой. Это эффективный способ поддерживать чистоту и последовательность кода.