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 relation with Null Value #1049

Open
sergefabre opened this issue Aug 29, 2024 · 6 comments
Open

HasOne relation with Null Value #1049

sergefabre opened this issue Aug 29, 2024 · 6 comments

Comments

@sergefabre
Copy link

Package version

18.4.0

Describe the bug

the Doc say :

Preload relationship
Preloading allows you to fetch the relationship data alongside the main query. For example: Select all the users and preload their profiles at the same time.

The preload method accepts the name of the relationship defined on the model.
The relationship property value for the hasOne and the belongsTo relationship is either set to the related model instance or null when no records are found. The relationship property value is an array of the related model instance for all other relationship types.

but, with hasone, the relation not work with null value. It's OK with relation BelongTo

Reproduction repo

https://github.com/adonisjs/lucid/blob/v18.4.0/src/Orm/Relations/HasOne/QueryBuilder.ts#L84

@ThomasPrioul
Copy link

ThomasPrioul commented Aug 29, 2024

Hi, I'm a colleague of @sergefabre, I'll try to add some details about our problem. We're using the preload method on a query builder (Signalement).

let sql = Signalement.query().preload('anomalie', (ano) =>
  ano.select(['id', 'statut', 'commentaireCloture', 'timestampDerniereModif'])
)

Here is the queryBuilder code for hasOne relationship:
https://github.com/adonisjs/lucid/blob/v18.4.0/src/Orm/Relations/HasOne/QueryBuilder.ts#L84

And here the queryBuilder code for belongsTo relationship:
https://github.com/adonisjs/lucid/blob/v18.4.0/src/Orm/Relations/BelongsTo/QueryBuilder.ts#L86

It is clear that in the hasOne relationship, a value equal to null or undefined throws an exception, in the getValue() method as it uses ensureValue method:
https://github.com/adonisjs/lucid/blob/v18.4.0/src/utils/index.ts#L45C1-L53C2

export function ensureValue(collection: any, key: string, missingCallback: () => void) {
  const value = collection[key]
  if (value === undefined || value === null) {
    missingCallback()
    return
  }


  return value
}

Whereas the belongsTo relationship only checks for undefined values and allows null values.
https://github.com/adonisjs/lucid/blob/v18.4.0/src/Orm/Relations/BelongsTo/QueryBuilder.ts#L98C7-L105C11

const foreignKeyValues = this.parent
  .map((model) => model[this.relation.foreignKey])
  .filter((foreignKeyValue) => {
    if (foreignKeyValue === undefined) {
      this.raiseMissingForeignKey()
    }
    return foreignKeyValue !== null
  })

This is not what is mentioned in the docs : https://v5-docs.adonisjs.com/guides/models/relationships#preload-relationship

We found the related question in SO, applied the answer (using belongsTo relationship instead of hasOne) and now it works.
https://stackoverflow.com/questions/64094574/adonisjs-5-preload-model-even-if-foreign-key-is-null

@thetutlage what are your thoughts on this ? Is this by design ? Perhaps the docs should mention this explicitly.

Kind regards

@thetutlage
Copy link
Member

Can share the relationships and the table schema for these relationships

@sergefabre
Copy link
Author

sergefabre commented Aug 29, 2024

image

Model Signalement

 @belongsTo(() => Anomalie, {
    foreignKey: 'anomalieId',
    localKey: 'id',
    onQuery: (ano) => {
      if (ano.client.dialect.name === 'postgres') ano.withSchema(Anomalie.schema)
    },
  })
  public anomalie: BelongsTo<typeof Anomalie>`

Model Anomalie :

  @column({ isPrimary: true }) public id: number
  @column.dateTime({ autoCreate: true }) public createdAt: DateTime
  @column.dateTime({ autoCreate: true, autoUpdate: true }) public updatedAt: DateTime

  @column() public statut: string
  @column() public commentaireCloture: string
  @column.dateTime() public timestampDerniereModif: DateTime

  @hasMany(() => Signalement, {
    localKey: 'id',
    foreignKey: 'anomalieId',
  })
  public signalements: HasMany<typeof Signalement>

@sergefabre
Copy link
Author

anomalie_id of table "Signalements", is "NULLABLE"

@Kai-Lin
Copy link

Kai-Lin commented Dec 18, 2024

hi guy, I had the same problem as you

@sergefabre
Copy link
Author

hi
With this code, it works

`Vine.macro('defaultSetNull', addNullableToAny)
VineString.macro('defaultSetNull', addNullableToAny)
VineNumber.macro('defaultSetNull', addNullableToAny)
VineDate.macro('defaultSetNull', addNullableToAny)

/**

  • Informing TypeScript about the newly added method
    */
    declare module '@vinejs/vine' {
    interface Vine {
    defaultSetNull<A, B, C>(
    type: BaseLiteralType<A, B, C>
    ): NullableModifier<BaseLiteralType<A, B, C>>
    }

interface VineString {
defaultSetNull(): NullableModifier
}
interface VineNumber {
defaultSetNull(): NullableModifier
}

interface VineDate {
defaultSetNull(): NullableModifier
}`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants