逆引きFacebook/ent

# Facebook/entのschema定義

前回Gormの恨み節とFacebook/entを使用した感想書いた流れで詳細書き始めたらダラダラ長くなってしまったので分割しました。
Facebook/entの基本的な使い方は[公式のチュートリアル](https://entgo.io/docs/getting-started/)が親切なのでそちらを参照するのが良いですが、公式チュートリアルはAutoMigrationを前提としているため実際に出来上がるEntityの説明がちょい薄めなのでそこを踏まえてFacebook/entのgenerateの基となるschema定義について逆引き形式で記載していきます。

Facebook/entのschemaは主に

- DBのEntityとgolangのstructのMappingを定義する `Fields()`
- Entity同士の関連(join)を定義する `Edges()`

の2つで成り立っています。

EntityのPKはデフォルトでは `id:int`です。
EntityのPKの名称がidでない場合は使用することは出来ますが結構苦労します。
AutoMigrationを行う場合、Entity同士のjoinのためのfieldは`<edge.Toのstruct name>_<edge.Toで指定したedge name>`という名前で`edge.From`のfieldに自動的に追加されます。

Facebook/entのEdgeの考え方は論理と物理schemaが入り混じってちょっとややこしいくなっているのですが

- `one-to-many`relationの場合のFK fieldはEdge.To(many)に定義し、Edge.From(one)に作成される
- `one-to-one`relationの場合FKはEdge.Toに定義し、Edge.Toに作成される

となっています。

その為、実schema的にはEdge.From側にFKのfieldが定義されていても、Facebook/ent的にはEdge.To側で定義します。

具体的には `User`, `Car`という2つのschemaで`one-to-many` relationを定義する場合

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age"),
		field.String("name"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("cars", Car.Type),
	}
}
```

```go:ent/schema/car.go
// Fields of the Car.
func (Car) Fields() []ent.Field {
	return []ent.Field{
		field.String("model"),
		field.Time("registered_at"),
	}
}
```

`user_cars` という名前のfieldが `Car` EntityにFKとして追加されます。

<!-- 
http://www.plantuml.com/plantuml/uml/bLBHQi8m57tFLzpfYx8LH5I689ZMTYkkHRGEnX09pRKBjib8KiQG_Vjg758VbvSavvpSSq-uSsEPjdLLaX8F5gm2BOeF2rnepArGajnnF0Y9aD0utOTf5cTlq0s7ORIWNZ091_xeAjbi_QiY2iBHOk95Zu5F5uIOev2VJBCA6YshInifQLDMDO-mK0ZRk2N9IsOCd0WqosVviT2gbZnGfTAGX1bzkJ1ASzGNr2yRcKEfrkgh3VuGWj8A-msThK7T0MQWRWwcdUZFx1u4d_hnKvnclU6W3--hvSuHivdRM84jsa6ooJ1zfUlWcQvxm_6utpOCsCKlPzgut_cNMwKubZUr7Qon4CQYHhvdTfh5IRZ9QB9oNDT5sv_zRYf7ovxB_21uVtVkqDt0yqwkDp97oPiX-0K0
-->
![image](http://www.plantuml.com/plantuml/svg/bLBHQi8m57tFLzpfYx8LH5I689ZMTYkkHRGEnX09pRKBjib8KiQG_Vjg758VbvSavvpSSq-uSsEPjdLLaX8F5gm2BOeF2rnepArGajnnF0Y9aD0utOTf5cTlq0s7ORIWNZ091_xeAjbi_QiY2iBHOk95Zu5F5uIOev2VJBCA6YshInifQLDMDO-mK0ZRk2N9IsOCd0WqosVviT2gbZnGfTAGX1bzkJ1ASzGNr2yRcKEfrkgh3VuGWj8A-msThK7T0MQWRWwcdUZFx1u4d_hnKvnclU6W3--hvSuHivdRM84jsa6ooJ1zfUlWcQvxm_6utpOCsCKlPzgut_cNMwKubZUr7Qon4CQYHhvdTfh5IRZ9QB9oNDT5sv_zRYf7ovxB_21uVtVkqDt0yqwkDp97oPiX-0K0)

このようにFacebook/entではEntityを結合するFKは`Fields()`には記載せずに`Edges()`に記載する必要があります。

# `one-to-many` ralationを設定する

`one-to-many` ralationを設定する場合は`Edge.To`側だけの記載で問題ないです。
ただし、`Edge.To`だけしか記載しない場合は`Edge.From`側のidを指定して`Edge.To`のレコードを取得するといったような事はできません。取得したい場合は [`one-to-many` ralationのmany側でone側の情報を取得する](#one-to-many-ralationのmany側でone側の情報を取得する)を参照して`Edge.From`を設定してください。

<!-- 
http://www.plantuml.com/plantuml/uml/bLBHQi8m57tFLzpfYx8LH5I689ZMTYkkHRGEnX09pRKBjib8KiQG_Vjg758VbvSavvpSSq-uSsEPjdLLaX8F5gm2BOeF2rnepArGajnnF0Y9aD0utOTf5cTlq0s7ORIWNZ091_xeAjbi_QiY2iBHOk95Zu5F5uIOev2VJBCA6YshInifQLDMDO-mK0ZRk2N9IsOCd0WqosVviT2gbZnGfTAGX1bzkJ1ASzGNr2yRcKEfrkgh3VuGWj8A-msThK7T0MQWRWwcdUZFx1u4d_hnKvnclU6W3--hvSuHivdRM84jsa6ooJ1zfUlWcQvxm_6utpOCsCKlPzgut_cNMwKubZUr7Qon4CQYHhvdTfh5IRZ9QB9oNDT5sv_zRYf7ovxB_21uVtVkqDt0yqwkDp97oPiX-0K0
-->
![image](http://www.plantuml.com/plantuml/svg/bLBHQi8m57tFLzpfYx8LH5I689ZMTYkkHRGEnX09pRKBjib8KiQG_Vjg758VbvSavvpSSq-uSsEPjdLLaX8F5gm2BOeF2rnepArGajnnF0Y9aD0utOTf5cTlq0s7ORIWNZ091_xeAjbi_QiY2iBHOk95Zu5F5uIOev2VJBCA6YshInifQLDMDO-mK0ZRk2N9IsOCd0WqosVviT2gbZnGfTAGX1bzkJ1ASzGNr2yRcKEfrkgh3VuGWj8A-msThK7T0MQWRWwcdUZFx1u4d_hnKvnclU6W3--hvSuHivdRM84jsa6ooJ1zfUlWcQvxm_6utpOCsCKlPzgut_cNMwKubZUr7Qon4CQYHhvdTfh5IRZ9QB9oNDT5sv_zRYf7ovxB_21uVtVkqDt0yqwkDp97oPiX-0K0)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age"),
		field.String("name"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("cars", Car.Type),
	}
}
```

