@ember-data/model 4.10.0 → 4.12.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/{-private/diff-array.ts → -private.js} +41 -13
- package/addon/-private.js.map +1 -0
- package/addon/has-many-a275fe0d.js +6283 -0
- package/addon/has-many-a275fe0d.js.map +1 -0
- package/addon/index.js +1 -0
- package/addon/index.js.map +1 -0
- package/addon-main.js +90 -0
- package/package.json +43 -15
- package/addon/-private/attr.js +0 -162
- package/addon/-private/belongs-to.js +0 -268
- package/addon/-private/debug/assert-polymorphic-type.js +0 -71
- package/addon/-private/deprecated-promise-proxy.ts +0 -76
- package/addon/-private/errors.ts +0 -425
- package/addon/-private/has-many.js +0 -280
- package/addon/-private/index.ts +0 -14
- package/addon/-private/legacy-data-fetch.js +0 -395
- package/addon/-private/legacy-data-utils.ts +0 -92
- package/addon/-private/legacy-relationships-support.ts +0 -774
- package/addon/-private/many-array.ts +0 -401
- package/addon/-private/model-for-mixin.ts +0 -38
- package/addon/-private/model.js +0 -2516
- package/addon/-private/notify-changes.ts +0 -72
- package/addon/-private/promise-belongs-to.ts +0 -73
- package/addon/-private/promise-many-array.ts +0 -425
- package/addon/-private/promise-proxy-base.js +0 -4
- package/addon/-private/record-state.ts +0 -468
- package/addon/-private/references/belongs-to.ts +0 -624
- package/addon/-private/references/has-many.ts +0 -669
- package/addon/-private/relationship-meta.ts +0 -98
- package/addon/-private/util.ts +0 -31
- package/addon/index.ts +0 -39
- package/blueprints/model/HELP.md +0 -26
- package/blueprints/model/files/__root__/__path__/__name__.js +0 -5
- package/blueprints/model/index.js +0 -158
- package/blueprints/model/native-files/__root__/__path__/__name__.js +0 -5
- package/blueprints/model-test/index.js +0 -33
- package/blueprints/model-test/mocha-files/__root__/__path__/__test__.js +0 -18
- package/blueprints/model-test/mocha-rfc-232-files/__root__/__path__/__test__.js +0 -15
- package/blueprints/model-test/qunit-files/__root__/__path__/__test__.js +0 -14
- package/index.js +0 -46
package/addon/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { a as attr, b as belongsTo, M as default, h as hasMany } from "./has-many-a275fe0d";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
package/addon-main.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const pkg = require('./package.json');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
name: pkg.name,
|
|
5
|
+
|
|
6
|
+
options: {
|
|
7
|
+
'@embroider/macros': {
|
|
8
|
+
setOwnConfig: {},
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
_emberDataConfig: null,
|
|
13
|
+
configureEmberData() {
|
|
14
|
+
if (this._emberDataConfig) {
|
|
15
|
+
return this._emberDataConfig;
|
|
16
|
+
}
|
|
17
|
+
const app = this._findHost();
|
|
18
|
+
const isProd = /production/.test(process.env.EMBER_ENV);
|
|
19
|
+
const hostOptions = app.options?.emberData || {};
|
|
20
|
+
const debugOptions = Object.assign(
|
|
21
|
+
{
|
|
22
|
+
LOG_PAYLOADS: false,
|
|
23
|
+
LOG_OPERATIONS: false,
|
|
24
|
+
LOG_MUTATIONS: false,
|
|
25
|
+
LOG_NOTIFICATIONS: false,
|
|
26
|
+
LOG_REQUEST_STATUS: false,
|
|
27
|
+
LOG_IDENTIFIERS: false,
|
|
28
|
+
LOG_GRAPH: false,
|
|
29
|
+
LOG_INSTANCE_CACHE: false,
|
|
30
|
+
},
|
|
31
|
+
hostOptions.debug || {}
|
|
32
|
+
);
|
|
33
|
+
let HAS_DEBUG_PACKAGE, HAS_META_PACKAGE;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// eslint-disable-next-line node/no-missing-require
|
|
37
|
+
require.resolve('@ember-data/debug', { paths: [process.cwd(), __dirname] });
|
|
38
|
+
HAS_DEBUG_PACKAGE = true;
|
|
39
|
+
} catch {
|
|
40
|
+
HAS_DEBUG_PACKAGE = false;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// eslint-disable-next-line node/no-missing-require
|
|
44
|
+
require.resolve('ember-data', { paths: [process.cwd(), __dirname] });
|
|
45
|
+
HAS_META_PACKAGE = true;
|
|
46
|
+
} catch {
|
|
47
|
+
HAS_META_PACKAGE = false;
|
|
48
|
+
}
|
|
49
|
+
const includeDataAdapterInProduction =
|
|
50
|
+
typeof hostOptions.includeDataAdapterInProduction === 'boolean'
|
|
51
|
+
? hostOptions.includeDataAdapterInProduction
|
|
52
|
+
: HAS_META_PACKAGE;
|
|
53
|
+
|
|
54
|
+
const includeDataAdapter = HAS_DEBUG_PACKAGE ? (isProd ? includeDataAdapterInProduction : true) : false;
|
|
55
|
+
const DEPRECATIONS = require('@ember-data/private-build-infra/src/deprecations')(hostOptions.compatWith || null);
|
|
56
|
+
const FEATURES = require('@ember-data/private-build-infra/src/features')(isProd);
|
|
57
|
+
|
|
58
|
+
// copy configs forward
|
|
59
|
+
const ownConfig = this.options['@embroider/macros'].setOwnConfig;
|
|
60
|
+
ownConfig.compatWith = hostOptions.compatWith || null;
|
|
61
|
+
ownConfig.debug = debugOptions;
|
|
62
|
+
ownConfig.deprecations = Object.assign(DEPRECATIONS, ownConfig.deprecations || {});
|
|
63
|
+
ownConfig.features = Object.assign({}, FEATURES);
|
|
64
|
+
ownConfig.includeDataAdapter = includeDataAdapter;
|
|
65
|
+
|
|
66
|
+
this._emberDataConfig = ownConfig;
|
|
67
|
+
return ownConfig;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
included() {
|
|
71
|
+
this.configureEmberData();
|
|
72
|
+
return this._super.included.call(this, ...arguments);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
treeForVendor() {
|
|
76
|
+
return;
|
|
77
|
+
},
|
|
78
|
+
treeForPublic() {
|
|
79
|
+
return;
|
|
80
|
+
},
|
|
81
|
+
treeForStyles() {
|
|
82
|
+
return;
|
|
83
|
+
},
|
|
84
|
+
treeForAddonStyles() {
|
|
85
|
+
return;
|
|
86
|
+
},
|
|
87
|
+
treeForApp() {
|
|
88
|
+
return;
|
|
89
|
+
},
|
|
90
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ember-data/model",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.0-alpha.0",
|
|
4
4
|
"description": "A presentation layer for apps built with @ember-data/store",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -13,15 +13,28 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "",
|
|
15
15
|
"directories": {},
|
|
16
|
+
"ember-addon": {
|
|
17
|
+
"main": "addon-main.js",
|
|
18
|
+
"type": "addon",
|
|
19
|
+
"version": 1
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"addon-main.js",
|
|
23
|
+
"addon"
|
|
24
|
+
],
|
|
16
25
|
"peerDependencies": {
|
|
17
|
-
"@ember-data/
|
|
18
|
-
"@ember-data/
|
|
19
|
-
"@ember-data/
|
|
26
|
+
"@ember-data/json-api": "4.12.0-alpha.0",
|
|
27
|
+
"@ember-data/graph": "4.12.0-alpha.0",
|
|
28
|
+
"@ember-data/store": "4.12.0-alpha.0",
|
|
29
|
+
"@ember-data/tracking": "4.12.0-alpha.0",
|
|
20
30
|
"@ember/string": "^3.0.1",
|
|
21
31
|
"ember-inflector": "^4.0.2"
|
|
22
32
|
},
|
|
23
33
|
"peerDependenciesMeta": {
|
|
24
|
-
"@ember-data/
|
|
34
|
+
"@ember-data/json-api": {
|
|
35
|
+
"optional": true
|
|
36
|
+
},
|
|
37
|
+
"@ember-data/graph": {
|
|
25
38
|
"optional": true
|
|
26
39
|
}
|
|
27
40
|
},
|
|
@@ -34,31 +47,46 @@
|
|
|
34
47
|
}
|
|
35
48
|
},
|
|
36
49
|
"dependencies": {
|
|
37
|
-
"@ember-data/canary-features": "4.
|
|
38
|
-
"@ember-data/private-build-infra": "4.
|
|
50
|
+
"@ember-data/canary-features": "4.12.0-alpha.0",
|
|
51
|
+
"@ember-data/private-build-infra": "4.12.0-alpha.0",
|
|
39
52
|
"@ember/edition-utils": "^1.2.0",
|
|
40
53
|
"@embroider/macros": "^1.10.0",
|
|
41
|
-
"ember-auto-import": "^2.4.3",
|
|
42
54
|
"ember-cached-decorator-polyfill": "^1.0.1",
|
|
43
55
|
"ember-cli-babel": "^7.26.11",
|
|
44
56
|
"ember-cli-string-utils": "^1.1.0",
|
|
45
57
|
"ember-cli-test-info": "^1.0.0",
|
|
46
|
-
"ember-compatibility-helpers": "^1.2.6",
|
|
47
58
|
"inflection": "~2.0.0"
|
|
48
59
|
},
|
|
49
60
|
"devDependencies": {
|
|
50
|
-
"@babel/core": "^7.
|
|
61
|
+
"@babel/core": "^7.21.0",
|
|
62
|
+
"@babel/cli": "^7.21.0",
|
|
51
63
|
"@glimmer/component": "^1.1.2",
|
|
52
|
-
"ember-source": "~4.
|
|
53
|
-
"
|
|
64
|
+
"ember-source": "~4.9.3",
|
|
65
|
+
"@embroider/addon-dev": "^2.0.0",
|
|
66
|
+
"rollup": "^3.17.2",
|
|
67
|
+
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
68
|
+
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
|
69
|
+
"@babel/plugin-proposal-decorators": "^7.21.0",
|
|
70
|
+
"@babel/plugin-transform-typescript": "^7.21.0",
|
|
71
|
+
"@babel/plugin-transform-runtime": "^7.21.0",
|
|
72
|
+
"@babel/preset-typescript": "^7.21.0",
|
|
73
|
+
"@babel/preset-env": "^7.20.2",
|
|
74
|
+
"@babel/runtime": "^7.21.0",
|
|
75
|
+
"@rollup/plugin-babel": "^6.0.3",
|
|
76
|
+
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
77
|
+
"tslib": "^2.4.1",
|
|
78
|
+
"walk-sync": "^3.0.0",
|
|
79
|
+
"typescript": "^4.9.5"
|
|
54
80
|
},
|
|
55
81
|
"engines": {
|
|
56
82
|
"node": "^14.8.0 || 16.* || >= 18.*"
|
|
57
83
|
},
|
|
58
|
-
"ember-addon": {},
|
|
59
84
|
"volta": {
|
|
60
85
|
"extends": "../../package.json"
|
|
61
86
|
},
|
|
62
|
-
"packageManager": "pnpm@7.
|
|
63
|
-
"scripts": {
|
|
87
|
+
"packageManager": "pnpm@7.27.1",
|
|
88
|
+
"scripts": {
|
|
89
|
+
"build": "rollup --config && babel ./addon --out-dir addon --plugins=../private-build-infra/src/transforms/babel-plugin-transform-ext.js",
|
|
90
|
+
"start": "rollup --config --watch"
|
|
91
|
+
}
|
|
64
92
|
}
|
package/addon/-private/attr.js
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { assert } from '@ember/debug';
|
|
2
|
-
import { computed } from '@ember/object';
|
|
3
|
-
import { DEBUG } from '@glimmer/env';
|
|
4
|
-
|
|
5
|
-
import { recordIdentifierFor, storeFor } from '@ember-data/store';
|
|
6
|
-
import { recordDataFor } from '@ember-data/store/-private';
|
|
7
|
-
|
|
8
|
-
import { computedMacroWithOptionalParams } from './util';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
@module @ember-data/model
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
`attr` defines an attribute on a [Model](/ember-data/release/classes/Model).
|
|
16
|
-
By default, attributes are passed through as-is, however you can specify an
|
|
17
|
-
optional type to have the value automatically transformed.
|
|
18
|
-
Ember Data ships with four basic transform types: `string`, `number`,
|
|
19
|
-
`boolean` and `date`. You can define your own transforms by subclassing
|
|
20
|
-
[Transform](/ember-data/release/classes/Transform).
|
|
21
|
-
|
|
22
|
-
Note that you cannot use `attr` to define an attribute of `id`.
|
|
23
|
-
|
|
24
|
-
`attr` takes an optional hash as a second parameter, currently
|
|
25
|
-
supported options are:
|
|
26
|
-
|
|
27
|
-
- `defaultValue`: Pass a string or a function to be called to set the attribute
|
|
28
|
-
to a default value if and only if the key is absent from the payload response.
|
|
29
|
-
|
|
30
|
-
Example
|
|
31
|
-
|
|
32
|
-
```app/models/user.js
|
|
33
|
-
import Model, { attr } from '@ember-data/model';
|
|
34
|
-
|
|
35
|
-
export default class UserModel extends Model {
|
|
36
|
-
@attr('string') username;
|
|
37
|
-
@attr('string') email;
|
|
38
|
-
@attr('boolean', { defaultValue: false }) verified;
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Default value can also be a function. This is useful it you want to return
|
|
43
|
-
a new object for each attribute.
|
|
44
|
-
|
|
45
|
-
```app/models/user.js
|
|
46
|
-
import Model, { attr } from '@ember-data/model';
|
|
47
|
-
|
|
48
|
-
export default class UserModel extends Model {
|
|
49
|
-
@attr('string') username;
|
|
50
|
-
@attr('string') email;
|
|
51
|
-
|
|
52
|
-
@attr({
|
|
53
|
-
defaultValue() {
|
|
54
|
-
return {};
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
settings;
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
The `options` hash is passed as second argument to a transforms'
|
|
62
|
-
`serialize` and `deserialize` method. This allows to configure a
|
|
63
|
-
transformation and adapt the corresponding value, based on the config:
|
|
64
|
-
|
|
65
|
-
```app/models/post.js
|
|
66
|
-
import Model, { attr } from '@ember-data/model';
|
|
67
|
-
|
|
68
|
-
export default class PostModel extends Model {
|
|
69
|
-
@attr('text', {
|
|
70
|
-
uppercase: true
|
|
71
|
-
})
|
|
72
|
-
text;
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
```app/transforms/text.js
|
|
77
|
-
import Transform from '@ember-data/serializer/transform';
|
|
78
|
-
|
|
79
|
-
export default class TextTransform extends Transform {
|
|
80
|
-
serialize(value, options) {
|
|
81
|
-
if (options.uppercase) {
|
|
82
|
-
return value.toUpperCase();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return value;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
deserialize(value) {
|
|
89
|
-
return value;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
@method attr
|
|
95
|
-
@public
|
|
96
|
-
@static
|
|
97
|
-
@for @ember-data/model
|
|
98
|
-
@param {String|Object} type the attribute type
|
|
99
|
-
@param {Object} options a hash of options
|
|
100
|
-
@return {Attribute}
|
|
101
|
-
*/
|
|
102
|
-
function attr(type, options) {
|
|
103
|
-
if (typeof type === 'object') {
|
|
104
|
-
options = type;
|
|
105
|
-
type = undefined;
|
|
106
|
-
} else {
|
|
107
|
-
options = options || {};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let meta = {
|
|
111
|
-
type: type,
|
|
112
|
-
isAttribute: true,
|
|
113
|
-
options: options,
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
return computed({
|
|
117
|
-
get(key) {
|
|
118
|
-
if (DEBUG) {
|
|
119
|
-
if (['currentState'].indexOf(key) !== -1) {
|
|
120
|
-
throw new Error(
|
|
121
|
-
`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}`
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (this.isDestroyed || this.isDestroying) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
return recordDataFor(this).getAttr(recordIdentifierFor(this), key);
|
|
129
|
-
},
|
|
130
|
-
set(key, value) {
|
|
131
|
-
if (DEBUG) {
|
|
132
|
-
if (['currentState'].indexOf(key) !== -1) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}`
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
assert(
|
|
139
|
-
`Attempted to set '${key}' on the deleted record ${recordIdentifierFor(this)}`,
|
|
140
|
-
!this.currentState.isDeleted
|
|
141
|
-
);
|
|
142
|
-
const identifier = recordIdentifierFor(this);
|
|
143
|
-
const recordData = storeFor(this)._instanceCache.getRecordData(identifier);
|
|
144
|
-
let currentValue = recordData.getAttr(identifier, key);
|
|
145
|
-
if (currentValue !== value) {
|
|
146
|
-
recordData.setAttr(identifier, key, value);
|
|
147
|
-
|
|
148
|
-
if (!this.isValid) {
|
|
149
|
-
const { errors } = this;
|
|
150
|
-
if (errors.get(key)) {
|
|
151
|
-
errors.remove(key);
|
|
152
|
-
this.currentState.cleanErrorRequests();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return value;
|
|
158
|
-
},
|
|
159
|
-
}).meta(meta);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export default computedMacroWithOptionalParams(attr);
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { assert, deprecate, warn } from '@ember/debug';
|
|
2
|
-
import { computed } from '@ember/object';
|
|
3
|
-
import { dasherize } from '@ember/string';
|
|
4
|
-
import { DEBUG } from '@glimmer/env';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC,
|
|
8
|
-
DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
|
|
9
|
-
DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE,
|
|
10
|
-
} from '@ember-data/private-build-infra/deprecations';
|
|
11
|
-
|
|
12
|
-
import { lookupLegacySupport } from './model';
|
|
13
|
-
import { computedMacroWithOptionalParams } from './util';
|
|
14
|
-
|
|
15
|
-
function normalizeType(type) {
|
|
16
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
|
|
17
|
-
if (!type) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return dasherize(type);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
@module @ember-data/model
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
`belongsTo` is used to define One-To-One and One-To-Many
|
|
30
|
-
relationships on a [Model](/ember-data/release/classes/Model).
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`belongsTo` takes an optional hash as a second parameter, currently
|
|
34
|
-
supported options are:
|
|
35
|
-
|
|
36
|
-
- `async`: A boolean value used to explicitly declare this to be an async relationship. The default is true.
|
|
37
|
-
- `inverse`: A string used to identify the inverse property on a
|
|
38
|
-
related model in a One-To-Many relationship. See [Explicit Inverses](#explicit-inverses)
|
|
39
|
-
- `polymorphic` A boolean value to mark the relationship as polymorphic
|
|
40
|
-
|
|
41
|
-
#### One-To-One
|
|
42
|
-
To declare a one-to-one relationship between two models, use
|
|
43
|
-
`belongsTo`:
|
|
44
|
-
|
|
45
|
-
```app/models/user.js
|
|
46
|
-
import Model, { belongsTo } from '@ember-data/model';
|
|
47
|
-
|
|
48
|
-
export default class UserModel extends Model {
|
|
49
|
-
@belongsTo('profile') profile;
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
```app/models/profile.js
|
|
54
|
-
import Model, { belongsTo } from '@ember-data/model';
|
|
55
|
-
|
|
56
|
-
export default class ProfileModel extends Model {
|
|
57
|
-
@belongsTo('user') user;
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
#### One-To-Many
|
|
62
|
-
To declare a one-to-many relationship between two models, use
|
|
63
|
-
`belongsTo` in combination with `hasMany`, like this:
|
|
64
|
-
|
|
65
|
-
```app/models/post.js
|
|
66
|
-
import Model, { hasMany } from '@ember-data/model';
|
|
67
|
-
|
|
68
|
-
export default class PostModel extends Model {
|
|
69
|
-
@hasMany('comment') comments;
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
```app/models/comment.js
|
|
74
|
-
import Model, { belongsTo } from '@ember-data/model';
|
|
75
|
-
|
|
76
|
-
export default class CommentModel extends Model {
|
|
77
|
-
@belongsTo('post') post;
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
You can avoid passing a string as the first parameter. In that case Ember Data
|
|
82
|
-
will infer the type from the key name.
|
|
83
|
-
|
|
84
|
-
```app/models/comment.js
|
|
85
|
-
import Model, { belongsTo } from '@ember-data/model';
|
|
86
|
-
|
|
87
|
-
export default class CommentModel extends Model {
|
|
88
|
-
@belongsTo post;
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
will lookup for a Post type.
|
|
93
|
-
|
|
94
|
-
#### Sync relationships
|
|
95
|
-
|
|
96
|
-
Ember Data resolves sync relationships with the related resources
|
|
97
|
-
available in its local store, hence it is expected these resources
|
|
98
|
-
to be loaded before or along-side the primary resource.
|
|
99
|
-
|
|
100
|
-
```app/models/comment.js
|
|
101
|
-
import Model, { belongsTo } from '@ember-data/model';
|
|
102
|
-
|
|
103
|
-
export default class CommentModel extends Model {
|
|
104
|
-
@belongsTo('post', {
|
|
105
|
-
async: false
|
|
106
|
-
})
|
|
107
|
-
post;
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
In contrast to async relationship, accessing a sync relationship
|
|
112
|
-
will always return the record (Model instance) for the existing
|
|
113
|
-
local resource, or null. But it will error on access when
|
|
114
|
-
a related resource is known to exist and it has not been loaded.
|
|
115
|
-
|
|
116
|
-
```
|
|
117
|
-
let post = comment.post;
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
@method belongsTo
|
|
122
|
-
@public
|
|
123
|
-
@static
|
|
124
|
-
@for @ember-data/model
|
|
125
|
-
@param {String} modelName (optional) type of the relationship
|
|
126
|
-
@param {Object} options (optional) a hash of options
|
|
127
|
-
@return {Ember.computed} relationship
|
|
128
|
-
*/
|
|
129
|
-
function belongsTo(modelName, options) {
|
|
130
|
-
let opts = options;
|
|
131
|
-
let userEnteredModelName = modelName;
|
|
132
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
|
|
133
|
-
if (typeof modelName !== 'string' || !modelName.length) {
|
|
134
|
-
deprecate('belongsTo() must specify the string type of the related resource as the first parameter', false, {
|
|
135
|
-
id: 'ember-data:deprecate-non-strict-relationships',
|
|
136
|
-
for: 'ember-data',
|
|
137
|
-
until: '5.0',
|
|
138
|
-
since: { enabled: '4.7', available: '4.7' },
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
if (typeof modelName === 'object') {
|
|
142
|
-
opts = modelName;
|
|
143
|
-
userEnteredModelName = undefined;
|
|
144
|
-
} else {
|
|
145
|
-
opts = options;
|
|
146
|
-
userEnteredModelName = modelName;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
assert(
|
|
150
|
-
'The first argument to belongsTo must be a string representing a model type key, not an instance of ' +
|
|
151
|
-
typeof userEnteredModelName +
|
|
152
|
-
". E.g., to define a relation to the Person model, use belongsTo('person')",
|
|
153
|
-
typeof userEnteredModelName === 'string' || typeof userEnteredModelName === 'undefined'
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC) {
|
|
159
|
-
if (!opts || typeof opts.async !== 'boolean') {
|
|
160
|
-
opts = opts || {};
|
|
161
|
-
if (!('async' in opts)) {
|
|
162
|
-
opts.async = true;
|
|
163
|
-
}
|
|
164
|
-
deprecate('belongsTo(<type>, <options>) must specify options.async as either `true` or `false`.', false, {
|
|
165
|
-
id: 'ember-data:deprecate-non-strict-relationships',
|
|
166
|
-
for: 'ember-data',
|
|
167
|
-
until: '5.0',
|
|
168
|
-
since: { enabled: '4.7', available: '4.7' },
|
|
169
|
-
});
|
|
170
|
-
} else {
|
|
171
|
-
assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
|
|
178
|
-
if (opts.inverse !== null && (typeof opts.inverse !== 'string' || opts.inverse.length === 0)) {
|
|
179
|
-
deprecate(
|
|
180
|
-
'belongsTo(<type>, <options>) must specify options.inverse as either `null` or string type of the related resource.',
|
|
181
|
-
false,
|
|
182
|
-
{
|
|
183
|
-
id: 'ember-data:deprecate-non-strict-relationships',
|
|
184
|
-
for: 'ember-data',
|
|
185
|
-
until: '5.0',
|
|
186
|
-
since: { enabled: '4.7', available: '4.7' },
|
|
187
|
-
}
|
|
188
|
-
);
|
|
189
|
-
} else {
|
|
190
|
-
assert(
|
|
191
|
-
`Expected belongsTo options.inverse to be either null or the string type of the related resource.`,
|
|
192
|
-
opts.inverse === null || (typeof opts.inverse === 'string' && opts.inverse.length > 0)
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
assert(
|
|
197
|
-
`Expected belongsTo options.inverse to be either null or the string type of the related resource.`,
|
|
198
|
-
opts.inverse === null || (typeof opts.inverse === 'string' && opts.inverse.length > 0)
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
let meta = {
|
|
203
|
-
type: normalizeType(userEnteredModelName),
|
|
204
|
-
isRelationship: true,
|
|
205
|
-
options: opts,
|
|
206
|
-
kind: 'belongsTo',
|
|
207
|
-
name: 'Belongs To',
|
|
208
|
-
key: null,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
return computed({
|
|
212
|
-
get(key) {
|
|
213
|
-
// this is a legacy behavior we may not carry into a new model setup
|
|
214
|
-
// it's better to error on disconnected records so users find errors
|
|
215
|
-
// in their logic.
|
|
216
|
-
if (this.isDestroying || this.isDestroyed) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
const support = lookupLegacySupport(this);
|
|
220
|
-
|
|
221
|
-
if (DEBUG) {
|
|
222
|
-
if (['currentState'].indexOf(key) !== -1) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
`'${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()}`
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
if (Object.prototype.hasOwnProperty.call(opts, 'serialize')) {
|
|
228
|
-
warn(
|
|
229
|
-
`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`,
|
|
230
|
-
false,
|
|
231
|
-
{
|
|
232
|
-
id: 'ds.model.serialize-option-in-belongs-to',
|
|
233
|
-
}
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (Object.prototype.hasOwnProperty.call(opts, 'embedded')) {
|
|
238
|
-
warn(
|
|
239
|
-
`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`,
|
|
240
|
-
false,
|
|
241
|
-
{
|
|
242
|
-
id: 'ds.model.embedded-option-in-belongs-to',
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return support.getBelongsTo(key);
|
|
249
|
-
},
|
|
250
|
-
set(key, value) {
|
|
251
|
-
const support = lookupLegacySupport(this);
|
|
252
|
-
if (DEBUG) {
|
|
253
|
-
if (['currentState'].indexOf(key) !== -1) {
|
|
254
|
-
throw new Error(
|
|
255
|
-
`'${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()}`
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
this.store._join(() => {
|
|
260
|
-
support.setDirtyBelongsTo(key, value);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
return support.getBelongsTo(key);
|
|
264
|
-
},
|
|
265
|
-
}).meta(meta);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export default computedMacroWithOptionalParams(belongsTo);
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { assert } from '@ember/debug';
|
|
2
|
-
import { DEBUG } from '@glimmer/env';
|
|
3
|
-
|
|
4
|
-
import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@ember-data/private-build-infra/deprecations';
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
Assert that `addedRecord` has a valid type so it can be added to the
|
|
8
|
-
relationship of the `record`.
|
|
9
|
-
|
|
10
|
-
The assert basically checks if the `addedRecord` can be added to the
|
|
11
|
-
relationship (specified via `relationshipMeta`) of the `record`.
|
|
12
|
-
|
|
13
|
-
This utility should only be used internally, as both record parameters must
|
|
14
|
-
be stable record identifiers and the `relationshipMeta` needs to be the meta
|
|
15
|
-
information about the relationship, retrieved via
|
|
16
|
-
`record.relationshipFor(key)`.
|
|
17
|
-
*/
|
|
18
|
-
let assertPolymorphicType;
|
|
19
|
-
|
|
20
|
-
if (DEBUG) {
|
|
21
|
-
let checkPolymorphic = function checkPolymorphic(modelClass, addedModelClass) {
|
|
22
|
-
if (modelClass.__isMixin) {
|
|
23
|
-
return (
|
|
24
|
-
modelClass.__mixin.detect(addedModelClass.PrototypeMixin) ||
|
|
25
|
-
// handle native class extension e.g. `class Post extends Model.extend(Commentable) {}`
|
|
26
|
-
modelClass.__mixin.detect(Object.getPrototypeOf(addedModelClass).PrototypeMixin)
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return addedModelClass.prototype instanceof modelClass || modelClass.detect(addedModelClass);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
assertPolymorphicType = function assertPolymorphicType(parentIdentifier, parentDefinition, addedIdentifier, store) {
|
|
34
|
-
let asserted = false;
|
|
35
|
-
|
|
36
|
-
if (parentDefinition.inverseIsImplicit) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (parentDefinition.isPolymorphic) {
|
|
40
|
-
let meta = store.getSchemaDefinitionService().relationshipsDefinitionFor(addedIdentifier)[
|
|
41
|
-
parentDefinition.inverseKey
|
|
42
|
-
];
|
|
43
|
-
if (meta?.options?.as) {
|
|
44
|
-
asserted = true;
|
|
45
|
-
assert(
|
|
46
|
-
`The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
|
|
47
|
-
meta.options.as === parentDefinition.type
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
|
|
53
|
-
if (!asserted) {
|
|
54
|
-
store = store._store ? store._store : store; // allow usage with storeWrapper
|
|
55
|
-
let addedModelName = addedIdentifier.type;
|
|
56
|
-
let parentModelName = parentIdentifier.type;
|
|
57
|
-
let key = parentDefinition.key;
|
|
58
|
-
let relationshipModelName = parentDefinition.type;
|
|
59
|
-
let relationshipClass = store.modelFor(relationshipModelName);
|
|
60
|
-
let addedClass = store.modelFor(addedModelName);
|
|
61
|
-
|
|
62
|
-
let assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
|
|
63
|
-
let isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
|
|
64
|
-
|
|
65
|
-
assert(assertionMessage, isPolymorphic);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export { assertPolymorphicType };
|