Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hasOne() and belongsTo() not working in loopback4 #2209

Closed
drplutus opened this issue Jan 3, 2019 · 8 comments
Closed

hasOne() and belongsTo() not working in loopback4 #2209

drplutus opened this issue Jan 3, 2019 · 8 comments
Labels
needs steps to reproduce Issues missing a small app and/or instructions for reproducing the problem Relations Model relations (has many, etc.)

Comments

@drplutus
Copy link

drplutus commented Jan 3, 2019

I have try with following code but i will got only first table record, there are missed second table info.

person.model.ts:

  @property({
     name: 'name',
    type: 'string',
  })
  name: string;

 @hasOne(() => Info)
  infos: Info;

info.model.ts:

@property({
   type: 'number',
   id: true,
   required: true,
 })
 id: number;

  @belongsTo(() => Person)
   personId?: number;

person.repository.ts

 export class PersonRepository extends DefaultCrudRepository<Person,typeof Person.prototype.id> 
 {
     public readonly infos: HasOneRepositoryFactory<Info,typeof Person.prototype.id>;
     constructor(@inject('datasources.db') dataSource: DbDataSource,    
             @repository.getter('InfoRepository')
               getInfoRepository: Getter<InfoRepository>,) {
       super(Person, dataSource);
       this.infos = this._createHasOneRepositoryFactoryFor('infos',getInfoRepository);
    }
 }

Current Behavior

But when i will call the api using explorer they have return following schema

Example Value
 Schema
[  {
   "id": 0,
   "name": "string"
}]

Is there any solution for this?

@drplutus
Copy link
Author

drplutus commented Jan 7, 2019

@raymondfeng Can you please help me in this if you have brief idea ?

@bajtos
Copy link
Member

bajtos commented Jan 10, 2019

Hi @drplutus, please create a small app reproducing the issue per our bug reporting instructions.

@b-admike I think you are most familiar with relations, could you PTAL?

@drplutus
Copy link
Author

drplutus commented Jan 11, 2019

I have create one simple api which is going to fetch the data from database(Mysql).

 Table 1 : person (id, person_name )
 Table 2 : info (id, person_id, address)

Table 1 is belongs to table one. Suppose we fetch data from person table then respective info table data will fetch.

person.model.ts:

import { Info } from "./info.model";

import { Entity, model, property, hasMany, hasOne } from '@loopback/repository';

@model()
export class Person extends Entity {
  @property({ type: 'number', id: true, required: true, })
  id: number;

  @property({ person_name: 'name', type: 'string', })
  person_name: string;

  @hasOne(() => Info, { keyTo: 'person_id' })
  infos: Info[];

  constructor(data?: Partial<Person>) {
    super(data);
  }
}

info.model.ts:

import { Entity, model, property, belongsTo } from '@loopback/repository';
import { Person } from "./person.model";

@model()
export class Info extends Entity {

  @property({ type: 'number', id: true, required: true, })
  id: number;

  @property({ address: 'name', description: "Info name.", type: 'string', })
  address: string;

  @belongsTo(() => Person)
  person_id: number;

  constructor(data?: Partial<Info>) {
    super(data);
  }
}

person.repository.ts

import { DefaultCrudRepository, repository, juggler, HasManyRepositoryFactory, HasOneRepositoryFactory } from '@loopback/repository';
import { InfoRepository } from './info.repository';

import { Person, Info } from '../models';
import { DbDataSource } from '../datasources';
import { inject, Getter } from '@loopback/core';

export class PersonRepository extends DefaultCrudRepository<Person, typeof Person.prototype.id> {
  public infos: HasOneRepositoryFactory<Info, typeof Person.prototype.id>;
  constructor(
    @inject('datasources.db') dataSource: DbDataSource, @repository.getter('InfoRepository')
    protected infoRepositoryGetter: Getter<InfoRepository>
  ) {
    super(Person, dataSource);
    this.infos = this._createHasOneRepositoryFactoryFor('infos', infoRepositoryGetter);
  }
}

Api code which is called from controller

@get('/people', {
    responses: {
      '200': {
        description: 'Array of Person model instances',
        content: {
          'application/json': {
            schema: { type: 'array', items: { 'x-ts-type': Person } },
          },
        },
      },
    },
  })
  async find(
    @param.query.object('filter', getFilterSchemaFor(Person)) filter?: Filter): Promise<Person[]> {
    return await this.personRepository.find(filter);
  }

It will return error : 500 TypeError: Cannot read property 'target' of undefined

Just i want to get person information with address.

@bajtos
Copy link
Member

bajtos commented Jan 18, 2019

First of all, filter.include is not supported in LB4 yet, see #1352 and the spike/proof-of-concept in #2124.

But when i will call the api using explorer they have return following schema

Generating the correct schema for different CRUD endpoints is a bit tricky:

  • When creating a new "Person" model or editing an existing one, the payload must not contain "info" property because LB4 does not support creating related models.
  • When querying "Person" models, the response may contain "info" property with the related data.

I think we need two schemas, possibly even two TypeScript models/interfaces. Please refer to discussion in #2124 for more details.

/cc @jannyHou @b-admike

It will return error : 500 TypeError: Cannot read property 'target' of undefined

Sorry, I don't have bandwidth to re-create a working LB4 application from your instructions and thus I cannot look into details of the error you are encountering. Can you please follow the instructions in https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-4x-bugs and provide us with a LB4 app that we can simply git clone and then run to reproduce the problem.

@mapmalith
Copy link

try,
@hasOne(() => Info) infos: Info[];

instead of
@hasOne(() => Info, { keyTo: 'person_id' }) infos: Info[];

may be it will work

@bajtos bajtos removed their assignment May 24, 2019
@bajtos bajtos added the needs steps to reproduce Issues missing a small app and/or instructions for reproducing the problem label May 24, 2019
@agnes512
Copy link
Contributor

I spot some problems that might crash the app:

  1. The type of hasOne property is an object instead of an array( has one).
  @hasOne(() => Info, { keyTo: 'person_id' })
  infos: Info;  // here
  1. You are not using the default foreign key names (i.e person_id in your case. default is personId).
    To customize it, you need to do the following:
    in Person.model:
  @hasOne(() => Info, { keyTo: 'person_id' })
  infos: Info;

in Info.model:

  @belongsTo(() => Customer, {name: 'person'}) // `person` is the name of your belongsTo relation name
  person_id?: number;

Ref: HasOne relation customizing name

Also, we support inclusion for relations now :D check out Querying related models on our site!

@agnes512
Copy link
Contributor

agnes512 commented Jan 8, 2020

Feel free to re-open the issue if it's not solved yet. Thanks!

@agnes512 agnes512 closed this as completed Jan 8, 2020
@odykyi

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs steps to reproduce Issues missing a small app and/or instructions for reproducing the problem Relations Model relations (has many, etc.)
Projects
None yet
Development

No branches or pull requests

5 participants