```go:ent/schema/car.go
// Fields of the Car.
func (Car) Fields() []ent.Field {
	return []ent.Field{
		field.String("model"),
		field.Time("registered_at"),
	}
}
```

# `one-to-one` ralationを設定する

`one-to-one` relationを設定する場合は`edge.To`に`Unique()`を指定すればよいのですが、`one-to-many`の場合とは異なり、`edge.To`側にrelation用のfieldとFKが作成されます。

<!--
http://www.plantuml.com/plantuml/uml/bP5VQy8m5CNVyodkz4NP2Y8gGn1C_zGLPnck3iOG2KsimJQH96MCuNTVJ8TKzhIdXDyzzzvpahasH9cwAb79zWQC1CMBWm7A5SiDbmBTKRRdWi4A9-akIhCaUuTkD8pY47l191huewlaTUDV5N4GZSA95py6FWuHqaSkJaIH2huHWDMJkZIn52Ob5OCrpyqNXBpYWki3e-ZSQiXBehLh0_19VYoKh0KDP2aLhA8CFpSLgIXJ3VLBAtCKAoK_s_2C41E6sssTZMQg0qH3RI_w9b-px1uudVh98acptd3GXu-NvTOLPZDta8AroWu9csRwXjV14rxtXkDnGxjknUvY_7GpC5xs7DGFdFQtRNEB2tRk6e5xst-v-sl5Zc-YUfxtEnRDcQ3szty0
-->
![image](http://www.plantuml.com/plantuml/svg/bP5VQy8m5CNVyodkz4NP2Y8gGn1C_zGLPnck3iOG2KsimJQH96MCuNTVJ8TKzhIdXDyzzzvpahasH9cwAb79zWQC1CMBWm7A5SiDbmBTKRRdWi4A9-akIhCaUuTkD8pY47l191huewlaTUDV5N4GZSA95py6FWuHqaSkJaIH2huHWDMJkZIn52Ob5OCrpyqNXBpYWki3e-ZSQiXBehLh0_19VYoKh0KDP2aLhA8CFpSLgIXJ3VLBAtCKAoK_s_2C41E6sssTZMQg0qH3RI_w9b-px1uudVh98acptd3GXu-NvTOLPZDta8AroWu9csRwXjV14rxtXkDnGxjknUvY_7GpC5xs7DGFdFQtRNEB2tRk6e5xst-v-sl5Zc-YUfxtEnRDcQ3szty0)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("group", Group.Type).
			Unique(),
	}
}
```

```go:ent/schema/group.go
// Fields of the Group.
func (Group) Fields() []ent.Field {
	return []ent.Field{
		field.String("name"),
	}
}
```

# `many-to-many` ralationを設定する

`many-to-many`の場合は`Edge.To`と`Edge.From`双方を設定します。
`many-to-many` relationを設定した場合は紐付け用のテーブルが自動で作成されます。

<!--
http://www.plantuml.com/plantuml/uml/XP1lQ-em5CRVvrESwtsZI454vI88fd_gBPjbk3eOOqXeOWscYIGfOuZVVLqw9B39NkNmFC_fhyzP6akqRMg11DjRi0eqhmuMADUij5n9z8UoFPSCrZZBTqbUPCKp_4t6IHhZ89ebet1oZJnkmsiYZP99F0lIVr68OuJCaSiJqQI6Cm9eywGHDbNIvgHciE6bVOUOrrnoSs0KNRo5KX1Zt1f0ICfZfLKZQQI4qh1E2dpVEKfJfZir56tCgLXhzUQB5uIOjBobzRQ6wHuG0qqx6B_V5sm8dCx3R9NbHNyy6i3BmzshCnOBzv2AURQJPDjc_eGtqN-ywO-dqu4FN6dLd1on-foCVw_VYRS_x_Ln27DjjRi9wkmRj67tWp_PBQyxLn04Pv-1kkUxlcIIDhNu0000
-->
![image](http://www.plantuml.com/plantuml/svg/XP1lQ-em5CRVvrESwtsZI454vI88fd_gBPjbk3eOOqXeOWscYIGfOuZVVLqw9B39NkNmFC_fhyzP6akqRMg11DjRi0eqhmuMADUij5n9z8UoFPSCrZZBTqbUPCKp_4t6IHhZ89ebet1oZJnkmsiYZP99F0lIVr68OuJCaSiJqQI6Cm9eywGHDbNIvgHciE6bVOUOrrnoSs0KNRo5KX1Zt1f0ICfZfLKZQQI4qh1E2dpVEKfJfZir56tCgLXhzUQB5uIOjBobzRQ6wHuG0qqx6B_V5sm8dCx3R9NbHNyy6i3BmzshCnOBzv2AURQJPDjc_eGtqN-ywO-dqu4FN6dLd1on-foCVw_VYRS_x_Ln27DjjRi9wkmRj67tWp_PBQyxLn04Pv-1kkUxlcIIDhNu0000)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("groups", Group.Type),
	}
}
```

