pg icon indicating copy to clipboard operation
pg copied to clipboard

Has one Polymorphic

Open Janther opened this issue 4 years ago • 2 comments

I know that this one is a difficult one as stated in #885 But I'd like to present my use case. I have files that can be attached to many different other models if there's still one pointer to the file, the garbage collector will skip it, otherwise it will be deleted from the disk. So my models are:

type File struct {
	ID          int
	Filename    string
	ContentType string
	ByteSize    int
	MD5         string
}

type Attachment struct {
	ID             int
	FileID         int
	File           *File
	AttachableID   int
	AttachableType string
}

And the other models can have many files attached like an image gallery or just one like a profile picture.

I understand that is easy to have each model just point directly to the file. For adding and managing this is easy, but for deleting a file that can be pointed to by any model, this is a bit more difficult to maintain as any new feature could attach a file or files to a model.

Janther avatar Apr 18 '20 01:04 Janther

At the moment I'm happy having an array like this:

type Profile struct {
	Picture []*Attachment `pg:"polymorphic:attachable_"`
}

profile = Profile{}
db.model(&profile).
	Relation("Picture", func(q *orm.Query) (*orm.Query, error) {
		return q.Limit(1), nil
	}).
	Relation("Picture.File").
	Select()

But it just feels like this could be the ORM's job

Janther avatar Apr 18 '20 01:04 Janther

uuuh bonus if you also manage to do #1567 then we could have something like this.

type Attachment struct {
	ID             int
	FileID         int
	File           *File
	AttachableID   int
	AttachableType string
	AttachedAs     string
}

type Profile struct {
	Picture           *Attachment `pg:"polymorphic:attachable_,where: attached_as='Picture'"`
	BackgroundPicture *Attachment `pg:"polymorphic:attachable_",where: attached_as='BackgroundPicture'`
}

Then we could have this:

profile = Profile{}
db.model(&profile).
	Relation("Picture").
	Relation("Picture.File").
	Relation("BackgroundPicture").
	Relation("BackgroundPicture.File").
	Select()

Instead of

db.model(&profile).
	Relation("Picture", func(q *orm.Query) (*orm.Query, error) {
		return q.Where("attached_as = ?", "Picture").Limit(1), nil
	}).
	Relation("Picture.File").
	Relation("BackgroundPicture", func(q *orm.Query) (*orm.Query, error) {
		return q.Where("attached_as = ?", "BackgroundPicture").Limit(1), nil
	}).
	Relation("BackgroundPicture.File").
	Select()

I know I'm getting way too excited with this but I can see the possibilities.

Janther avatar Apr 18 '20 01:04 Janther