From 55ca1928ee0071ae76ff01672037bfa0d007004f Mon Sep 17 00:00:00 2001 From: Javad Zobeidi Date: Tue, 11 Nov 2025 02:49:02 +0330 Subject: [PATCH 1/3] fix(orm): resolve concurrent modification error with multiple `include()` calls #209 --- lib/src/database/orm/model.dart | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/src/database/orm/model.dart b/lib/src/database/orm/model.dart index ff0f327..e2d15f0 100644 --- a/lib/src/database/orm/model.dart +++ b/lib/src/database/orm/model.dart @@ -701,10 +701,11 @@ abstract class Model extends QueryBuilderImpl { if (!_relationsRegistered) { registerRelations(); _relationsRegistered = true; - final rela = _relations[relation]; - if (rela != null && rela is MorphTo) { - where(rela.morphType, '=', rela.type!); - } + } + + final rela = _relations[relation]; + if (rela != null && rela is MorphTo) { + where(rela.morphType, '=', rela.type!); } _withRelation.add(_RelationQuery(relation, callback)); @@ -712,11 +713,7 @@ abstract class Model extends QueryBuilderImpl { } void _clearWithRelation(_RelationQuery r) { - if (_withRelation.length == 1) { - _withRelation = []; - } else { - _withRelation.remove(r); - } + _withRelation = _withRelation.where((item) => item != r).toList(); } Future _eagerLoadRelation( @@ -812,7 +809,9 @@ abstract class Model extends QueryBuilderImpl { List> result, ) async { if (_withRelation.isNotEmpty) { - for (_RelationQuery relation in _withRelation) { + final relationsToLoad = List<_RelationQuery>.from(_withRelation); + + for (_RelationQuery relation in relationsToLoad) { await _eagerLoadRelation(result, relation, (callBackResult) { result = callBackResult; }); From 0315d8e3ca8fda62a107a0c689ebeb24130c4b13 Mon Sep 17 00:00:00 2001 From: Javad Zobeidi Date: Tue, 11 Nov 2025 03:41:42 +0330 Subject: [PATCH 2/3] fix(orm): correct eager loading for chained BelongsTo relations --- lib/src/database/orm/model.dart | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/src/database/orm/model.dart b/lib/src/database/orm/model.dart index e2d15f0..3a70ff7 100644 --- a/lib/src/database/orm/model.dart +++ b/lib/src/database/orm/model.dart @@ -771,7 +771,16 @@ abstract class Model extends QueryBuilderImpl { } } } else { - Set ids = models.map((m) => m[rela.localKey]).toSet(); + late final String getLocalKey; + if (rela is BelongsTo) { + getLocalKey = + rela.foreignKey ?? + '${rela.related.runtimeType.toString()}_id'.toLowerCase(); + } else { + getLocalKey = rela.localKey; + } + + Set ids = models.map((m) => m[getLocalKey]).toSet(); if (rela is BelongsToMany) { qb = @@ -793,15 +802,16 @@ abstract class Model extends QueryBuilderImpl { qb = qb.whereIn(rela.foreignKey!, ids.toList()) as Model; } } + late final List> results; + if (wr.length > 1) { wr.removeAt(0); - var results = await qb.include(wr.join('.')).get(); - callBack(rela.match(models, results, primaryRelation)); + results = await qb.include(wr.join('.')).get(); } else { - var results = await qb.get(); - - callBack(rela.match(models, results, primaryRelation)); + results = await qb.get(); } + + callBack(rela.match(models, results, primaryRelation)); } } From d817d59bd122b2ef8920e28592de8d9f3ac220de Mon Sep 17 00:00:00 2001 From: Javad Zobeidi Date: Tue, 11 Nov 2025 03:43:19 +0330 Subject: [PATCH 3/3] feat(orm): support selective column loading in `include()` Allows specifying columns for eager-loaded relations using the `relation:col1,col2` syntax to improve query performance. --- lib/src/database/orm/model.dart | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/src/database/orm/model.dart b/lib/src/database/orm/model.dart index 3a70ff7..9541ec1 100644 --- a/lib/src/database/orm/model.dart +++ b/lib/src/database/orm/model.dart @@ -728,6 +728,23 @@ abstract class Model extends QueryBuilderImpl { String primaryRelation = wr.first; + List getColumns = ['*']; + final relationParts = primaryRelation.split(':'); + + if (relationParts.length > 1) { + primaryRelation = relationParts.first.trim(); + + final columnsString = relationParts.last.trim(); + + if (columnsString.isNotEmpty) { + getColumns = columnsString + .split(',') + .map((col) => col.trim()) + .where((col) => col.isNotEmpty) + .toList(); + } + } + if (!_relations.containsKey(primaryRelation)) { throw InvalidArgumentException( 'Relation $relation not found in $runtimeType', @@ -806,9 +823,9 @@ abstract class Model extends QueryBuilderImpl { if (wr.length > 1) { wr.removeAt(0); - results = await qb.include(wr.join('.')).get(); + results = await qb.include(wr.join('.')).get(getColumns); } else { - results = await qb.get(); + results = await qb.get(getColumns); } callBack(rela.match(models, results, primaryRelation));