Skip to content

Relationship Editing

Bill Heaton edited this page Nov 20, 2016 · 13 revisions

With a persisted resource (model) methods are available to modify the resource's relations, addRelationship, addRelationships, removeRelationship, and removeRelationships, see the API Docs.

These methods do not persist the changes, they only modify the relationships data stored in the resource. The resource keeps track of it's relations by storing the "resource identifier objects" (type, id).

For a to-one relation the data is an object (having one resource identifier object or null); and for a to-many relation the data is an array of resource identifier objects, or an empty array.

See the specific methods to use based on the relationship type. The relationships property of the resource looks just like the payload you would expect that is defined by the JSON API 1.0 Specification. The data path is like 'relationships.' + relationName + '.data' the relationName would be singular for a to-one relation and plural for a to-many relation.

Also, you may just check if the data is an Array if so it's a to-many relation, otherwise the relation is to-one. The promise proxy object that is assigned to the resource using the toOne or toMany Resource helper methods to define a property have the kind of relation set, i.e. toMany or toOne.

There are various options to consider when editing Relationships…

To-One

Use the store or service patchRelationship method.

The JSON API 1.0 Specification indicates that a PATCH request can be used to create/update/remove a to-one relation.

When creating or adding the payload will include a "resource identifier object" (type and id) assigned to the data node.

When removing the payload will assign null to the data node.

To-Many

Use the store or service createRelationship method to add a to-many relation.

When creating (adding) a new relation the adapter will make a POST request that includes a array assigned to the data node which includes one "resource identifier object" (type and id) - the relation to be added.

Use the store or service deleteRelationship method to remove a to-many relation.

When removing a relation the adapter will make a DELETE request which includes a array assigned to the data node which includes one "resource identifier object" (type and id) - the relation to be deleted.

Use the store or service patchRelationship method to replace app of the to-many relations.

The JSON API 1.0 Specification indicates that if the server does not support replacing all of a resource's relations that a 403 response must be returned. Otherwise, the PATCH request will result in a total replacement of all the relations.

When replacing all of a resource's relations the adapter will make a PATCH request which includes a array assigned to the data node which includes one or more "resource identifier object" (type and id) - the relations to replace the existing ones.

Resource Operations Mixin

Mixin to provide interations between a Resource instance and service/adapter.

  • Methods: createRelationship, deleteRelationship, updateRelationship

The methods have built in rollback and offer a callback to handler errors. Also they provide an example of how you can create your own methods to operation on the relationships using the related services.

The API Contract

Ember-jsonapi-resources does not implement Active Record (like) behavior. The library is only concerned with the API contact (the JSON API syntax, format and nomenclature). So the conventions followed for operating on relationships, to-one (has-one) and to-many (has-many), are about working with the expected JSON format in the client app, not about active record conventions.

The Resource (model) and Adapter provide an interface for interacting with the API to operate on a resource's relations. Because the Resource does not provide Active Record (like) behavior for modeling relationships, the app developer is free to work with relations as they see fit regarding the application needs - i.e. does a relationship eagerly load, or not? Does editing a relation result in an optimistic or pessimistic representation on screen? (Are changes made to a relation applied only after an API request is successful? Or, is the change to the relation applied and rendered straightaway?)

The toOne and toMany helpers for a Resource (model) simply setup the PromiseProxy object/array. The actual related resource must be requested in order for the relation's Promise to resolve. Because Array|Object -Proxy objects use a content property to store the proxied object/array - the relation can be found at model.relation-name(s).content.

The JSON API spec has very clear instruction on how to operate on relations, which depend on a Resource's to-one or to-many type of relationship. See the available Adapter methods which are callable from the resource itself via patchRelationship, createRelationship, updateRelationship, and deleteRelationship methods which are defined in a ResourceOperationsMixin

In addition to persisting relationship changes separately from PATCH'ing a resource's attribute(s) by using the the service (adapter) methods mentioned above, the updateResource has an optional second parameter includeRelationships to provide an array with a list of relationship names that should be PATCH'd using the resource's link (endpoint). This optional argument, provides a way to update attributes and/or relationships together using the main endpoint for the resource.

Operating on a Resource's Relationships

The Resource has addRelationship and removeRelationship methods for operating on it's relationships (internal hash which follows the format of the JSON API spec). The Resource also has methods that interact with the service (decorated Adapter) using a ResourceOperationsMixin - patchRelationship, createRelationship, updateRelationship, and deleteRelationship. See the section above on the "API Contract".

Instead of defining how you must work with relationships in the client application, the above mentioned Resource methods for operating on relationships provide an interface for defining how your relationships should behave according to your application needs.

Often you may decide to re-define the addRelationship method to create the desired behavior for how your relationships should interact. In some cases you may want to load an incomplete representation of a list of resources to use as options to select from when assigning a relation to a resource. In this case the resource is new (id-less) and needs to fetch the relationship by an id in order to render the properties found in the related resource. So, after the relation assignment is made the app can fetch the complete representation. In other cases you may want to be more explicit and optimistic, assigning a loaded and complete representation of a relation to a resource and rendering immediately by setting the model.relation (to-one) or adding to a model.relations (to-many) array.

These various methods can be used as-is; or perhaps redefined according to your applications need(s). For example when a resource is new, and a list of relations are known only by reference (id and a displayName, an incomplete representation) the addRelationship method should also do the work to request the complete representation.

/**
  @method addRelationship
  @param {String} related - resource name
  @param {String} id
*/
addRelationship(related, id) {
  if (this.get('isNew')) {
    this.get('store').find(related, id).then(function(resource) {
      this.set(related, resource);
    }.bind(this));
  }
  return this._super(related, id);
},