Routing: find record by two "ids"

The following problems seems to turn out as some sort of routing madness:

I have an url which looks like /<user-slug>/entity/<entity-slug>

where, both values user-slug and entity-slug denote properties that need to be passed to the backend as path params: /api/public/<user-slug>/entity/<entity-slug>. (spoiler: It actually does not really matter if they are path params or not)

From the ember perspective, /<user-slug> is not a url which leads to meaningful data and the url format is due to wanting to provide a readable and shareable link to the user, which clearly defines to which user space the entity belongs.

I tried to map this with a single route in ember land, which takes a path like

this.route('public-entity', { path: ':user_slug/entity/:entity-slug' })

which seems to work fairly well at first. However, now I am faced with the problem that ember datas findRecord only takes a single argument to construct the path, which is a string. So I can pass the entity-slug, but no user-slug.

I tried to map this with nested routes like:

this.route('public', { path: ':user_slug', resetNamespace: false }, function() {
    this.route('public-entity', { path: '/entity/:entity_slug' } );
 });

But now, I have another problem. I need to access the path param of the public route from within the public-entity model hook. The router service does not have any useful information yet when booting up the app. But wait, we can build a workaround for that:

If I return the user_slug from the public routes model hook:

model: function(params) {
    return params.user_slug;
 }

I can now get the slug from within the public-entity using modelFor('public'). Whoa. But wait a minute. This is starting to get really ugly by now and I am just working against embers conventions. So, what is the convention for doing stuff like this?

I also tried to use query instead of findRecord, however, now ember seems to expect to receive an array, which means I have to take care of that either in the model hook or within the template. Not so nice either.

you want queryRecord, or you want findRecord with adapterOptions, depending on whether one of these params is the ultimate id of this entity or not.

I’ll run through a few different examples since I don’t know which you’ve hit, and let’s stick with the single route since you don’t need to design your ui with nested routes it sounds like:

this.route('public-entity', { path: ':user_slug/entity/:entity_slug' })

The ID is neither of :user_slug or :entity_slug

@service store;
model({ user_slug, entity_slug }) {
   return this.store.queryRecord('entity', { user_slug, entity_slug });
}

The ID is :entity_slug

@service store;
model({ user_slug, entity_slug }) {
   return this.store.findRecord('entity', entity_slug, { adapterOptions: { user_slug } });
}

There are also several ways to model this as either a belongsTo or hasMany relationship depending on what user <-> entity looks like for your app that offer other APIs for doing this.

Hope this helps!

Thanks for your reply: I think one of the issues was that the backend was returning an array, which lead to some of the issues I had when using queryRecord.

To me the most confusing thing often is the buildURL mixin stuff, which resides in private packages and is not easily customisable. However, in that case there is no downside in completely circumventing it.

In the end I just created a dedicated adapter for the entity, which seems to work fine:

export default ApplicationAdapter.extend({
  queryRecord(store, type, query) {
    var url = '/api/public/' + query.user_slug + '/entity/' + query.entity_slug;
    return this.ajax(url, 'GET');
  },
}); 

There currently is no real relationship between user and entity. I am building a feature which allows the user to share otherwise private pages by creating a customizable link to that page. The user_slug is just the users own space where she/he is allowed to create unique links for dedicated resources. At the moment /<user-slug> will return nothing - but that might change in the future.