diff --git a/packages/orm/angel_orm_generator/lib/src/orm_generator.dart b/packages/orm/angel_orm_generator/lib/src/orm_generator.dart index bfb4a749..2bfed44e 100644 --- a/packages/orm/angel_orm_generator/lib/src/orm_generator.dart +++ b/packages/orm/angel_orm_generator/lib/src/orm_generator.dart @@ -17,6 +17,7 @@ var floatTypes = [ const ColumnType('double precision'), ]; +/// ORM Builder Builder ormBuilder(BuilderOptions options) { return SharedPartBuilder([ OrmGenerator( @@ -73,6 +74,9 @@ class OrmGenerator extends GeneratorForAnnotation { var rc = ctx.buildContext.modelClassNameRecase; var queryWhereType = refer('${rc.pascalCase}QueryWhere'); + + log.info('Generating ${rc.pascalCase}QueryWhere'); + var nullableQueryWhereType = TypeReference((b) => b ..symbol = '${rc.pascalCase}QueryWhere' ..isNullable = true); @@ -144,8 +148,7 @@ class OrmGenerator extends GeneratorForAnnotation { ..name = 'fields' ..returns = TypeReference((b) => b ..symbol = 'List' - ..types.add(TypeReference( - (b) => b..symbol = 'String'))) //refer('List') + ..types.add(TypeReference((b) => b..symbol = 'String'))) ..annotations.add(refer('override')) ..type = MethodType.getter ..body = Block((b) { @@ -202,7 +205,9 @@ class OrmGenerator extends GeneratorForAnnotation { for (var field in ctx.effectiveFields) { var fType = field.type; Reference type = convertTypeReference(field.type); - if (isSpecialId(ctx, field)) type = refer('int'); + if (isSpecialId(ctx, field)) { + type = refer('int'); + } var expr = (refer('row').index(literalNum(i++))); if (isSpecialId(ctx, field)) { @@ -230,6 +235,7 @@ class OrmGenerator extends GeneratorForAnnotation { b.statements .add(Code('if (row.every((x) => x == null)) { return null; }')); + b.addExpression(ctx.buildContext.modelClassType .newInstance([], args).assignVar('model')); @@ -239,13 +245,14 @@ class OrmGenerator extends GeneratorForAnnotation { RelationshipType.belongsTo, RelationshipType.hasMany ].contains(relation.type)) { + //log.warning('Unsupported relationship for field $name'); return; } //log.fine('Process relationship'); var foreign = relation.foreign; if (foreign == null) { - log.warning('Foreign is null'); + log.warning('Foreign relationship for field $name is null'); return; } //log.fine('Detected relationship ${RelationshipType.belongsTo}'); @@ -257,16 +264,23 @@ class OrmGenerator extends GeneratorForAnnotation { .call([literalNum(foreign.effectiveFields.length)]) .property('toList') .call([]); + var parsed = refer( '${foreign.buildContext.modelClassNameRecase.pascalCase}Query') .property('parseRow') .call([skipToList]); + + var baseClass = '${foreign.buildContext.originalClassName}'; + // Assume: baseclass starts with "_" + //'_${foreign.buildContext.modelClassNameRecase.pascalCase}'; + if (relation.type == RelationshipType.hasMany) { - parsed = literalList([parsed]); + parsed = literalList([parsed.asA(refer(baseClass))]); var pp = parsed.accept(DartEmitter(useNullSafetySyntax: true)); - parsed = CodeExpression( - Code('$pp.where((x) => x != null).toList()')); + parsed = CodeExpression(Code('$pp')); + //Code('$pp.where((x) => x != null).toList()')); } + var expr = refer('model').property('copyWith').call([], {name: parsed}); var block = @@ -337,7 +351,6 @@ class OrmGenerator extends GeneratorForAnnotation { ); // Note: this is where subquery fields for relations are added. - ctx.relations.forEach((fieldName, relation) { //var name = ctx.buildContext.resolveFieldName(fieldName); if (relation.type == RelationshipType.belongsTo || @@ -354,9 +367,9 @@ class OrmGenerator extends GeneratorForAnnotation { } var relationContext = relation.throughContext ?? relation.foreign; - log.fine( - '$fieldName relation.throughContext => ${relation.throughContext?.tableName} relation.foreign => ${relation.foreign?.tableName}'); + //log.fine( + // '$fieldName relation.throughContext => ${relation.throughContext?.tableName} relation.foreign => ${relation.foreign?.tableName}'); // If this is a many-to-many, add the fields from the other object. var additionalStrs = relationForeign.effectiveFields.map((f) => relationForeign.buildContext.resolveFieldName(f.name)); @@ -400,12 +413,12 @@ class OrmGenerator extends GeneratorForAnnotation { b.write(' LEFT JOIN ${relationContext?.tableName}'); // Figure out which field on the "through" table points to users (foreign) - log.fine('$fieldName query => ${b.toString()}'); + //log.fine('$fieldName query => ${b.toString()}'); var throughRelation = relationContext?.relations.values.firstWhere((e) { - log.fine( - 'ForeignTable(Rel) => ${e.foreignTable}, ${relationForeign.tableName}'); + //log.fine( + // 'ForeignTable(Rel) => ${e.foreignTable}, ${relationForeign.tableName}'); return e.foreignTable == relationForeign.tableName; }, orElse: () { // _Role has a many-to-many to _User through _RoleUser, but @@ -518,6 +531,13 @@ class OrmGenerator extends GeneratorForAnnotation { .accept(DartEmitter(useNullSafetySyntax: true)); b ..name = methodName + ..returns = TypeReference((b) => b + ..symbol = 'Future' + ..types.add(TypeReference((b) => b + ..symbol = 'List' + ..types.add(TypeReference((b) => b + ..symbol = '$type' + ..isNullable = false))))) ..annotations.add(refer('override')) ..requiredParameters.add(Parameter((b) => b ..name = 'executor' @@ -531,13 +551,14 @@ class OrmGenerator extends GeneratorForAnnotation { // This is only allowed with lists. var field = ctx.buildContext.fields.firstWhere((f) => f.name == name); + var typeLiteral = convertTypeReference(field.type) .accept(DartEmitter(useNullSafetySyntax: true)) .toString() .replaceAll('?', ''); merge.add(''' - $name: $typeLiteral.from(l.$name ?? [])..addAll(model.$name ?? []) + $name: $typeLiteral.from(l.$name)..addAll(model.$name) '''); } }); @@ -546,6 +567,7 @@ class OrmGenerator extends GeneratorForAnnotation { var keyName = findPrimaryFieldInList(ctx, ctx.buildContext.fields)?.name; + if (keyName == null) { throw '${ctx.buildContext.originalClassName} has no defined primary key.\n' '@HasMany and @ManyToMany relations require a primary key to be defined on the model.'; @@ -575,6 +597,9 @@ class OrmGenerator extends GeneratorForAnnotation { Class buildWhereClass(OrmBuildContext ctx) { return Class((clazz) { var rc = ctx.buildContext.modelClassNameRecase; + + log.info('Generating ${rc.pascalCase}QueryWhere'); + clazz ..name = '${rc.pascalCase}QueryWhere' ..extend = refer('QueryWhere'); @@ -625,7 +650,10 @@ class OrmGenerator extends GeneratorForAnnotation { builderType = TypeReference((b) => b ..symbol = 'EnumSqlExpressionBuilder' ..types.add(convertTypeReference(type))); - args.add(CodeExpression(Code('(v) => v.index'))); + + var question = + type.nullabilitySuffix == NullabilitySuffix.question ? '?' : ''; + args.add(CodeExpression(Code('(v) => v$question.index as int'))); } else if (const TypeChecker.fromRuntime(String).isExactlyType(type)) { builderType = refer('StringSqlExpressionBuilder'); } else if (const TypeChecker.fromRuntime(bool).isExactlyType(type)) { @@ -642,7 +670,7 @@ class OrmGenerator extends GeneratorForAnnotation { // Detect relationship } else if (name.endsWith('Id')) { - log.fine('Relationship detected = $name'); + //log.fine('Relationship detected = $name'); var relation = ctx.relations[name.replaceAll('Id', '')]; if (relation != null) { //if (relation?.type != RelationshipType.belongsTo) { @@ -710,6 +738,8 @@ class OrmGenerator extends GeneratorForAnnotation { return Class((clazz) { var rc = ctx.buildContext.modelClassNameRecase; + log.info('Generating ${rc.pascalCase}QueryValues'); + clazz ..name = '${rc.pascalCase}QueryValues' ..extend = refer('MapQueryValues'); @@ -829,7 +859,7 @@ class OrmGenerator extends GeneratorForAnnotation { // Added null safety check var parsedId = prop.property(foreignField.name); - log.fine('Foreign field => ${foreignField.name}'); + //log.fine('Foreign field => ${foreignField.name}'); if (foreignField.type.nullabilitySuffix == NullabilitySuffix.question) { parsedId = prop.nullSafeProperty(foreignField.name);