diff --git a/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md b/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md index d4d95f3f23a5..f70d480293c8 100644 --- a/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md +++ b/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md @@ -87,70 +87,52 @@ export class TodoListRepository extends DefaultCrudRepository< ### Inclusion of Related Models -To get the related `Todo` object for each `TodoList`, we have to override the -`find` and `findById` functions. +To get the related `Todo` objects for each `TodoList`, we can use register a +custom +[`InclusionResolver`](https://loopback.io/doc/en/lb4/apidocs.repository.inclusionresolver.html) +in the `TodoList` repository. -First add the following imports: +First, add the following import: ```ts -import {Filter, Options} from '@loopback/repository'; -import {TodoListWithRelations} from '../models'; +import {InclusionResolver} from '@loopback/repository'; ``` -Add the following two functions after the constructor: - -{% include code-caption.html content="src/repositories/todo-list.repository.ts" %} +Next, in the constructor, add the following custom resolver: ```ts -async find( - filter?: Filter, - options?: Options, -): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - const result = await super.find(filter, options); - - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todos in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todos') { - await Promise.all( - result.map(async r => { - r.todos = await this.todos(r.id).find(); - }), - ); - } - - return result; +export class TodoListRepository extends DefaultCrudRepository { + // ... + constructor( + //db, relation factories setup + + // add the following code to build a custom resolver + const todosResolver: InclusionResolver = async todoLists => { + const todos: Todo[][] = []; + for (const todoList of todoLists) { + const todo = await this.todos(todoList.id).find(); + todos.push(todo); + } + + return todos; + }; + // the resolver needs to be registered before using + this.registerInclusionResolver('todos', todosResolver); + ) } +``` -async findById( - id: typeof TodoList.prototype.id, - filter?: Filter, - options?: Options, -): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - - const result = await super.findById(id, filter, options); - - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todos in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todos') { - result.todos = await this.todos(result.id).find(); - } +After that, we need to register this resolver to the repository class, which we +can do as follows: - return result; -} +```ts +this.registerInclusionResolver('todos', todosResolver); ``` +{% include note.html content=" +This is a temporary implementation until we implement our relation resolvers. See [GitHub issue #3450](https://github.com/strongloop/loopback-next/issues/3450) for details. +" %} + Now when you get a `TodoList`, a `todos` property will be included that contains your related `Todo`s, for example: @@ -174,53 +156,30 @@ Let's do the same on the `TodoRepository`: {% include code-caption.html content="src/repositories/todo.repository.ts" %} ```ts -async find( - filter?: Filter, - options?: Options, -): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - - const result = await super.find(filter, options); - - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todo-lists in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todoList') { - await Promise.all( - result.map(async r => { - r.todoList = await this.todoList(r.id); - }), - ); - } +// .. other imports +import {InclusionResolver} from '@loopback/repository'; +``` - return result; -} +```ts +export class TodoRepository extends DefaultCrudRepository { + // ... + constructor( + //db, factories setup -async findById( - id: typeof Todo.prototype.id, - filter?: Filter, - options?: Options, -): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - - const result = await super.findById(id, filter, options); - - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todo-lists in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todoList') { - result.todoList = await this.todoList(result.id); - } + // add the following code to build/register a custom resolver + const todoListResolver: InclusionResolver = async todos => { + const todoLists = []; + + for (const todo of todos) { + const todoList = await this.todoList(todo.id); + todoLists.push(todoList); + } + + return todoLists; + }; - return result; + this.registerInclusionResolver('todoList', todoListResolver); + ) } ``` diff --git a/examples/todo-list/src/repositories/todo-list-image.repository.ts b/examples/todo-list/src/repositories/todo-list-image.repository.ts index bfbae8aa0a81..32c38a197238 100644 --- a/examples/todo-list/src/repositories/todo-list-image.repository.ts +++ b/examples/todo-list/src/repositories/todo-list-image.repository.ts @@ -7,17 +7,11 @@ import {Getter, inject} from '@loopback/core'; import { BelongsToAccessor, DefaultCrudRepository, - Filter, - Options, + InclusionResolver, repository, } from '@loopback/repository'; import {DbDataSource} from '../datasources'; -import { - TodoList, - TodoListImage, - TodoListImageRelations, - TodoListImageWithRelations, -} from '../models'; +import {TodoList, TodoListImage, TodoListImageRelations} from '../models'; import {TodoListRepository} from './todo-list.repository'; export class TodoListImageRepository extends DefaultCrudRepository< @@ -39,55 +33,23 @@ export class TodoListImageRepository extends DefaultCrudRepository< 'todoList', todoListRepositoryGetter, ); - } - - async find( - filter?: Filter, - options?: Options, - ): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - - const result = await super.find(filter, options); - - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todo-lists in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todoList') { - await Promise.all( - result.map(async r => { - // eslint-disable-next-line require-atomic-updates - r.todoList = await this.todoList(r.id); - }), - ); - } - - return result; - } - async findById( - id: typeof TodoListImage.prototype.id, - filter?: Filter, - options?: Options, - ): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; + // this is a temporary implementation until + // https://github.com/strongloop/loopback-next/issues/3450 is landed + const todoListResolver: InclusionResolver< + TodoListImage, + TodoList + > = async images => { + const todoLists = []; - const result = await super.findById(id, filter, options); + for (const image of images) { + const todoList = await this.todoList(image.id); + todoLists.push(todoList); + } - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todo-lists in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todoList') { - result.todoList = await this.todoList(result.id); - } + return todoLists; + }; - return result; + this.registerInclusionResolver('todoList', todoListResolver); } } diff --git a/examples/todo-list/src/repositories/todo-list.repository.ts b/examples/todo-list/src/repositories/todo-list.repository.ts index 8c138917011d..50ed81841999 100644 --- a/examples/todo-list/src/repositories/todo-list.repository.ts +++ b/examples/todo-list/src/repositories/todo-list.repository.ts @@ -6,20 +6,13 @@ import {Getter, inject} from '@loopback/core'; import { DefaultCrudRepository, - Filter, HasManyRepositoryFactory, HasOneRepositoryFactory, + InclusionResolver, juggler, - Options, repository, } from '@loopback/repository'; -import { - Todo, - TodoList, - TodoListImage, - TodoListRelations, - TodoListWithRelations, -} from '../models'; +import {Todo, TodoList, TodoListImage, TodoListRelations} from '../models'; import {TodoListImageRepository} from './todo-list-image.repository'; import {TodoRepository} from './todo.repository'; @@ -49,62 +42,49 @@ export class TodoListRepository extends DefaultCrudRepository< 'todos', todoRepositoryGetter, ); + + // this is a temporary implementation until + // https://github.com/strongloop/loopback-next/issues/3450 is landed + const todosResolver: InclusionResolver< + TodoList, + Todo + > = async todoLists => { + const todos: Todo[][] = []; + for (const todoList of todoLists) { + const todo = await this.todos(todoList.id).find(); + todos.push(todo); + } + + return todos; + }; + + this.registerInclusionResolver('todos', todosResolver); + this.image = this.createHasOneRepositoryFactoryFor( 'image', todoListImageRepositoryGetter, ); - } - public findByTitle(title: string) { - return this.findOne({where: {title}}); - } + // this is a temporary implementation until + // https://github.com/strongloop/loopback-next/issues/3450 is landed + const imageResolver: InclusionResolver< + TodoList, + TodoListImage + > = async todoLists => { + const images = []; - async find( - filter?: Filter, - options?: Options, - ): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - const result = await super.find(filter, options); + for (const todoList of todoLists) { + const image = await this.image(todoList.id).get(); + images.push(image); + } - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todos in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todos') { - await Promise.all( - result.map(async r => { - // eslint-disable-next-line require-atomic-updates - r.todos = await this.todos(r.id).find(); - }), - ); - } + return images; + }; - return result; + this.registerInclusionResolver('image', imageResolver); } - async findById( - id: typeof TodoList.prototype.id, - filter?: Filter, - options?: Options, - ): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - - const result = await super.findById(id, filter, options); - - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todos in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todos') { - result.todos = await this.todos(result.id).find(); - } - - return result; + public findByTitle(title: string) { + return this.findOne({where: {title}}); } } diff --git a/examples/todo-list/src/repositories/todo.repository.ts b/examples/todo-list/src/repositories/todo.repository.ts index b374b6e5dfe9..92720221a27f 100644 --- a/examples/todo-list/src/repositories/todo.repository.ts +++ b/examples/todo-list/src/repositories/todo.repository.ts @@ -7,12 +7,11 @@ import {Getter, inject} from '@loopback/core'; import { BelongsToAccessor, DefaultCrudRepository, - Filter, + InclusionResolver, juggler, - Options, repository, } from '@loopback/repository'; -import {Todo, TodoList, TodoRelations, TodoWithRelations} from '../models'; +import {Todo, TodoList, TodoRelations} from '../models'; import {TodoListRepository} from './todo-list.repository'; export class TodoRepository extends DefaultCrudRepository< @@ -36,55 +35,20 @@ export class TodoRepository extends DefaultCrudRepository< 'todoList', todoListRepositoryGetter, ); - } - - async find( - filter?: Filter, - options?: Options, - ): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; - - const result = await super.find(filter, options); - - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todo-lists in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todoList') { - await Promise.all( - result.map(async r => { - // eslint-disable-next-line require-atomic-updates - r.todoList = await this.todoList(r.id); - }), - ); - } - - return result; - } - async findById( - id: typeof Todo.prototype.id, - filter?: Filter, - options?: Options, - ): Promise { - // Prevent juggler for applying "include" filter - // Juggler is not aware of LB4 relations - const include = filter && filter.include; - filter = {...filter, include: undefined}; + // this is a temporary implementation until + // https://github.com/strongloop/loopback-next/issues/3450 is landed + const todoListResolver: InclusionResolver = async todos => { + const todoLists = []; - const result = await super.findById(id, filter, options); + for (const todo of todos) { + const todoList = await this.todoList(todo.id); + todoLists.push(todoList); + } - // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo - // and use `inq` operator to fetch related todo-lists in fewer DB queries - // this is a temporary implementation, please see - // https://github.com/strongloop/loopback-next/issues/3195 - if (include && include.length && include[0].relation === 'todoList') { - result.todoList = await this.todoList(result.id); - } + return todoLists; + }; - return result; + this.registerInclusionResolver('todoList', todoListResolver); } }