API Docs for: 5.4.0-alpha.18+9f4e020c
Show:

File: ../packages/model/src/-private/belongs-to.js

import { assert, warn } from '@ember/debug';
import { computed } from '@ember/object';

import { DEBUG } from '@ember-data/env';

import { lookupLegacySupport } from './legacy-relationships-support';
import { computedMacroWithOptionalParams, normalizeModelName } from './util';

/**
  @module @ember-data/model
*/

/**
  `belongsTo` is used to define One-To-One and One-To-Many
  relationships on a [Model](/ember-data/release/classes/Model).


  `belongsTo` takes an optional hash as a second parameter, currently
  supported options are:

  - `async`: A boolean value used to explicitly declare this to be an async relationship. The default is true.
  - `inverse`: A string used to identify the inverse property on a
    related model in a One-To-Many relationship. See [Explicit Inverses](#explicit-inverses)
  - `polymorphic` A boolean value to mark the relationship as polymorphic

  #### One-To-One
  To declare a one-to-one relationship between two models, use
  `belongsTo`:

  ```app/models/user.js
  import Model, { belongsTo } from '@ember-data/model';

  export default class UserModel extends Model {
    @belongsTo('profile') profile;
  }
  ```

  ```app/models/profile.js
  import Model, { belongsTo } from '@ember-data/model';

  export default class ProfileModel extends Model {
    @belongsTo('user') user;
  }
  ```

  #### One-To-Many

  To declare a one-to-many relationship between two models, use
  `belongsTo` in combination with `hasMany`, like this:

  ```app/models/post.js
  import Model, { hasMany } from '@ember-data/model';

  export default class PostModel extends Model {
    @hasMany('comment', { async: false, inverse: 'post' }) comments;
  }
  ```

  ```app/models/comment.js
  import Model, { belongsTo } from '@ember-data/model';

  export default class CommentModel extends Model {
    @belongsTo('post', { async: false, inverse: 'comments' }) post;
  }
  ```

  #### Sync relationships

  Ember Data resolves sync relationships with the related resources
  available in its local store, hence it is expected these resources
  to be loaded before or along-side the primary resource.

  ```app/models/comment.js
  import Model, { belongsTo } from '@ember-data/model';

  export default class CommentModel extends Model {
    @belongsTo('post', {
      async: false,
      inverse: null
    })
    post;
  }
  ```

  In contrast to async relationship, accessing a sync relationship
  will always return the record (Model instance) for the existing
  local resource, or null. But it will error on access when
  a related resource is known to exist and it has not been loaded.

  ```
  let post = comment.post;

  ```

  @method belongsTo
  @public
  @static
  @for @ember-data/model
  @param {String} modelName (optional) type of the relationship
  @param {Object} options (optional) a hash of options
  @return {Ember.computed} relationship
*/
function belongsTo(modelName, options) {
  const opts = options;
  const userEnteredModelName = modelName;

  assert(
    `Expected options.async from @belongsTo('${userEnteredModelName}', options) to be a boolean`,
    opts && typeof opts.async === 'boolean'
  );
  assert(
    `Expected options.inverse from @belongsTo('${userEnteredModelName}', options) to be either null or the string type of the related resource.`,
    opts.inverse === null || (typeof opts.inverse === 'string' && opts.inverse.length > 0)
  );

  const meta = {
    type: normalizeModelName(userEnteredModelName),
    isRelationship: true,
    options: opts,
    kind: 'belongsTo',
    name: '<Unknown BelongsTo>',
    key: null,
  };

  return computed({
    get(key) {
      // this is a legacy behavior we may not carry into a new model setup
      // it's better to error on disconnected records so users find errors
      // in their logic.
      if (this.isDestroying || this.isDestroyed) {
        return null;
      }
      const support = lookupLegacySupport(this);

      if (DEBUG) {
        if (['currentState'].indexOf(key) !== -1) {
          throw new Error(
            `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your belongsTo on ${this.constructor.toString()}`
          );
        }
        if (Object.prototype.hasOwnProperty.call(opts, 'serialize')) {
          warn(
            `You provided a serialize option on the "${key}" property in the "${support.identifier.type}" class, this belongs in the serializer. See Serializer and it's implementations https://api.emberjs.com/ember-data/release/classes/Serializer`,
            false,
            {
              id: 'ds.model.serialize-option-in-belongs-to',
            }
          );
        }

        if (Object.prototype.hasOwnProperty.call(opts, 'embedded')) {
          warn(
            `You provided an embedded option on the "${key}" property in the "${support.identifier.type}" class, this belongs in the serializer. See EmbeddedRecordsMixin https://api.emberjs.com/ember-data/release/classes/EmbeddedRecordsMixin`,
            false,
            {
              id: 'ds.model.embedded-option-in-belongs-to',
            }
          );
        }
      }

      return support.getBelongsTo(key);
    },
    set(key, value) {
      const support = lookupLegacySupport(this);
      if (DEBUG) {
        if (['currentState'].indexOf(key) !== -1) {
          throw new Error(
            `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your belongsTo on ${this.constructor.toString()}`
          );
        }
      }
      this.store._join(() => {
        support.setDirtyBelongsTo(key, value);
      });

      return support.getBelongsTo(key);
    },
  }).meta(meta);
}

export default computedMacroWithOptionalParams(belongsTo);