Skip to content

Commit fa565e8

Browse files
author
Miguel Molina
committed
Field methods to access through properties
1 parent 9605c1b commit fa565e8

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

generator/types.go

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,8 @@ type ImplicitFK struct {
562562
}
563563

564564
// Field is the representation of a model field.
565+
// TODO(erizocosmico): please, refactor all this structure to use precomputed
566+
// data instead of calculating it upon each call.
565567
type Field struct {
566568
// Name is the field name.
567569
Name string
@@ -714,8 +716,12 @@ func (f *Field) IsInverse() bool {
714716
return false
715717
}
716718

717-
for _, part := range strings.Split(f.Tag.Get("fk"), ",") {
718-
if part == "inverse" {
719+
if f.IsManyToManyRelationship() {
720+
return f.isInverseThrough()
721+
}
722+
723+
for i, part := range strings.Split(f.Tag.Get("fk"), ",") {
724+
if i > 0 && part == "inverse" {
719725
return true
720726
}
721727
}
@@ -729,6 +735,58 @@ func (f *Field) IsOneToManyRelationship() bool {
729735
return f.Kind == Relationship && strings.HasPrefix(f.Type, "[]")
730736
}
731737

738+
// IsManyToManyRelationship reports whether the field is a many to many
739+
// relationship.
740+
func (f *Field) IsManyToManyRelationship() bool {
741+
return f.Kind == Relationship && f.Tag.Get("through") != ""
742+
}
743+
744+
// ThroughTable returns the name of the intermediate table used to access the
745+
// current field.
746+
func (f *Field) ThroughTable() string {
747+
return f.getThroughTablePart(0)
748+
}
749+
750+
// LeftForeignKey is the name of the column used to join the current model with
751+
// the intermediate table.
752+
func (f *Field) LeftForeignKey() string {
753+
fk := f.getThroughTablePart(1)
754+
if fk == "" {
755+
fk = foreignKeyForModel(f.Model.Name)
756+
}
757+
return fk
758+
}
759+
760+
// RightForeignKey is the name of the column used to join the relationship
761+
// model with the intermediate table.
762+
func (f *Field) RightForeignKey() string {
763+
fk := f.getThroughTablePart(2)
764+
if fk == "" {
765+
fk = foreignKeyForModel(f.TypeSchemaName())
766+
}
767+
return fk
768+
}
769+
770+
func (f *Field) isInverseThrough() bool {
771+
return f.getThroughPart(1) == "inverse"
772+
}
773+
774+
func (f *Field) getThroughPart(idx int) string {
775+
parts := strings.Split(f.Tag.Get("through"), ",")
776+
if len(parts) > idx {
777+
return strings.TrimSpace(parts[idx])
778+
}
779+
return ""
780+
}
781+
782+
func (f *Field) getThroughTablePart(idx int) string {
783+
parts := strings.Split(f.getThroughPart(0), ":")
784+
if len(parts) > idx {
785+
return strings.TrimSpace(parts[idx])
786+
}
787+
return ""
788+
}
789+
732790
func foreignKeyForModel(model string) string {
733791
return toLowerSnakeCase(model) + "_id"
734792
}

generator/types_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,79 @@ func (s *FieldSuite) TestValue() {
194194
}
195195
}
196196

197+
func (s *FieldSuite) TestIsInverse() {
198+
cases := []struct {
199+
tag string
200+
expected bool
201+
}{
202+
{"", false},
203+
{`inverse:"true"`, false},
204+
{`fk:"inverse"`, false},
205+
{`through:"inverse"`, false},
206+
{`fk:"foo,inverse"`, true},
207+
{`fk:",inverse"`, true},
208+
{`through:"foo,inverse"`, true},
209+
{`through:"foo:a:b,inverse"`, true},
210+
}
211+
212+
for _, tt := range cases {
213+
f := withTag(mkField("", ""), tt.tag)
214+
f.Kind = Relationship
215+
s.Equal(tt.expected, f.IsInverse(), tt.tag)
216+
}
217+
}
218+
219+
func (s *FieldSuite) TestThroughTable() {
220+
cases := []struct {
221+
tag, expected string
222+
}{
223+
{``, ""},
224+
{`through:"foo"`, "foo"},
225+
{`through:"foo,inverse"`, "foo"},
226+
{`through:"foo:a:b,inverse"`, "foo"},
227+
}
228+
229+
for _, tt := range cases {
230+
s.Equal(tt.expected, withTag(mkField("", ""), tt.tag).ThroughTable(), tt.tag)
231+
}
232+
}
233+
234+
func (s *FieldSuite) TestLeftForeignKey() {
235+
cases := []struct {
236+
tag, expected string
237+
}{
238+
{``, "bar_id"},
239+
{`through:"foo"`, "bar_id"},
240+
{`through:"foo,inverse"`, "bar_id"},
241+
{`through:"foo:a,inverse"`, "a"},
242+
{`through:"foo:a:b,inverse"`, "a"},
243+
}
244+
245+
for _, tt := range cases {
246+
f := withTag(mkField("", ""), tt.tag)
247+
f.Model = &Model{Name: "Bar"}
248+
s.Equal(tt.expected, f.LeftForeignKey(), tt.tag)
249+
}
250+
}
251+
252+
func (s *FieldSuite) TestRightForeignKey() {
253+
cases := []struct {
254+
tag, expected string
255+
}{
256+
{``, "foo_id"},
257+
{`through:"foo"`, "foo_id"},
258+
{`through:"foo,inverse"`, "foo_id"},
259+
{`through:"foo:a,inverse"`, "foo_id"},
260+
{`through:"foo:a:b,inverse"`, "b"},
261+
}
262+
263+
for _, tt := range cases {
264+
f := withTag(mkField("", ""), tt.tag)
265+
f.Type = "Foo"
266+
s.Equal(tt.expected, f.RightForeignKey(), tt.tag)
267+
}
268+
}
269+
197270
type ModelSuite struct {
198271
suite.Suite
199272
model *Model

0 commit comments

Comments
 (0)