Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/vuex-orm-graphql.es5.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/vuex-orm-graphql.es5.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/vuex-orm-graphql.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/vuex-orm-graphql.umd.js.map

Large diffs are not rendered by default.

59 changes: 56 additions & 3 deletions docs/guide/eager-loading.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Eager Loading
# Eager Loading and Saving

[[toc]]

All `belongsTo` and `hasOne` related entities are eager loaded when `fetch` is called. All other related entities have to
be added to a static field in the model called `eagerLoad` to have them eagerly loaded with fetch.

## Eager Loading

All `belongsTo`, `hasOne` and `morphOne` related records are eager loaded when `fetch` is called.
All other related records have to be added to a static field in the model called `eagerLoad` to
have them eagerly loaded with fetch.

Example:

Expand All @@ -21,3 +25,52 @@ class User extends Model {
}
}
}
```

## Eager Saving

Similar to the eager loading there is a "eager saving". When saving (via `$persist` or `$push`) a
record will automatically sends all `belongsTo` related records too to the server.

All other related records have to be added to a static field in the model called `eagerSave` to
have them eagerly saved with persist and push.

```javascript{4}
class User extends Model {
static entity = 'users';
static eagerLoad = ['posts'];
static eagerSave = ['posts'];

static fields () {
return {
id: this.attr(null),
name: this.attr(''),

posts: this.hasMany(Post, 'userId')
}
}
}
```


## Eager Syncing

`eagerSync` combines these two fields. Adding a relation to this array will make it eagerly loaded
and saved:


```javascript{3}
class User extends Model {
static entity = 'users';
static eagerSync = ['posts'];

static fields () {
return {
id: this.attr(null),
name: this.attr(''),

posts: this.hasMany(Post, 'userId')
}
}
}
```
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"app": "latest"
},
"peerDependencies": {
"@vuex-orm/core": "^0.31.11"
"@vuex-orm/core": "^0.31.12"
},
"devDependencies": {
"@commitlint/cli": "^7.1.2",
Expand All @@ -65,7 +65,7 @@
"@types/jest": "^23.3.2",
"@types/node": "^10.11.0",
"@types/sinon": "^5.0.1",
"@vuex-orm/core": "^0.31.11",
"@vuex-orm/core": "^0.31.12",
"apollo-client": "^2.2.2",
"apollo-link": "^1.2.0",
"apollo-link-http": "^1.3.2",
Expand Down
4 changes: 2 additions & 2 deletions src/actions/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default class Action {
* @returns {Arguments}
*/
static addRecordToArgs(args: Arguments, model: Model, data: Data): Arguments {
args[model.singularName] = Transformer.transformOutgoingData(model, data);
args[model.singularName] = Transformer.transformOutgoingData(model, data, false);
return args;
}

Expand All @@ -121,7 +121,7 @@ export default class Action {

if (value instanceof context.components.Model) {
const model = context.getModel(singularize(value.$self().entity));
const transformedValue = Transformer.transformOutgoingData(model, value);
const transformedValue = Transformer.transformOutgoingData(model, value, false);
context.logger.log(
"A",
key,
Expand Down
1 change: 1 addition & 0 deletions src/actions/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default class Fetch extends Action {
filter = Transformer.transformOutgoingData(
model,
params.filter as Data,
true,
Object.keys(params.filter)
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/actions/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default class Query extends Action {
const schema: Schema = await context.loadSchema();

// Filter
filter = filter ? Transformer.transformOutgoingData(model, filter as Data) : {};
filter = filter ? Transformer.transformOutgoingData(model, filter as Data, true) : {};

// Multiple?
const multiple: boolean = Schema.returnsConnection(schema.getQuery(name)!);
Expand Down
45 changes: 11 additions & 34 deletions src/graphql/query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export default class QueryBuilder {
if (isPlainObject(value) && value.__type) {
// Case 2 (User!)
typeOrValue = context.adapter.getInputTypeName(context.getModel(value.__type)) + "!";
} else if (Array.isArray(value) && field) {
} else if (value instanceof Array && field) {
const arg = QueryBuilder.findSchemaFieldForArgument(key, field, model, filter);

/* istanbul ignore next */
Expand Down Expand Up @@ -353,38 +353,8 @@ export default class QueryBuilder {
const context = Context.getInstance();
const relationQueries: Array<string> = [];

model.getRelations().forEach((field: Field, name: string) => {
let relatedModel: Model;
let fieldAsRelation: Relation = field as Relation;

if (
fieldAsRelation instanceof context.components.BelongsToMany ||
fieldAsRelation instanceof context.components.HasMany ||
fieldAsRelation instanceof context.components.HasManyThrough ||
fieldAsRelation instanceof context.components.MorphedByMany ||
fieldAsRelation instanceof context.components.MorphMany ||
fieldAsRelation instanceof context.components.MorphOne ||
fieldAsRelation instanceof context.components.MorphToMany ||
fieldAsRelation instanceof context.components.HasOne
) {
relatedModel = context.getModel(fieldAsRelation.related.entity);
} else if (
fieldAsRelation instanceof context.components.BelongsTo ||
fieldAsRelation instanceof context.components.HasManyBy
) {
relatedModel = context.getModel(fieldAsRelation.parent.entity);
} else if (fieldAsRelation instanceof context.components.MorphTo) {
relatedModel = context.getModel(fieldAsRelation.type);

/* istanbul ignore next */
} else {
relatedModel = context.getModel(name);

context.logger.log(
"WARNING: unknown field type. Fallback to attribute name",
fieldAsRelation
);
}
model.getRelations().forEach((field: Relation, name: string) => {
let relatedModel: Model = Model.getRelatedModel(field)!;

// We will ignore the field, when it's already in the path. Means: When it's already queried. However there are
// cases where the model will have a relationship to itself. For example a nested category strucure where the
Expand All @@ -405,7 +375,14 @@ export default class QueryBuilder {
newPath.push(relatedModel.singularName);

relationQueries.push(
this.buildField(relatedModel, Model.isConnection(field), undefined, newPath, name, false)
this.buildField(
relatedModel,
Model.isConnection(field as Field),
undefined,
newPath,
name,
false
)
);
}
});
Expand Down
17 changes: 9 additions & 8 deletions src/graphql/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,18 @@ export default class Schema {
}

static getTypeNameOfField(field: GraphQLField): string {
const type = this.getRealType(field.type);
let type = this.getRealType(field.type);

if (type.kind === "LIST") {
return `[${type.ofType.name}]`;
}
while (!type.name) type = type.ofType;
return `[${type.name}]`;
} else {
while (!type.name) type = type.ofType;

const name = type.name || type.ofType.name || type.ofType.ofType.name;
/* istanbul ignore next */
if (!type.name) throw new Error(`Can't find type name for field ${field.name}`);

/* istanbul ignore next */
if (!name) throw new Error(`Can't find type name for field ${field.name}`);

return name;
return type.name;
}
}
}
Loading