```go:ent/schema/group.go
// Fields of the Group.
func (Group) Fields() []ent.Field {
	return []ent.Field{
		field.String("name"),
	}
}

// Edges of the Group.
func (Group) Edges() []ent.Edge {
	return []ent.Edge{
		edge.From("users", User.Type).
			Ref("groups"),
	}
}
```


# FKの名前を変更したい
デフォルトで名付けられる `<struct name>_<edge_name>` の形式ではなく `xxx_id` のように自由に名前を変更したい場合はEdge.To側で`StorageKey()`を指定することで変更が行なえます。


<!--
http://www.plantuml.com/plantuml/uml/bP1VQy8m5CNVyodkz4NP2Y8gGn1C_tGhhaMq3iOG2KsimJGPISeOudTVbu9qUzjJopddtjyzMHfBjAqhWGGxMh0AD2zF5YZNhB1SINH7sP5B1YbEiaEKvKd-1lre7CKXze9PDF8djyXsvzyISH1DmfaNFmO-3X4oPou_Y2OLN110aoUriB6IDYCLWmqlx1U4lEAIcnEZwDePA0Gnneq1-AGubrhLaWPAA0rfbEENrb6QCjsglcXYJiLQgy-kU4M8ISiRMczdcEu1CL0tF-PNlnPs3vpE_UGfoVB1U3I4z_Lgxup5mdr8oJgsasJJPlwADy4ptWp6q-cm2mn8ook8Dkwj_aMh56MYkxibQbPoOvbcz43iF4_IQ9lZTEsylgjs-BCtNWrSumTEX_kV-zh-ddTnbw4baxIkn3S0
-->
![image](http://www.plantuml.com/plantuml/svg/bP1VQy8m5CNVyodkz4NP2Y8gGn1C_tGhhaMq3iOG2KsimJGPISeOudTVbu9qUzjJopddtjyzMHfBjAqhWGGxMh0AD2zF5YZNhB1SINH7sP5B1YbEiaEKvKd-1lre7CKXze9PDF8djyXsvzyISH1DmfaNFmO-3X4oPou_Y2OLN110aoUriB6IDYCLWmqlx1U4lEAIcnEZwDePA0Gnneq1-AGubrhLaWPAA0rfbEENrb6QCjsglcXYJiLQgy-kU4M8ISiRMczdcEu1CL0tF-PNlnPs3vpE_UGfoVB1U3I4z_Lgxup5mdr8oJgsasJJPlwADy4ptWp6q-cm2mn8ook8Dkwj_aMh56MYkxibQbPoOvbcz43iF4_IQ9lZTEsylgjs-BCtNWrSumTEX_kV-zh-ddTnbw4baxIkn3S0)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age"),
		field.String("name"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("cars", Car.Type).
			StorageKey(edge.Column("user_id")),
	}
}
```

```go:ent/schema/car.go
// Fields of the Car.
func (Car) Fields() []ent.Field {
	return []ent.Field{
		field.String("model"),
		field.Time("registered_at"),
	}
}
```


# PKに`id`以外を指定したい
Facebook/entはdefaultではPKは`id: int`で定義されている前提でgenerateされます。idの名前を変更したいというのはよくありそうなユースケースですが[何故か公式ドキュメントには載っていません](https://entgo.io/docs/schema-fields/#id-field)。
そもそもPKをidという名前以外で定義されることが想定外…？

<!--
http://www.plantuml.com/plantuml/uml/LO_HIiCm68NlynI_ysP31gDiGmQ39MsgHLUaTeA8ZD1aNLYQI98Y8djtQmgZkqeut_TpE1ldcVLjev0I1m_UW9NrqGENLbHU6erkk3X8BM2BitnFyp8ht-6MHZHDy3HUqXcPNvJN7RaOQKpdoNAQtiS49mYvazHVpB86VX50vxDM-THedxD6G24h_mE9RAIMxYWuEWyEAiMS2sS0X5MdsffMyzWeOs5BI_pS4sEvi7rALAU555jhleVX6I6XlUpQHZidx0YOWxRxkAjzVTbT87l9LoHxoF9o7CqcyF7oz1deUXqULei13f7k9gtUS14_uc8SBHQJ_zQDqBnjr1y0
-->
![image](http://www.plantuml.com/plantuml/svg/LO_HIiCm68NlynI_ysP31gDiGmQ39MsgHLUaTeA8ZD1aNLYQI98Y8djtQmgZkqeut_TpE1ldcVLjev0I1m_UW9NrqGENLbHU6erkk3X8BM2BitnFyp8ht-6MHZHDy3HUqXcPNvJN7RaOQKpdoNAQtiS49mYvazHVpB86VX50vxDM-THedxD6G24h_mE9RAIMxYWuEWyEAiMS2sS0X5MdsffMyzWeOs5BI_pS4sEvi7rALAU555jhleVX6I6XlUpQHZidx0YOWxRxkAjzVTbT87l9LoHxoF9o7CqcyF7oz1deUXqULei13f7k9gtUS14_uc8SBHQJ_zQDqBnjr1y0)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("id").
			StorageKey("user_id").
			StructTag(`json:"user_id"`),
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}
```

StructTagは別になくても動きますが指定してあげないと`json.Marshal()`した時に定義した名前ではなく元の`id`という名称でjsonが出力されます。

```json
{
    "id": 3,
    "age": 30,
    "name": "a8m",
    "edges": {
        "Cars": null
    }
}
```

StructTagを指定すると定義した名前でjsonが出力されます。

```json
{
    "user_id": 3,
    "age": 30,
    "name": "a8m",
    "edges": {
        "Cars": null
    }
}
```

# PKの型を変えたい
PKに[UUID](https://github.com/google/uuid)や[xid](https://github.com/rs/xid)を使用したい場合などもあると思います。その場合はPKの型を変更する必要があります。
こちらに関しては[公式ドキュメントにも載っています](https://entgo.io/docs/schema-fields/#id-field)。

## PKをstringに変更する場合
<!--
http://www.plantuml.com/plantuml/uml/LP1HIyCm58NVyokkyyKXWp6s8OF1aZRLeYioEq5493HP5vOcSfCY8lllnXRcV4euvpiSpbtv832qZI56xG841wZhVG2fKLL1EqikfDffgs1Dy-AT5sLUli0bd_0ifQDapiTiUaAUjkn4P0cVflDHTfCmcXBY3zf-21GDV1E0o8lMXCpPK8X6mKPNuGjItMYh_Lv9SZmBL4PutyK0cAWEDRhMoiGPXx3c9Nte7OTIOQyo4x5EfOZkyrmy4g9iqB5jiFKA1o0yjF7Z_-thowv1oyKptIHtT7Cr6G_XzV7-hNEMo-uHjLgm_3Olob-xasoSyvUPpOQnSQMiZ1V-0G00
-->
![image](http://www.plantuml.com/plantuml/svg/LP1HIyCm58NVyokkyyKXWp6s8OF1aZRLeYioEq5493HP5vOcSfCY8lllnXRcV4euvpiSpbtv832qZI56xG841wZhVG2fKLL1EqikfDffgs1Dy-AT5sLUli0bd_0ifQDapiTiUaAUjkn4P0cVflDHTfCmcXBY3zf-21GDV1E0o8lMXCpPK8X6mKPNuGjItMYh_Lv9SZmBL4PutyK0cAWEDRhMoiGPXx3c9Nte7OTIOQyo4x5EfOZkyrmy4g9iqB5jiFKA1o0yjF7Z_-thowv1oyKptIHtT7Cr6G_XzV7-hNEMo-uHjLgm_3Olob-xasoSyvUPpOQnSQMiZ1V-0G00)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.String("id").
			SchemaType(map[string]string{
				dialect.MySQL:    "VARCHAR(20)", // Override MySQL.
				dialect.Postgres: "VARCHAR(20)", // Override Postgres.
			}),
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}
```

## PKを[UUID](https://github.com/google/uuid)に変更する場合
<!--
http://www.plantuml.com/plantuml/uml/LOzlIm914CRVxwUOx8qIWfXA28AxTtjrb4VO6KH4BBUZBkwVsDqZ8lpk7Ik8lPhXDx-7Xrc6A7niZIOQjn6Y0wzs-mXIUMoYSfPSITmgYx2YPVN9gxgit-2QZtcHqs4suoCsEIil6tOsYenFyjcmkCiOpGa91sM_X1S6VWb0vujMnyBPM0c3i5PD_85S6MLLsACanuj0eqK8AGR0H7FOUTTQcJdjFAnuJPzE5-Sb-XDbkjCIfTwxxqju90HjL5rRRnFGzq04QBib_FllL7O3Iiwp1xhktyu6yFxy-97mOf66sE6SbVTbLVV7eq52LXYSlz9roeodqq5NjqGhMwF_0000
-->
![image](http://www.plantuml.com/plantuml/svg/LOzlIm914CRVxwUOx8qIWfXA28AxTtjrb4VO6KH4BBUZBkwVsDqZ8lpk7Ik8lPhXDx-7Xrc6A7niZIOQjn6Y0wzs-mXIUMoYSfPSITmgYx2YPVN9gxgit-2QZtcHqs4suoCsEIil6tOsYenFyjcmkCiOpGa91sM_X1S6VWb0vujMnyBPM0c3i5PD_85S6MLLsACanuj0eqK8AGR0H7FOUTTQcJdjFAnuJPzE5-Sb-XDbkjCIfTwxxqju90HjL5rRRnFGzq04QBib_FllL7O3Iiwp1xhktyu6yFxy-97mOf66sE6SbVTbLVV7eq52LXYSlz9roeodqq5NjqGhMwF_0000)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.UUID("id", uuid.UUID{}).
			Default(func() uuid.UUID { return uuid.Must(uuid.NewRandom())}),
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}
```

## PKを[xid](https://github.com/rs/xid)に変更する場合

<!--
http://www.plantuml.com/plantuml/uml/LOzTIyCm68NlyojUvur31cDiGmQ3fMsgHLTaTeA8I6ZUTM7va2H5HFRVpLeOCpS9ptaEXwnyu2wqMX65km31WfFDFe2G3kiWhI5N0dVI8AnfKNwoiYggDxXc4vPdT9JEsJYPdfMNRN8syfHDi_aelqiJcX7Y3z9ySSSr_1A0wFDMXToQK7ADi95r-85CQccatwCWnuj2hRZtfrey2QyFZREj4QbLri6QLVIfJwmJw7gQgAXrb3fdlo_XaH0qGSQrmTQZ6m3tqCQ7__U_VkmMf5Ya3tHpCnaFuVtvyQF3osLtyGONIN5VbDKfxf3X6XUlTDDtPhDXd5kX4QrMVm00
-->
![image](http://www.plantuml.com/plantuml/svg/LOzTIyCm68NlyojUvur31cDiGmQ3fMsgHLTaTeA8I6ZUTM7va2H5HFRVpLeOCpS9ptaEXwnyu2wqMX65km31WfFDFe2G3kiWhI5N0dVI8AnfKNwoiYggDxXc4vPdT9JEsJYPdfMNRN8syfHDi_aelqiJcX7Y3z9ySSSr_1A0wFDMXToQK7ADi95r-85CQccatwCWnuj2hRZtfrey2QyFZREj4QbLri6QLVIfJwmJw7gQgAXrb3fdlo_XaH0qGSQrmTQZ6m3tqCQ7__U_VkmMf5Ya3tHpCnaFuVtvyQF3osLtyGONIN5VbDKfxf3X6XUlTDDtPhDXd5kX4QrMVm00)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.UUID("id", xid.ID{}).
			SchemaType(map[string]string{
				dialect.MySQL:    "CHAR(20)", // Override MySQL.
				dialect.Postgres: "CHAR(20)", // Override Postgres.
			}).
			Default(xid.New),
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}
```

# PKをFKに設定する

**出来ません。**

PKにそのEntity独自のIDではなく別のEntityのIDを指定したい場合、`edge.To`に`StorageKey`を指定して`id`を指定すればいけそうですが実際にはidが重複してテーブル作成時に`Duplicate column name error`になります。

<!--
http://www.plantuml.com/plantuml/uml/RP5lQy8m4CRVvrFSz8so5KHKXY2O_kbMd6MuEXWo93In1jDa92bZYDzz8GMfOwzoFFSSltikIsE9jaqja6072rQ1vjNH0kMQbPOhYUueEt39O8spV9_aHLPyG3yP9scCWsYMZCB9rVAs3Q-ED4ecyIn87wCGnmYP4vTVH9CQpWZ0-KaZRAgapKdDOCDB-mCnhxdaviWekdG6Ia6CyMC08IbFbLQDf94IIiCwAV1Bsr6QCjsgeN0shsAjrNTNl234fEMErjiQfdj033IkC3VvMjWzS3eFiwSiBmRZqH1shwjFtrWi_4CgrcbxIReqytUyYPxnPZ2UJeT_WMjcYOVMhkWUh4Ns_q7kqjN0h_Z0wV1c0Q4gBlUbEuVjBkAGRQGWE7iEMZ99tH__0W00
-->
![image](http://www.plantuml.com/plantuml/svg/RP5lQy8m4CRVvrFSz8so5KHKXY2O_kbMd6MuEXWo93In1jDa92bZYDzz8GMfOwzoFFSSltikIsE9jaqja6072rQ1vjNH0kMQbPOhYUueEt39O8spV9_aHLPyG3yP9scCWsYMZCB9rVAs3Q-ED4ecyIn87wCGnmYP4vTVH9CQpWZ0-KaZRAgapKdDOCDB-mCnhxdaviWekdG6Ia6CyMC08IbFbLQDf94IIiCwAV1Bsr6QCjsgeN0shsAjrNTNl234fEMErjiQfdj033IkC3VvMjWzS3eFiwSiBmRZqH1shwjFtrWi_4CgrcbxIReqytUyYPxnPZ2UJeT_WMjcYOVMhkWUh4Ns_q7kqjN0h_Z0wV1c0Q4gBlUbEuVjBkAGRQGWE7iEMZ99tH__0W00)

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("meta", UserMeta.Type).
			StorageKey(edge.Column("id")).
			Unique(),
	}
}
```

```go:ent/schema/user_meta.go
// Fields of the UserMeta.
func (UserMeta) Fields() []ent.Field {
	return []ent.Field{
		field.Int("login_count").
			Positive(),
	}
}

// Edges of the UserMeta.
func (UserMeta) Edges() []ent.Edge {
	return []ent.Edge{
		edge.From("user", User.Type).
			Ref("meta").
			Unique().
			Required(),
	}
}
```

```sql
-- CREATE TABLE IF NOT EXISTS `users`(`id` char(36) binary NOT NULL, `age` bigint NOT NULL, `name` varchar(255) NOT NULL DEFAULT 'unknown', `id` bigint NULL, PRIMARY KEY(`id`, `id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
create table "users": Error 1060: Duplicate column name 'id'
```

# テーブル名と異なるschemaを設定する

既にあるテーブルとstructをそのままにFacebook/entを導入したい場合、テーブル名とschema名が必ずしも一致していない事があると思います。その場合は[Annotations](https://entgo.io/docs/schema-annotations/)をschemaに追加します。

<!--
http://www.plantuml.com/plantuml/uml/LP1HQ_8m68NlyolUp-z66O8ubI48fcqwosOPhWx662CqhpMO9YD96MFutnTI47ULyEGv74xMpdFhkrOHXGSFte2LpT63a1PhBuqc_mGUf4ROqgByO6LLLA_mdqrPdj5nkc2JP7PHdlV9nSXJDiiMu_mkJMX6Y3j9_SajR-6700IVTyhdHlkIjmWxMVjlo6GhjNH75EHy5QWLTox6019UdnfhEYrIeuo5BQleO_zYh43RqqG5BL9ghVcwXcT2K7iPsWORq-3UeHq0Tz25Y_kpiI-y0IcMwORkXhUB4RmzFRn7l5h5WpUuJ8hxego6qyae8XtsB5_eBcQcy_aeLAvHY_35lm00
-->
![image](http://www.plantuml.com/plantuml/svg/LP1HQ_8m68NlyolUp-z66O8ubI48fcqwosOPhWx662CqhpMO9YD96MFutnTI47ULyEGv74xMpdFhkrOHXGSFte2LpT63a1PhBuqc_mGUf4ROqgByO6LLLA_mdqrPdj5nkc2JP7PHdlV9nSXJDiiMu_mkJMX6Y3j9_SajR-6700IVTyhdHlkIjmWxMVjlo6GhjNH75EHy5QWLTox6019UdnfhEYrIeuo5BQleO_zYh43RqqG5BL9ghVcwXcT2K7iPsWORq-3UeHq0Tz25Y_kpiI-y0IcMwORkXhUB4RmzFRn7l5h5WpUuJ8hxego6qyae8XtsB5_eBcQcy_aeLAvHY_35lm00)

```go:ent/schema/user.go
// Annotations of the User.
func (User) Annotations() []schema.Annotation {
	return []schema.Annotation{
		entsql.Annotation{Table: "HogeUser"},
	}
}
```

# `Edge.To`側で`Edge.From`側の情報を取得する - Eager Loading

`one-to-many` ralationの例で説明しますが`one-to-one` ralationや`many-to-many` ralationでも同じです。

<!--
http://www.plantuml.com/plantuml/uml/bP3VQ_em5CNVyrVShxuetnL4L8OWcFxeLboBQ1sC8HAQM8DfCfAKCSJ_VJK5gOzxQdlEkVTpJvV64cthIY31ZXQi0itBamNADIiiLnBze-p89OCK9zaXol8a_u1-D8xY47l1B1hvatjak_FlYJY89k7CYvy37uS8cJENNqIJ2Yu8eCcJMjXOIPkHYi665_O7GbvnoSs9KNJj31I26EF607nId4kjQaa39PI6DChnM-ieJPbkLLyqCQTYhTLtLxmYnAJb3QstCqptW1YecnVpqA-5_GTEvtxoacJvO3mQmkTwjNV6Ok4Uf6GTsqcoQJD_nvlW5My6u-bqs0K6fEKLH1lth_v4gnHbuc6tapKhkR5CCteWTfudQRJDSRfsNjzLEznu3vlZrS1rVk9qkBzTwBx0yoxkDhHaajQL-0K0
-->
![image](http://www.plantuml.com/plantuml/svg/bP3VQ_em5CNVyrVShxuetnL4L8OWcFxeLboBQ1sC8HAQM8DfCfAKCSJ_VJK5gOzxQdlEkVTpJvV64cthIY31ZXQi0itBamNADIiiLnBze-p89OCK9zaXol8a_u1-D8xY47l1B1hvatjak_FlYJY89k7CYvy37uS8cJENNqIJ2Yu8eCcJMjXOIPkHYi665_O7GbvnoSs9KNJj31I26EF607nId4kjQaa39PI6DChnM-ieJPbkLLyqCQTYhTLtLxmYnAJb3QstCqptW1YecnVpqA-5_GTEvtxoacJvO3mQmkTwjNV6Ok4Uf6GTsqcoQJD_nvlW5My6u-bqs0K6fEKLH1lth_v4gnHbuc6tapKhkR5CCteWTfudQRJDSRfsNjzLEznu3vlZrS1rVk9qkBzTwBx0yoxkDhHaajQL-0K0)

`Edge.To`側で`Edge.From`側の情報を取得するだけの場合は`Edge.From`側の設定は不要です。
取得時に`WithXXX()`を設定するだけで紐づくレコードを自動で取得します。
取得したレコードは`Edges`というfieldの中に格納されています。
詳細は公式ドキュメントの[Eager Loading](https://entgo.io/docs/eager-load/)を参照してください。

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("cars", Car.Type),
	}
}
```

```go:ent/schema/car.go
// Fields of the Car.
func (Car) Fields() []ent.Field {
	return []ent.Field{
		field.String("model"),
		field.Time("registered_at"),
	}
}
```

```go:main.go
func main() {
	ctx := context.Background()
	client, err := ent.Open("mysql", "mysql_dsn")
	if err != nil {
		log.Fatalf("failed opening connection to mysql: %v", err)
	}
	defer client.Close()

	usr, err := client.User.Query().
		Where(user.ID(1)).
		WithCars(). // これを指定する
		Only(ctx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(usr.String())

	for _, c := range usr.Edges.Cars {
		fmt.Println(c.String())
	}
}
```

# `Edge.From`側で`Edge.To`側の情報を取得する

`one-to-many` ralationの例で説明しますが`one-to-one` ralationや`many-to-many` ralationでも同じです。

<!--
http://www.plantuml.com/plantuml/uml/bP3VQ_em5CNVyrVShxuetnL4L8OWcFxeLboBQ1sC8HAQM8DfCfAKCSJ_VJK5gOzxQdlEkVTpJvV64cthIY31ZXQi0itBamNADIiiLnBze-p89OCK9zaXol8a_u1-D8xY47l1B1hvatjak_FlYJY89k7CYvy37uS8cJENNqIJ2Yu8eCcJMjXOIPkHYi665_O7GbvnoSs9KNJj31I26EF607nId4kjQaa39PI6DChnM-ieJPbkLLyqCQTYhTLtLxmYnAJb3QstCqptW1YecnVpqA-5_GTEvtxoacJvO3mQmkTwjNV6Ok4Uf6GTsqcoQJD_nvlW5My6u-bqs0K6fEKLH1lth_v4gnHbuc6tapKhkR5CCteWTfudQRJDSRfsNjzLEznu3vlZrS1rVk9qkBzTwBx0yoxkDhHaajQL-0K0
-->
![image](http://www.plantuml.com/plantuml/svg/bP3VQ_em5CNVyrVShxuetnL4L8OWcFxeLboBQ1sC8HAQM8DfCfAKCSJ_VJK5gOzxQdlEkVTpJvV64cthIY31ZXQi0itBamNADIiiLnBze-p89OCK9zaXol8a_u1-D8xY47l1B1hvatjak_FlYJY89k7CYvy37uS8cJENNqIJ2Yu8eCcJMjXOIPkHYi665_O7GbvnoSs9KNJj31I26EF607nId4kjQaa39PI6DChnM-ieJPbkLLyqCQTYhTLtLxmYnAJb3QstCqptW1YecnVpqA-5_GTEvtxoacJvO3mQmkTwjNV6Ok4Uf6GTsqcoQJD_nvlW5My6u-bqs0K6fEKLH1lth_v4gnHbuc6tapKhkR5CCteWTfudQRJDSRfsNjzLEznu3vlZrS1rVk9qkBzTwBx0yoxkDhHaajQL-0K0)

`Edge.From`側の`car` Entityを取得した際に`Edge.To`側の`user` Entityの情報も取得したい場合は、`user`側に設定する`Edge.To`と合わせて`car`側に`Edge.From`の設定をする必要があります。
詳細は公式ドキュメントの[Eager Loading](https://entgo.io/docs/eager-load/)を参照してください。

```go:ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("cars", Car.Type), // edge.Toとedge.FromにUnique()の設定をすると one-to-one になります
	}
}
```

```go:ent/schema/car.go
// Fields of the Car.
func (Car) Fields() []ent.Field {
	return []ent.Field{
		field.String("model"),
		field.Time("registered_at"),
	}
}

// Edges of the Car.
func (Car) Edges() []ent.Edge {
	return []ent.Edge{
		edge.From("user", User.Type). // edge.Fromを設定しないとWithUser()を指定できない
			Ref("cars"). // edge.Toのnameと一致させる
			Unique(), // edge.FromにUnique()の設定がないと many-to-many になります
	}
}
```

```go:main.go
func main() {
	ctx := context.Background()
	client, err := ent.Open("mysql", "mysql_dsn")
	if err != nil {
		log.Fatalf("failed opening connection to mysql: %v", err)
	}
	defer client.Close()

	c, err := client.Car.Query().
		Where(car.ID(1)).
		WithUser(). // これを指定する
		Only(ctx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(c.String())
	if c.Edges.User != nil {
		fmt.Println(c.Edges.User.String())
	}
}
```