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

Override which serializer is used for an included relationship #2053

Open
beerlington opened this issue Sep 28, 2020 · 11 comments
Open

Override which serializer is used for an included relationship #2053

beerlington opened this issue Sep 28, 2020 · 11 comments

Comments

@beerlington
Copy link

Is there a way to override which serializer is used for a relationship? I have two similar models that belong to the same parent model type, but I want to use a different serializer for the parent, depending on the context.

For example, say I have two "child" models that belong to a parent:

child.js

import { Model, belongsTo } from 'ember-cli-mirage';

export default Model.extend({
  parent: belongsTo('parent'),
});

and

super-child.js

import { Model, belongsTo } from 'ember-cli-mirage';

export default Model.extend({
  parent: belongsTo('parent'),
});

If I want the super-child parent model to use a different serializer than the child parent (such as mirage/serializers/super-parent.js), Is that possible?

I was looking for something like this in the docs:

import { Model, belongsTo } from 'ember-cli-mirage';

export default Model.extend({
  parent: belongsTo('parent', { serializer: 'super-parent' }),
});

or even this:

import { Model, belongsTo } from 'ember-cli-mirage';
import SuperParentSerializer from './super-parent.js';

export default Model.extend({
  parent: belongsTo('parent', { serializer: SuperParentSerializer }),
});

but obviously those aren't supported. Did I miss something in the docs or is this just not a common use case?

@cah-brian-gantzler
Copy link
Collaborator

The serializer is expected to have the same name as the model that it serializes. One would also expect that model to be serialized the same way every time, so the default lookup only supports one.

Also usually the parent gets serialized, then it serializes the children, for your belongsTo syntax to work, if would have to attempt to serialize the children first to know what serializer to use for the parent.

It appears the internal serializer map is in a private variable. You would have to do something like this

let parentSerializer = RestSerializer.extend({
  keyForAttribute(attr) {
    return upperFirst(camelCase(attr))
  },
})

let superParentSerializer = RestSerializer.extend({
  keyForAttribute(attr) {
    return upperFirst(camelCase(attr))
  },
})

new Server({
  routes() {
      this.get("/movies/:id", (schema, request) => {
          let id = request.params.id

          let movie = schema.movies.find(id)

         if (movie.genre === 'Comedy') {
           return parentSerializer.serialize(movie, request);
         } else {
            return superParentSerializer.serialize(movie, request)
         } 
     })
  }
})

Something like that. Mirage is not looking up the serializer, you are calling it directly. Note that the parent is still serializing the children (say actors in the case of a move). If you needed the children to display the same functionality, you would have to iterate all the children choosing the correct serializer, then stitch the final JSON together.

This is not a normal situation so you have to do all the work to build the JSON yourself.

@samselikoff
Copy link
Collaborator

@beerlington could you give me an example of the actual requests/URLs and what you'd want the JSON payloads to look like?

@phcoliveira
Copy link

phcoliveira commented Sep 9, 2021

@cah-briangantzler, I tried your approach, but it didn't work for me at all.

First, the result of RestSerializer.extend({}) should be a class, right? It does not have the method serialize.
Second, after getting a new instance, the serialize method always fails with snapshot.eachAttribute is not a function. Regardless of passing one record or a list of them.

My real problem is that the serializer I have is never used at all. It is not about choosing one over the another. I have a model in Ember and a serializer in Mirage. I do not have this model in Mirage because it is supposed to use the one from Ember, right?

@cah-brian-gantzler
Copy link
Collaborator

Looking at the code it would need to be created the serializers should have been

let parentSerializer = RestSerializer.extend({
  keyForAttribute(attr) {
    return upperFirst(camelCase(attr))
  },
}).create({});

let superParentSerializer = RestSerializer.extend({
  keyForAttribute(attr) {
    return upperFirst(camelCase(attr))
  },
}).create({});

Is there perhaps a repo you could create demonstrating this that I would be able to run. It would help for me seeing the actual problem, also could check my code when I post this hear to help avoid the problem above.

@phcoliveira
Copy link

Thanks for your reply. I forgot it needs to be created, not instantitated with new. I will try that first.
BTW, I also tried

import SomeSerializer from "..."
...
this.config({
  serializers: {
    someModel: SomeSerializer

But it didn't work.

@phcoliveira
Copy link

phcoliveira commented Sep 9, 2021

Unfortunately, I can not show you the repo as it is private, but using create({}) also produced the same error:

    const topics = schema.db.topics.where(filters);
    const serializer = TopicSerializer.create({});
    return { topics: topics.map((t) => serializer.serialize(t, request)) };
    // return { topics: serializer.serialize(topics, request) }; // same error

@cah-brian-gantzler
Copy link
Collaborator

I assumed the repo was private. I meant if you could create a repo with some sample code demonstrating the problem

@phcoliveira
Copy link

phcoliveira commented Sep 9, 2021

I will try to do that, but I think I won't be able to reproduce it. For example, a serializer found on mirage/serializers/foo should be used automatically with a model found at app/models/foo, should it not?
I can't explain why it does not work like that with our app, but it is probably something we did.

@cah-brian-gantzler
Copy link
Collaborator

That is true and if it is not, then yea something is wrong.

But I thought the point was you wanted the parent to have two different serializers depending on the child. In that case you can not depend on Mirage to look up the serializer named the same as the model, but instead you have to handle the serialization yourself

@phcoliveira
Copy link

I am trying to specify the serializer because I gave up on figuring out why the correspondent serializer is not picked up automatically.

@phcoliveira
Copy link

I could not reproduce it, @cah-briangantzler. I am sorry.

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