Skip to content

Commit 3b9c832

Browse files
committed
ORM model lists now can contain other ORM models
1 parent de33782 commit 3b9c832

10 files changed

+634
-247
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
0.2.1
2+
-----
3+
4+
- Added support for ORM models as list members
5+
16
0.2.0
27
-----
38

lib/src/annotations.dart

+44-13
Original file line numberDiff line numberDiff line change
@@ -89,19 +89,50 @@ class AnnotationsParser {
8989
// to create additional tables for connecting those references
9090
if (table.hasReferenceFields) {
9191
for (Field f in table.fields) {
92-
if (f is ListReferenceField) {
93-
ListReferenceTable fieldReferenceTable =
94-
new ListReferenceTable(table, f);
95-
96-
f.referenceTable = fieldReferenceTable;
92+
if (f is ListJoinField) {}
93+
}
94+
}
95+
}
96+
}
97+
}
9798

98-
ormClasses[fieldReferenceTable.tableName] = fieldReferenceTable;
99-
}
99+
// When all types with annotations processed we need to go over all
100+
// tables to find references to other tables and set [Table] instance
101+
// on such references
102+
Map tablesToAdd = {};
103+
for (Table t in ormClasses.values) {
104+
if (t.hasReferenceFields) {
105+
for (Field f in t.fields) {
106+
if (f is ListJoinField) {
107+
// check if field is a reference to another model table
108+
var listGenericModelTable =
109+
AnnotationsParser.getTableForType(f.generic.reflectedType);
110+
111+
// If list members are simple dart types -
112+
// construct [ListJoinValuesTable] which will store values
113+
// in-place.
114+
if (listGenericModelTable == null) {
115+
ListJoinValuesTable fieldJoinTable =
116+
new ListJoinValuesTable(t, f);
117+
f.joinTable = fieldJoinTable;
118+
} else {
119+
// If list members are other ORM models -
120+
// construct [ListJoinModelsTable] which will
121+
// store primary key references between original [Table]
122+
// and list members.
123+
ListJoinModelsTable fieldJoinTable =
124+
new ListJoinModelsTable(t, f, listGenericModelTable);
125+
f.joinTable = fieldJoinTable;
100126
}
127+
128+
tablesToAdd[f.joinTable.tableName] = f.joinTable;
101129
}
102130
}
103131
}
104132
}
133+
for (String tableName in tablesToAdd.keys) {
134+
ormClasses[tableName] = tablesToAdd[tableName];
135+
}
105136
}
106137

