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

Ad hoc includes for the serializer layer #754

Closed
samselikoff opened this Issue May 31, 2016 · 1 comment

Comments

Projects
None yet
1 participant
@samselikoff
Owner

samselikoff commented May 31, 2016

Currently, Mirage's ORM only supports basic hasMany and belongsTo relationships. This makes it hard to fake out responses with many to many relationships that do not include the join model.

Originally I was going to add a manyToMany helper to the ORM, but that involves a lot of work, as it touches the foreign keys + makes assumptions about the database. A better short-term solution, to help folks out today, is to update the Serializer layer.

Let's say we have a manyToMany between posts and tags. In the ORM, we still need to set up the join model. So we'd have

// mirage/models/post.js
import { Model, hasMany } from 'ember-cli-mirage';
export default Model.extend({
  postTags: hasMany()
});

// mirage/models/post-tag.js
import { Model, belongsTo } from 'ember-cli-mirage';
export default Model.extend({
  post: belongsTo(),
  tag: belongsTo(),
});

// mirage/models/tag.js
import { Model, hasMany } from 'ember-cli-mirage';
export default Model.extend({
  postTags: hasMany()
});

This simulates the backend setup with the join table. Create related data like so:

let post = server.create('post');
let economics = server.create('tag', { title: 'Economics' });
let programming = server.create('tag', { title: 'Programming' });

server.create('post-tag', { post, tag: economics });
server.create('post-tag', { post, tag: programming });

This sets up the database correct; the trick is, how to get the Serializer to return the m2m between the posts and the tags, without the join model. We basically want to simulate something like this:

GET /posts?include=tags

{
  data: [{
    type: 'posts',
    id: 1,
    attributes: {}
    relationships: {
      tags: {
        data: [
          { type: 'tags', id: '1' },
          { type: 'tags', id: '2' }
        ]
      }
    }
  }],
  included: [{
    type: 'tags',
    id: '1',
    attributes: {
      title: 'Economics'
    }
  }, {
    type: 'tags',
    id: '2',
    attributes: {
      title: 'Programming'
    }
  }]
}

I think the easiest way to do this is to get the Serializer to accept adhoc includes, and to check for locally defined methods, similar to how active_model_serializers does it.

This is what I want to see:

// serializers/post.js
export default ApplicationSerializer.extend({

  include: ['tags'],

  tags(post) {
    let tags = post.postTags.map(pt => pt.tag);
    return new Collection('tag', tags);
  }

});

Note that we've defined a local method here, and because tags is included, the serializer first checks to see if it has a local method of the same name. If so, it invokes it, and then it asserts the return value is either a Model or Collection, at which point it should be able to continue on with the serialization process as normal, just as if it were an actual relationship on the underlying Model itself.

Note that this should work even if we don't have the include array here, i.e. if tags were included as a query param in the request. In that case, we'd just do

// serializers/post.js
export default ApplicationSerializer.extend({

  // Expose the `tags` relationship to the serializer layer 
  tags(post) {
    let tags = post.postTags.map(pt => pt.tag);
    return new Collection('tag', tags);
  }

});

and GET /posts?include=tags should still work.

Note this should also work for hasOne. The ad hoc methods will return either a Collection or Model, and in that way the Serializer should be able to serialize them. An empty Collection is still a Collection. A model that doesn't exist is null so shouldn't be included.


The work for the JSONAPISerializer will be around here. Unfortunately this logic has not been extracted to the Base serializer, so this part will also need to be updated (or, even better, refactor JSONAPISerializer to extend from Base. Not sure how much work is involved here).

I'm sure there's details I missed, feel free to post here as you work.

@samselikoff

This comment has been minimized.

Owner

samselikoff commented Jan 23, 2017

Filed under the Roadmap project

@samselikoff samselikoff removed the backlog label Jan 23, 2017

imtayadeway added a commit to imtayadeway/assemble that referenced this issue Jul 7, 2017

imtayadeway added a commit to imtayadeway/assemble that referenced this issue Jul 7, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment