diff --git a/doc/md/faq.md b/doc/md/faq.md index b1e96a64b..640b24e47 100644 --- a/doc/md/faq.md +++ b/doc/md/faq.md @@ -15,7 +15,9 @@ sidebar_label: FAQ [How to customize time fields to type `DATETIME` in MySQL?](#how-to-customize-time-fields-to-type-datetime-in-mysql) \ [How to use a custom generator of IDs?](#how-to-use-a-custom-generator-of-ids) \ [How to define a spatial data type field in MySQL?](#how-to-define-a-spatial-data-type-field-in-mysql) \ -[How to define custom types in external templates?](#how-to-define-custom-types-in-external-templates) +[How to extend the generated models?](#how-to-extend-the-generated-models) \ +[How to extend the generated builders?](#how-to-extend-the-generated-builders) + ## Answers #### How to create an entity from a struct `T`? @@ -464,29 +466,70 @@ func (Point) SchemaType() map[string]string { A full example exists in the [example repository](https://github.com/a8m/entspatial). -#### How to define custom types in external templates? -We can extend ent generated types with additional fields by defining a template called **model/fields/additional** like in this [example](https://github.com/ent/ent/blob/3ee6621194709db97e53f8ee6888593e156acc5e/examples/entcpkg/ent/template/static.tmpl). And sometimes we want to define these additional fields with our own types then we need to add the following template definitions also: -``` -{{- define "import/additional/field_types" }} + +#### How to extend the generated models? + +Ent supports extending generated types (both global types and models) using custom templates. For example, in order to +add additional struct fields or methods to the generated model, we can override the `model/fields/additional` template like in this +[example](https://github.com/ent/ent/blob/dd4792f5b30bdd2db0d9a593a977a54cb3f0c1ce/examples/entcpkg/ent/template/static.tmpl). + + +If your custom fields/methods require additional imports, you can add those imports using custom templates as well: + +```gotemplate +{{- define "import/additional/field_types" -}} "github.com/path/to/your/custom/type" {{- end -}} -{{- define "import/additional/client_dependencies" }} +{{- define "import/additional/client_dependencies" -}} "github.com/path/to/your/custom/type" {{- end -}} ``` -Full example: -``` -{{- define "import/additional/user_types" }} - "github.com/path/to/your/groupcount/type" -{{- end -}} +#### How to extend the generated builders? -{{ define "model/fields/additional" }} - {{- if eq $.Name "User" }} - // StaticField defined by template. - GroupsCount []Groupcount `json:"groups_count,omitempty"` - {{- end }} +In case you want to extend the generated client and add dependencies to all different builders under the `ent` package, +you can use the `"config/{fields,options}/*"` templates as follows: + +```gotemplate +{{/* A template for adding additional config fields/options. */}} +{{ define "config/fields/httpclient" -}} + // HTTPClient field added by a test template. + HTTPClient *http.Client +{{ end }} + +{{ define "config/options/httpclient" }} + // HTTPClient option added by a test template. + func HTTPClient(hc *http.Client) Option { + return func(c *config) { + c.HTTPClient = hc + } + } {{ end }} ``` -**Note:** Additional import template definitions must start with **import/additional/**. + +Then, you can inject this new dependency to your client, and access it in all builders: + +```go +func main() { + client, err := ent.Open( + "sqlite3", + "file:ent?mode=memory&cache=shared&_fk=1", + // Custom config option. + ent.HTTPClient(http.DefaultClient), + ) + if err != nil { + log.Fatal(err) + } + defer client.Close() + ctx := context.Background() + client.User.Use(func(next ent.Mutator) ent.Mutator { + return hook.UserFunc(func(ctx context.Context, m *ent.UserMutation) (ent.Value, error) { + // Access the injected HTTP client here. + _ = m.HTTPClient + return next.Mutate(ctx, m) + }) + }) + // ... +} +``` \ No newline at end of file diff --git a/entc/integration/template/ent/config.go b/entc/integration/template/ent/config.go index 79577ae27..933ffee5c 100644 --- a/entc/integration/template/ent/config.go +++ b/entc/integration/template/ent/config.go @@ -7,6 +7,8 @@ package ent import ( + "net/http" + "entgo.io/ent" "entgo.io/ent/dialect" ) @@ -24,8 +26,8 @@ type config struct { log func(...interface{}) // hooks to execute on mutations. hooks *hooks - // Boring field added by a test template. - Boring string + // HTTPClient field added by a test template. + HTTPClient *http.Client } // hooks per client, for fast access. @@ -66,9 +68,9 @@ func Driver(driver dialect.Driver) Option { } } -// Boring option added by a test template. -func Boring() Option { +// HTTPClient option added by a test template. +func HTTPClient(hc *http.Client) Option { return func(c *config) { - c.Boring = "Boring" + c.HTTPClient = hc } } diff --git a/entc/integration/template/ent/template/config.tmpl b/entc/integration/template/ent/template/config.tmpl index 9c78a136d..bd4ec8871 100644 --- a/entc/integration/template/ent/template/config.tmpl +++ b/entc/integration/template/ent/template/config.tmpl @@ -1,14 +1,14 @@ {{/* A template for adding additional config fields/options. */}} -{{ define "dialect/sql/config/fields/boring" -}} - // Boring field added by a test template. - Boring string +{{ define "config/fields/httpclient" -}} + // HTTPClient field added by a test template. + HTTPClient *http.Client {{ end }} -{{ define "dialect/sql/config/options/boring" }} - // Boring option added by a test template. - func Boring() Option { +{{ define "config/options/httpclient" }} + // HTTPClient option added by a test template. + func HTTPClient(hc *http.Client) Option { return func(c *config) { - c.Boring = "Boring" + c.HTTPClient = hc } } {{ end }} diff --git a/entc/integration/template/template_test.go b/entc/integration/template/template_test.go index 55c337d37..f43717c5d 100644 --- a/entc/integration/template/template_test.go +++ b/entc/integration/template/template_test.go @@ -6,10 +6,12 @@ package template import ( "context" + "net/http" "reflect" "testing" "entgo.io/ent/entc/integration/template/ent" + "entgo.io/ent/entc/integration/template/ent/hook" "entgo.io/ent/entc/integration/template/ent/migrate" "entgo.io/ent/entc/integration/template/ent/user" @@ -22,12 +24,19 @@ func TestCustomTemplate(t *testing.T) { "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1", // Custom config option. - ent.Boring(), + ent.HTTPClient(http.DefaultClient), ) require.NoError(t, err) defer client.Close() ctx := context.Background() require.NoError(t, client.Schema.Create(ctx, migrate.WithGlobalUniqueID(true))) + client.User.Use(func(next ent.Mutator) ent.Mutator { + return hook.UserFunc(func(ctx context.Context, m *ent.UserMutation) (ent.Value, error) { + // Access the injected HTTP client here. + _ = m.HTTPClient + return next.Mutate(ctx, m) + }) + }) p := client.Pet.Create().SetAge(1).SaveX(ctx) u := client.User.Create().SetName("a8m").AddPets(p).SaveX(ctx)