Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EmberData | Polymorphic Relationship Support #793

Merged
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions text/0793-polymporphic-relations-without-inheritance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
Stage: Accepted
Start Date:
Release Date: Unreleased
Release Versions:
ember-source: vX.Y.Z
ember-data: vX.Y.Z
Relevant Team(s):
RFC PR: https://github.com/emberjs/rfcs/pull/793
---

<!---
Directions for above:

Stage: Leave as is
Start Date: 2022-02-11
Release Date: Leave as is
Release Versions: Leave as is
Relevant Team(s): Ember Data
RFC PR: https://github.com/emberjs/rfcs/pull/793
-->

# Polymorphic relations without inheritance

## Summary

Allow setting polymorph relations without inheritance and mixins.
runspired marked this conversation as resolved.
Show resolved Hide resolved

## Motivation

In pre-octane Ember you could set polymorphic relations using two ways: inheritance or mixins. Mixins are deprecated and impossible to use with native classes, so the only option is to use inheritance:
runspired marked this conversation as resolved.
Show resolved Hide resolved

```js
import Model, { belongsTo, hasMany } from '@ember-data/model';

class TagModel extends Model {
@belongsTo('taggable', { polymorphic: true }) taggable;
}

// not a model, just a way to make relations work
class TaggableModel extends Model {
@hasMany('tag', { inverse: 'taggable' }) tags;
}

// real models with tags
class CommentModel extends TaggableModel {}
class PostModel extends TaggableModel {}
```

Which fells apart when you need your model to be a target of several polymorphic relations.
runspired marked this conversation as resolved.
Show resolved Hide resolved

```js
import Model, { belongsTo, hasMany } from '@ember-data/model';
runspired marked this conversation as resolved.
Show resolved Hide resolved

class ViewModel extends Model {
@belongsTo('viewable', { polymorphic: true }) viewable;
}

class ViewableModel extends Model {
@hasMany('views', { inverse: 'viewable' }) views;
}

// How to make Comment and Post viewable?...
```

You can do some dirty tricks making it inherit from each other `ViewableModel extends TaggableModel`. Or even by adding new base model class, like this:

```js

import Model, { belongsTo, hasMany } from '@ember-data/model';
runspired marked this conversation as resolved.
Show resolved Hide resolved

class ModelWithPolymorphic extends Model {}

class TagModel extends Model {
runspired marked this conversation as resolved.
Show resolved Hide resolved
@belongsTo('model-with-polymorphic', { polymorphic: true }) taggable;
}

class ViewModel extends Model {
runspired marked this conversation as resolved.
Show resolved Hide resolved
@belongsTo('model-with-polymorphic', { polymorphic: true }) viewable;
}

class CommentModel extends ModelWithPolymorphic {
@hasMany('views', { inverse: 'viewable' }) views;
@hasMany('tag', { inverse: 'taggable' }) tags;
}

```

While it solves the problem, it seems like a misuse of inheritance and too much trickery involved.

## Detailed design

I propose to introduce new API, similar to what [rails have](https://guides.rubyonrails.org/association_basics.html#polymorphic-associations).
yratanov marked this conversation as resolved.
Show resolved Hide resolved

runspired marked this conversation as resolved.
Show resolved Hide resolved
```js
import Model, { belongsTo, hasMany } from '@ember-data/model';

class TagModel extends Model {
@belongsTo('taggable', { polymorphic: true }) taggable;
runspired marked this conversation as resolved.
Show resolved Hide resolved
}

class ViewModel extends Model {
@belongsTo('viewable', { polymorphic: true }) viewable;
runspired marked this conversation as resolved.
Show resolved Hide resolved
}

class CommentModel extends Model {
@hasMany('views', { inverse: 'viewable' }) views;
runspired marked this conversation as resolved.
Show resolved Hide resolved
@hasMany('tag', { inverse: 'taggable' }) tags;
runspired marked this conversation as resolved.
Show resolved Hide resolved
}
```

`taggable` and `viewable` in this example should be typed as `extends Model`, so any model can take that spot.
JSON:API has enough information (`type` and `id`) to set this relation.
runspired marked this conversation as resolved.
Show resolved Hide resolved

## How we teach this

> Would the acceptance of this proposal mean the Ember guides must be
re-organized or altered? Does it change how Ember is taught to new users
at any level?

The docs should be changed to promote the new way.
runspired marked this conversation as resolved.
Show resolved Hide resolved

## Drawbacks

None

## Alternatives

> What other designs have been considered? What is the impact of not doing this?
runspired marked this conversation as resolved.
Show resolved Hide resolved

> This section could also include prior art, that is, how other frameworks in the same domain have solved this problem.

## Questions

It might be considered to use `as` instead of `inverse` to make it stand out as polymorphic relation, but if it works without it, I think it's better to keep `inverse`.
runspired marked this conversation as resolved.
Show resolved Hide resolved