Use slug for page routes, id for store.find


#1

In my link-to route I currently pass model.slug as the dynamic segment and then in my route’s model hook simply run

this.store.find('model', params.slug)

This is great because I get nice slugged urls in my rendered HTML for SEO purposes… but I don’t want to use the slug for model lookups on my API, I prefer to use the model.id… is it possible to somehow pass both slug and id to my route handler? Will I need to extend the LinkView for this?


#2

You can do smthing like:

serialize: function(model, params) {
  return { post_slug: model.slug, post_id: model.id };
}

#3

Hmm, but what about the case where you’re loading the route directly such as

GET /posts/this-is-my-slugged-post

Only thing you got is the slug in that case


#4

Why not use slugs throughout? It sounds like it will simplify your life in a huge way


#5

I want to keep id’s around for other consumers of the API such as iPhone apps or external services, or even my own admin ember-cli app - which don’t need to use slugs. I went down the route of using store.find method with {slug: model.slug} as parameter.


#6

Now that you mention it I’ve also stumbled across this same issue! As soon as I posted my reply I thought about the problem and a solution to this would help me a lot…


#7

It would be better if you find record from store using id instead of using slug.


How to use slugs in URL?
#8

but the slug is what is passed in the URL


#9

I think I’m going to bite the bullet and commit to slugs all round - I don’t think there is a way around this


#10

I guess you are passing two parameters in your routes smthing like:

this.route('post', {path: '/post/:post_slug/:post_id'});

and location: 'history'. If you can provide more codes, it would be good.


#11

@cjroebuck In your API, you can check if variable is either an integer or string then act accordingly.

GET /posts/:variable 

You can also serialize your models in index actions with both IDs and slugs. This way you can ask Ember’s store for record containing that slug then query API with ID associated with that slug for rest of information.

It is worth mentioning that querying an RDBMS with ID or a string won’t make a noticeable performance difference beside having larger indexes.


#12

I’ve put a lot of thought into this concept and tried a number of different things. For me, I’m using the ‘name’ attribute instead of a slug, but I believe the same rules apply.

I think I finally have a pattern that I can live with, but there were a few “gotchas”, such as:

  • Replacing this.store.find('thing', params.thing_id) with this.store.find('thing', { name: params.thing_name }) has at least three consequences:
    1. it will always query the server,
    2. it will expect an array (e.g. {“things”: […]}) from the server,
    3. it will return a DS.PromiseArray

Things I tried but decided against:

  • Setting the primaryKey to ‘name’ on my adapters, even though my server uses ids (this messed up my belongsTo relationships among other things.)
  • Coercing my server-side code to accept api/thing/kitty instead of api/thing?name=kitty. I decided that I should tinker as little as possible with my server side and look for the Ember solution.
  • Using transitionTo to cheat my router and turn thing/6 into thing/kitty. (Need to be able to load the app fresh at thing/kitty)
  • Building elaborate beforeModel hooks to pre-fetch models from the store (ie server) and fish the ids out of them so my model hooks can use the ol’ this.store.find('thing', 6)

After learning more about Ember’s Promises, specifically DS.PromiseArray, I settled into just doing this a bunch:

which seems good, but it still hits the server every time.

check out https://github.com/ballPointPenguin/cupery/tree/master/ember/app, which is very much a work-in-progress and changing very quickly, but might be helpful.