diff --git a/src/Lucid/Relations/BelongsTo.js b/src/Lucid/Relations/BelongsTo.js index a9159ca3..10a81c71 100644 --- a/src/Lucid/Relations/BelongsTo.js +++ b/src/Lucid/Relations/BelongsTo.js @@ -21,6 +21,26 @@ const CE = require('../../Exceptions') * @constructor */ class BelongsTo extends BaseRelation { + /** + * Returns the first row for the related model + * + * @method first + * + * @return {Object|Null} + */ + first () { + if (!this.parentInstance.$persisted) { + throw CE.RuntimeException.unSavedModel(this.parentInstance.constructor.name) + } + + if (!this.$primaryKeyValue) { + return null + } + + this._decorateQuery() + return this.relatedQuery.first() + } + /** * Map values from model instances to an array. It is required * to make `whereIn` query when eagerloading results. diff --git a/test/unit/lucid-belongs-to.spec.js b/test/unit/lucid-belongs-to.spec.js index 289c8463..ca4b6628 100644 --- a/test/unit/lucid-belongs-to.spec.js +++ b/test/unit/lucid-belongs-to.spec.js @@ -9,6 +9,7 @@ * file that was distributed with this source code. */ +require('../../lib/iocResolver').setFold(require('@adonisjs/fold')) const test = require('japa') const fs = require('fs-extra') const path = require('path') @@ -634,4 +635,104 @@ test.group('Relations | Belongs To', (group) => { assert.equal(userQuery.sql, helpers.formatQuery('select * from "users" where "id" in (?)')) assert.deepEqual(userQuery.bindings, helpers.formatBindings([1])) }) + + test('do not load relation with null value in foreign key', async (assert) => { + class User extends Model { + } + + class Car extends Model { + user () { + return this.belongsTo(User) + } + } + + User._bootIfNotBooted() + Car._bootIfNotBooted() + + let userQuery = null + let carQuery = null + User.onQuery((query) => (userQuery = query)) + Car.onQuery((query) => (carQuery = query)) + + await ioc.use('Database').table('users').insert({ username: 'virk' }) + await ioc.use('Database').table('cars').insert({ name: 'E180', model: 'Mercedes', user_id: null }) + + const car = await Car.query().select(['id', 'name', 'user_id']).where('id', 1).first() + await car.load('user') + const json = car.toJSON() + + assert.deepEqual(json, { + id: 1, + name: 'E180', + user: null, + user_id: null + }) + + assert.equal(userQuery, null) + assert.equal(carQuery.sql, helpers.formatQuery('select "id", "name", "user_id" from "cars" where "id" = ? limit ?')) + assert.deepEqual(carQuery.bindings, helpers.formatBindings([1, 1])) + }) + + test('do not eager load relation with null value in foreign key', async (assert) => { + class User extends Model { + } + + class Car extends Model { + user () { + return this.belongsTo(User) + } + } + + User._bootIfNotBooted() + Car._bootIfNotBooted() + + let userQuery = null + let carQuery = null + User.onQuery((query) => (userQuery = query)) + Car.onQuery((query) => (carQuery = query)) + + await ioc.use('Database').table('users').insert({ username: 'virk' }) + await ioc.use('Database').table('cars').insert({ name: 'E180', model: 'Mercedes', user_id: null }) + + const car = await Car + .query() + .select(['id', 'name', 'user_id']) + .where('id', 1) + .with('user') + .first() + + const json = car.toJSON() + + assert.deepEqual(json, { + id: 1, + name: 'E180', + user: null, + user_id: null + }) + + assert.equal(userQuery, null) + assert.equal(carQuery.sql, helpers.formatQuery('select "id", "name", "user_id" from "cars" where "id" = ? limit ?')) + assert.deepEqual(carQuery.bindings, helpers.formatBindings([1, 1])) + }) + + test('throw exception when not eagerloading', async (assert) => { + class User extends Model { + } + + class Car extends Model { + user () { + return this.belongsTo(User) + } + } + + User._bootIfNotBooted() + Car._bootIfNotBooted() + + await ioc.use('Database').table('users').insert({ username: 'virk' }) + await ioc.use('Database').table('cars').insert({ name: 'E180', model: 'Mercedes', user_id: null }) + + const car = await Car.query().where('id', 1).first() + const fn = () => car.user().toSQL() + assert.throw(fn, 'E_UNSAVED_MODEL_INSTANCE: Cannot process relation, since Car model is not persisted to database or relational value is undefined') + }) })