Skip to content

Commit 253a381

Browse files
Miguel Molinaerizocosmico
Miguel Molina
authored andcommitted
Field methods to access through properties
1 parent 2d19136 commit 253a381

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

generator/types.go

+60-2
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,8 @@ type ImplicitFK struct {
611611
}
612612

613613
// Field is the representation of a model field.
614+
// TODO(erizocosmico): please, refactor all this structure to use precomputed
615+
// data instead of calculating it upon each call.
614616
type Field struct {
615617
// Name is the field name.
616618
Name string
@@ -807,8 +809,12 @@ func (f *Field) IsInverse() bool {
807809
return false
808810
}
809811

810-
for _, part := range strings.Split(f.Tag.Get("fk"), ",") {
811-
if part == "inverse" {
812+
if f.IsManyToManyRelationship() {
813+
return f.isInverseThrough()
814+
}
815+
816+
for i, part := range strings.Split(f.Tag.Get("fk"), ",") {
817+
if i > 0 && part == "inverse" {
812818
return true
813819
}
814820
}
@@ -822,6 +828,58 @@ func (f *Field) IsOneToManyRelationship() bool {
822828
return f.Kind == Relationship && strings.HasPrefix(f.Type, "[]")
823829
}
824830

831+
// IsManyToManyRelationship reports whether the field is a many to many
832+
// relationship.
833+
func (f *Field) IsManyToManyRelationship() bool {
834+
return f.Kind == Relationship && f.Tag.Get("through") != ""
835+
}
836+
837+
// ThroughTable returns the name of the intermediate table used to access the
838+
// current field.
839+
func (f *Field) ThroughTable() string {
840+
return f.getThroughTablePart(0)
841+
}
842+
843+
// LeftForeignKey is the name of the column used to join the current model with
844+
// the intermediate table.
845+
func (f *Field) LeftForeignKey() string {
846+
fk := f.getThroughTablePart(1)
847+
if fk == "" {
848+
fk = foreignKeyForModel(f.Model.Name)
849+
}
850+
return fk
851+
}
852+
853+
// RightForeignKey is the name of the column used to join the relationship
854+
// model with the intermediate table.
855+
func (f *Field) RightForeignKey() string {
856+
fk := f.getThroughTablePart(2)
857+
if fk == "" {
858+
fk = foreignKeyForModel(f.TypeSchemaName())
859+
}
860+
return fk
861+
}
862+
863+
func (f *Field) isInverseThrough() bool {
864+
return f.getThroughPart(1) == "inverse"
865+
}
866+
867+
func (f *Field) getThroughPart(idx int) string {
868+
parts := strings.Split(f.Tag.Get("through"), ",")
869+
if len(parts) > idx {
870+
return strings.TrimSpace(parts[idx])
871+
}
872+
return ""
873+
}
874+
875+
func (f *Field) getThroughTablePart(idx int) string {
876+
parts := strings.Split(f.getThroughPart(0), ":")
877+
if len(parts) > idx {
878+
return strings.TrimSpace(parts[idx])
879+
}
880+
return ""
881+
}
882+
825883
func foreignKeyForModel(model string) string {
826884
return toLowerSnakeCase(model) + "_id"
827885
}

generator/types_test.go

+73
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)