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

Polymorphic Relationship setup #53

Open
derekgstevens opened this issue Nov 21, 2019 · 7 comments
Open

Polymorphic Relationship setup #53

derekgstevens opened this issue Nov 21, 2019 · 7 comments

Comments

@derekgstevens
Copy link

Say I have a Project model defined, which in the ProjectResource defined on the backend has a relationship to Board defined as such:

polymorphic_has_many :boards, as: :boardable

The BoardResource includes

  attribute :boardable_id, :uuid
  attribute :boardable_type, :string
  polymorphic_belongs_to :boardable do
    group_by(:boardable_type) do
      on(:Client)
      on(:Company)
      on(:Project)
    end
  end

Using Spraypaint on the Frontend, I have the Project and Board models defined as such:

export const Project = ApplicationRecord.extend({
    static: {
        jsonapiType: "projects"
    },
    attrs: {
        ...
        boards: hasMany()
    }
});

export const Board = ApplicationRecord.extend({
    static: {
        jsonapiType: "boards"
    },
    attrs: {
        name: attr(),
        boardable_id: attr(),
        boardable_type: attr()
    }
})

When I query for a project using Project.find(projectId) I'm getting the following error:

Error: "Unknown type "boards""

I've tried including boardable: belongsTo() in the Board model on the frontend, but that doesn't help.

Can someone point me in the right direction?

Thanks in advance!

@mihaimuntenas
Copy link

@derekgstevens As far as I got into this, it does not seem that SprayPaint supports polymorphic relationships straight forward (as expected from ActiveRecord).
I did got working results in front-end (at least for reading associated records) by using an intermediary record class (in your case Boardable) that the polymorphic models extend (Project, Client, Company for you). And on the board class you can do boardable: belongsTo(), because at this point SP will be able to map to the "base class" Boardable and subsequently to any of the "derived classes" Project, Client, Company.

I am using TS and my scenario is a bit different, as I have an Address (STI enabled model in the back-end) that can polymorphic belong to Company or User.

@derekgstevens
Copy link
Author

@mihaimuntenas Thanks for that!

Could you go into the intermediary class a little bit more? I know you're using TS, but could you possibly share your Addressable class with me?

In my scenario, would the Boardable class have the boards: hasMany() definition?

Thanks again!

@mihaimuntenas
Copy link

@derekgstevens the intermediary class is just a "base" class in between ApplicationRecord and the polymorphics (and you have a deeper hierarchy). So you would have something like the following:

export const Boardable = ApplicationRecord.extend({
  static: { jsonapiType: 'boardables' },
  attrs: { boards: hasMany({type: Board, name: 'boardable'}) }
// ts: @HasMany({ type: Board, name: 'boarable'}) boards: Boards[];
});

// Same for Company and Client
export const Project = Boardable.extend({
  static: { jsonapiType: 'projects' }
});

export const Board = ApplicationRecord.extend({
  static: { jsonapiType: 'boards' },
  attrs: { boardable: belongsTo('boardable') }
// ts: @BelongsTo(Boardable) boardable: Project|Company|Client;
});

Basically, at this point, you kinda have STI on Boardable (as an ActiveRecord parallel); your Board, belongs to a Boardable, which can be one of Project, Company or Client (as these are the only "derived" classes).
This means that when you do Project.includes('boards').find(projectId), the resulting object should have a boards:[] object. Similar, when you do Board.includes('boardable').find(boardId), the result should have a boardable: {} object; because of the class hierarchy in the front-end and the resources on the back-end, this object will be serialized with the correct jsonapiType (type attribute) which will determine SP to generate a Project, Company or Client instance, accordingly.

The Boardable does not need to exist in the back-end; it's just a front-end trick to resolve the "polymorphic" aspect.

All the relationships definition methods allow for some customization provided and opts parameter (check the attributes.ts and associations.ts in src/ folder of SP).

@derekgstevens
Copy link
Author

@mihaimuntenas Thanks! This was all super useful!

@derekgstevens
Copy link
Author

@wadetandy or @richmolj are there plans to support polymorphic relationships in a more standard way in Spraypaint? I have some models on my backend that have multiple polymorphic relationships, so the solution provided by @mihaimuntenas won't always be sufficient without multiple intermediate models, which seems less than ideal.

@richmolj
Copy link
Contributor

Hey @derekgstevens sorry for the delay - and @mihaimuntenas thanks so much for posting your solution ❤️ !

I agree it would be better to have more out-of-the-box support, but I'm not sure we'll be getting to it soon. I'd be happy to take a look at a PR if you wanted to go that way, though.

@andychongyz
Copy link

I faced the same issue as well, decided to query and write each of them manually for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants