HasOne relation with Null Value
Opened this issue · 6 comments
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
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
Can share the relationships and the table schema for these relationships
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>
anomalie_id of table "Signalements", is "NULLABLE"
hi guy, I had the same problem as you
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
}`