107138
/// Iterates through all class declarations in current isolate
@@ -134,8 +165,7 @@ class AnnotationsParser {
134165
String modelClassName = MirrorSystem.getName(modelMirror.simpleName);
135166

136167
if (!ormClasses.containsKey(modelClassName)) {
137-
throw new Exception(
138-
'Can\'t find ORM annotations for class $modelClassName');
168+
return null;
139169
}
140170

141171
return ormClasses[modelClassName];
@@ -174,8 +204,8 @@ class AnnotationsParser {
174204
/// Lists (or arrays) are implemented by creating separate table for
175205
/// all list values with reference to original record by primary key.
176206
if (fieldDartTypeName == 'List') {
177-
field = new ListReferenceField();
178-
(field as ListReferenceField).generic = fieldMirror.type.typeArguments[0];
207+
field = new ListJoinField();
208+
(field as ListJoinField).generic = fieldMirror.type.typeArguments[0];
179209
} else {
180210
field = new Field();
181211
}
@@ -188,13 +218,13 @@ class AnnotationsParser {
188218
if (annotationTypeName == "DBFieldPrimaryKey") {
189219
field.isPrimaryKey = true;
190220

191-
if (field is ListReferenceField) {
221+
if (field is ListJoinField) {
192222
throw new StateError('List fields could not be primary keys.');
193223
}
194224
} else if (annotationTypeName == "DBFieldType") {
195225
field.type = annotationMirror.reflectee.type;
196226

197-
if (field is ListReferenceField) {
227+
if (field is ListJoinField) {
198228
throw new StateError(
199229
'Field type could not be overriden for List fields');
200230
}
@@ -219,6 +249,7 @@ class AnnotationsParser {
219249
* Scans DB* annotations on class fields and constructs DBTableSQL instance
220250
*/
221251
static Table constructTable(ClassMirror modelClassMirror) => new Table()
252+
..modelType = modelClassMirror.reflectedType
222253
..className = _getTableName(modelClassMirror)
223254
..fields = _getFields(modelClassMirror);
224255

lib/src/model.dart

+105-14
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class Model {
104104
}
105105

106106
class FindBase extends Select {
107-
final Type _modelType;
107+
Type _modelType;
108108
final Table table;
109109

110110
FindBase(Type modelType)
@@ -131,6 +131,15 @@ class FindBase extends Select {
131131

132132
static Future<List> _executeFind(Type modelType, Select selectSql) async {
133133
Table modelTable = AnnotationsParser.getTableForType(modelType);
134+
135+
if (modelTable == null) {
136+
ClassMirror modelMirror = reflectClass(modelType);
137+
String modelClassName = MirrorSystem.getName(modelMirror.simpleName);
138+
139+
throw new Exception(
140+
'Can\'t find ORM annotations for class $modelClassName');
141+
}
142+
134143
ClassMirror modelMirror = reflectClass(modelType);
135144

136145
List<dynamic> foundInstances = new List<dynamic>();
@@ -147,7 +156,7 @@ class FindBase extends Select {
147156
for (Field field in modelTable.fields) {
148157
var fieldValue = row[field.fieldName];
149158

150-
if (field is ListReferenceField) {
159+
if (field is ListJoinField) {
151160
fieldValue = []; // just create new list. It will be populated later.
152161
}
153162

@@ -161,17 +170,36 @@ class FindBase extends Select {
161170
foundInstances.add(newInstance.reflectee);
162171
}
163172

164-
if (hasReferenceFields) {
165-
for (Field field in modelTable.fields) {
166-
if (field is ListReferenceField) {
167-
ListReferenceField listField = field;
168-
ListReferenceTable listTable = field.referenceTable;
173+
foundInstances = await _populate(foundInstances);
174+
175+
return foundInstances;
176+
}
177+
178+
static Future _populate(List modelsFound) async {
179+
if (modelsFound.length < 1) {
180+
return modelsFound;
181+
}
182+
183+
Table modelTable = AnnotationsParser.getTableForInstance(modelsFound[0]);
184+
Field modelPrimaryKeyField = modelTable.getPrimaryKeyField();
185+
186+
List modelsIds =
187+
new List.from(modelsFound.map((m) => orm.getPrimaryKeyValue(m)));
188+
189+
for (Field field in modelTable.fields) {
190+
if (field is ListJoinField) {
191+
ListJoinField listField = field;
192+
193+
if (field.joinTable is ListJoinValuesTable) {
194+
// When list members are simple types and are stored right inside
195+
// join table
196+
ListJoinValuesTable listTable = field.joinTable;
169197

170198
Select referenceSelect = new Select(['*']);
171-
referenceSelect.table = listField.referenceTable;
172-
referenceSelect.where(new In(
173-
listTable.primaryKeyReferenceField.fieldName,
174-
resulRowsPromaryKeys));
199+
referenceSelect.table = listField.joinTable;
200+
referenceSelect.where(
201+
new In(listTable.primaryKeyReferenceField.fieldName, modelsIds));
202+
175203
var results = await orm.getDefaultAdapter().select(referenceSelect);
176204

177205
// now we have all values from reference table for all results from original select.
@@ -180,21 +208,84 @@ class FindBase extends Select {
180208
var modelId = row[listTable.primaryKeyReferenceField.fieldName];
181209
var value = row[listTable.valueField.fieldName];
182210

183-
for (var foundInstance in foundInstances) {
211+
for (var foundInstance in modelsFound) {
184212
var foundInstanceId = AnnotationsParser.getPropertyValueForField(
185-
selectSql.table.getPrimaryKeyField(), foundInstance);
213+
modelPrimaryKeyField, foundInstance);
214+
186215
if (foundInstanceId == modelId) {
187216
var list = AnnotationsParser.getPropertyValueForField(
188217
field, foundInstance);
189218
list.add(value);
190219
}
191220
}
192221
}
222+
} else if (field.joinTable is ListJoinModelsTable) {
223+
ListJoinModelsTable listTable = field.joinTable;
224+
225+
// first - select list members ids from join table
226+
Select listMembersIdsSelect = new Select(['*']);
227+
listMembersIdsSelect.table = listField.joinTable;
228+
listMembersIdsSelect.where(new In(
229+
listTable.listHolderPrimaryKeyReference.fieldName, modelsIds));
230+
231+
// raw list of list members ids. Each item in this list will have a
232+
// map with list holder id and list member id.
233+
List rawListMembersIds =
234+
await orm.getDefaultAdapter().select(listMembersIdsSelect);
235+
236+
// Just gather all list members ids in a simple list
237+
List allListMemberIds = [];
238+
239+
// Make a map which keys are model ids and values are lists with found
240+
// list members
241+
Map<dynamic, List> listMembersIdByModelId = {};
242+
243+
rawListMembersIds.forEach((m) {
244+
allListMemberIds
245+
.add(m[listTable.listMembersPrimaryKeyReference.fieldName]);
246+
247+
var modelId = m[listTable.listHolderPrimaryKeyReference.fieldName];
248+
249+
if (!listMembersIdByModelId.containsKey(modelId)) {
250+
listMembersIdByModelId[modelId] = [];
251+
}
252+
253+
listMembersIdByModelId[modelId]
254+
.add(m[listTable.listMembersPrimaryKeyReference.fieldName]);
255+
});
256+
257+
if (allListMemberIds.length > 0) {
258+
// find all list members for all found models
259+
Find findMembers = new Find(listTable.listMembersTable.modelType);
260+
findMembers.where(new In(
261+
listTable.listMembersTable
262+
.getPrimaryKeyField()
263+
.fieldName,
264+
allListMemberIds));
265+
266+
List allListMembers = await findMembers.execute();
267+
268+
// now lets move found list members to models lists
269+
270+
for (var foundInstance in modelsFound) {
271+
var foundInstanceId = AnnotationsParser.getPropertyValueForField(
272+
modelPrimaryKeyField, foundInstance);
273+
274+
List listMemberIdsForFoundModel = listMembersIdByModelId[foundInstanceId];
275+
List listMembers = new List.from(allListMembers.where((m) {
276+
return listMemberIdsForFoundModel.contains(
277+
orm.getPrimaryKeyValue(m));
278+
}));
279+
280+
AnnotationsParser.setPropertyValueForField(
281+
field, listMembers, foundInstance);
282+
}
283+
}
193284
}
194285
}
195286
}
196287

197-
return foundInstances;
288+
return modelsFound;
198289
}
199290
}
200291

0 commit comments

Comments
 (0)