From 832601c8c5df898ddfc69a299c26a0a693aee0a3 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 3 Apr 2019 05:57:27 -0400 Subject: [PATCH] Testing for not-model/no default id --- angel_orm/CHANGELOG.md | 1 + angel_orm/lib/src/builder.dart | 16 +- angel_orm/lib/src/relations.dart | 6 +- angel_orm_generator/CHANGELOG.md | 6 + angel_orm_generator/build.yaml | 1 + .../lib/src/orm_build_context.dart | 111 +- .../lib/src/orm_generator.dart | 53 +- angel_orm_generator/lib/src/readers.dart | 20 + angel_orm_generator/test/belongs_to_test.dart | 2 +- angel_orm_generator/test/edge_case_test.dart | 89 ++ angel_orm_generator/test/migrations/numba.sql | 7 + angel_orm_generator/test/migrations/song.sql | 8 + .../test/migrations/unorthodox.sql | 4 + .../test/migrations/weird_join.sql | 13 + angel_orm_generator/test/models/author.g.dart | 10 +- angel_orm_generator/test/models/book.g.dart | 10 +- angel_orm_generator/test/models/car.g.dart | 6 +- .../test/models/customer.g.dart | 6 +- angel_orm_generator/test/models/foot.g.dart | 6 +- angel_orm_generator/test/models/fruit.g.dart | 6 +- .../test/models/has_car.g.dart | 6 +- angel_orm_generator/test/models/leg.g.dart | 6 +- angel_orm_generator/test/models/order.g.dart | 8 +- angel_orm_generator/test/models/tree.g.dart | 8 +- .../test/models/unorthodox.dart | 70 + .../test/models/unorthodox.g.dart | 1263 +++++++++++++++++ angel_orm_generator/test/models/user.g.dart | 16 +- angel_orm_generator/test/standalone_test.dart | 4 +- 28 files changed, 1649 insertions(+), 113 deletions(-) create mode 100644 angel_orm_generator/test/edge_case_test.dart create mode 100644 angel_orm_generator/test/migrations/numba.sql create mode 100644 angel_orm_generator/test/migrations/song.sql create mode 100644 angel_orm_generator/test/migrations/unorthodox.sql create mode 100644 angel_orm_generator/test/migrations/weird_join.sql create mode 100644 angel_orm_generator/test/models/unorthodox.dart create mode 100644 angel_orm_generator/test/models/unorthodox.g.dart diff --git a/angel_orm/CHANGELOG.md b/angel_orm/CHANGELOG.md index 855d219b..d08b3d35 100644 --- a/angel_orm/CHANGELOG.md +++ b/angel_orm/CHANGELOG.md @@ -1,5 +1,6 @@ # 2.0.0-dev.24 * Fix a bug that caused syntax errors on `ORDER BY`. +* Add `pattern` to `like` on string builder. `sanitize` is optional. # 2.0.0-dev.23 * Add `@ManyToMany` annotation, which builds many-to-many relations. diff --git a/angel_orm/lib/src/builder.dart b/angel_orm/lib/src/builder.dart index c3cb8908..abeaed28 100644 --- a/angel_orm/lib/src/builder.dart +++ b/angel_orm/lib/src/builder.dart @@ -239,17 +239,23 @@ class StringSqlExpressionBuilder extends SqlExpressionBuilder { /// Builds a `LIKE` predicate. /// - /// To prevent injections, the [pattern] is called with a name that - /// will be escaped by the underlying [QueryExecutor]. + /// To prevent injections, an optional [sanitizer] is called with a name that + /// will be escaped by the underlying [QueryExecutor]. Use this if the [pattern] + /// is not constant, and/or involves user input. + /// + /// Otherwise, you can omit [sanitizer]. /// /// Example: /// ```dart + /// carNameBuilder.like('%Mazda%'); /// carNameBuilder.like((name) => 'Mazda %$name%'); /// ``` - void like(String Function(String) pattern) { - _raw = 'LIKE \'' + pattern('@$substitution') + '\''; - query.substitutionValues[substitution] = _value; + void like(String pattern, {String Function(String) sanitize}) { + sanitize ??= (s) => pattern; + _raw = 'LIKE \'' + sanitize('@$substitution') + '\''; + query.substitutionValues[substitution] = pattern; _hasValue = true; + _value = null; } void isBetween(String lower, String upper) { diff --git a/angel_orm/lib/src/relations.dart b/angel_orm/lib/src/relations.dart index b27228a4..7c745b82 100644 --- a/angel_orm/lib/src/relations.dart +++ b/angel_orm/lib/src/relations.dart @@ -21,7 +21,7 @@ class Relationship { class HasMany extends Relationship { const HasMany( - {String localKey = 'id', + {String localKey, String foreignKey, String foreignTable, bool cascadeOnDelete = false}) @@ -36,7 +36,7 @@ const HasMany hasMany = const HasMany(); class HasOne extends Relationship { const HasOne( - {String localKey = 'id', + {String localKey, String foreignKey, String foreignTable, bool cascadeOnDelete = false}) @@ -63,7 +63,7 @@ class ManyToMany extends Relationship { final Type through; const ManyToMany(this.through, - {String localKey = 'id', + {String localKey, String foreignKey, String foreignTable, bool cascadeOnDelete = false}) diff --git a/angel_orm_generator/CHANGELOG.md b/angel_orm_generator/CHANGELOG.md index 87f20ad8..ac631ae4 100644 --- a/angel_orm_generator/CHANGELOG.md +++ b/angel_orm_generator/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2.0.0-dev.7 +* Handle `@ManyToMany`. +* Handle cases where the class is not a `Model`. + * Stop assuming things have `id`, etc. +* Resolve a bug where the `indexType` of `@Column` annotations. would not be found. + # 2.0.0-dev.6 * Fix bug where an extra field would be inserted into joins and botch the result. * Narrow analyzer dependency. diff --git a/angel_orm_generator/build.yaml b/angel_orm_generator/build.yaml index d213f307..ef954f9b 100644 --- a/angel_orm_generator/build.yaml +++ b/angel_orm_generator/build.yaml @@ -28,6 +28,7 @@ targets: - test/models/fruit.dart - test/models/has_map.dart - test/models/role.dart + - test/models/unorthodox.dart $default: dependencies: - angel_serialize_generator diff --git a/angel_orm_generator/lib/src/orm_build_context.dart b/angel_orm_generator/lib/src/orm_build_context.dart index 0aaf62cb..f21e6a1a 100644 --- a/angel_orm_generator/lib/src/orm_build_context.dart +++ b/angel_orm_generator/lib/src/orm_build_context.dart @@ -19,6 +19,39 @@ import 'readers.dart'; bool isHasRelation(Relationship r) => r.type == RelationshipType.hasOne || r.type == RelationshipType.hasMany; +bool isSpecialId(OrmBuildContext ctx, FieldElement field) { + return field is ShimFieldImpl && + field is! RelationFieldImpl && + (field.name == 'id' && + const TypeChecker.fromRuntime(Model) + .isAssignableFromType(ctx.buildContext.clazz.type)); +} + +FieldElement findPrimaryFieldInList( + OrmBuildContext ctx, Iterable fields) { + for (var field_ in fields) { + var field = field_ is RelationFieldImpl ? field_.originalField : field_; + var element = field.getter ?? field; + // print( + // 'Searching in ${ctx.buildContext.originalClassName}=>${field?.name} (${field.runtimeType})'); + // Check for column annotation... + var columnAnnotation = columnTypeChecker.firstAnnotationOf(element); + + if (columnAnnotation != null) { + var column = reviveColumn(new ConstantReader(columnAnnotation)); + // print( + // ' * Found column on ${field.name} with indexType = ${column.indexType}'); + if (column.indexType == IndexType.primaryKey) return field; + } + } + + var specialId = + fields.firstWhere((f) => isSpecialId(ctx, f), orElse: () => null); + // print( + // 'Special ID on ${ctx.buildContext.originalClassName} => ${specialId?.name}'); + return specialId; +} + final Map _cache = {}; Future buildOrmContext( @@ -59,19 +92,18 @@ Future buildOrmContext( for (var field in buildCtx.fields) { // Check for column annotation... Column column; - var columnAnnotation = columnTypeChecker.firstAnnotationOf(field); + var element = field.getter ?? field; + var columnAnnotation = columnTypeChecker.firstAnnotationOf(element); if (columnAnnotation != null) { column = reviveColumn(new ConstantReader(columnAnnotation)); } - if (column == null && - field.name == 'id' && - const TypeChecker.fromRuntime(Model) - .isAssignableFromType(buildCtx.clazz.type)) { + if (column == null && isSpecialId(ctx, field)) { // This is only for PostgreSQL, so implementations without a `serial` type // must handle it accordingly, of course. - column = const Column(type: ColumnType.serial); + column = const Column( + type: ColumnType.serial, indexType: IndexType.primaryKey); } if (column == null) { @@ -168,12 +200,30 @@ Future buildOrmContext( // Fill in missing keys var rcc = new ReCase(field.name); + + String keyName(OrmBuildContext ctx, String missing) { + var _keyName = + findPrimaryFieldInList(ctx, ctx.buildContext.fields)?.name; + // print( + // 'Keyname for ${buildCtx.originalClassName}.${field.name} maybe = $_keyName??'); + if (_keyName == null) { + throw '${ctx.buildContext.originalClassName} has no defined primary key, ' + 'so the relation on field ${buildCtx.originalClassName}.${field.name} must define a $missing.'; + } else { + return _keyName; + } + } + if (type == RelationshipType.hasOne || type == RelationshipType.hasMany) { - localKey ??= 'id'; - foreignKey ??= '${rc.snakeCase}_id'; + localKey ??= + ctx.buildContext.resolveFieldName(keyName(ctx, 'local key')); + // print( + // 'Local key on ${buildCtx.originalClassName}.${field.name} defaulted to $localKey'); + foreignKey ??= '${rc.snakeCase}_$localKey'; } else if (type == RelationshipType.belongsTo) { - localKey ??= '${rcc.snakeCase}_id'; - foreignKey ??= 'id'; + foreignKey ??= + ctx.buildContext.resolveFieldName(keyName(foreign, 'foreign key')); + localKey ??= '${rcc.snakeCase}_$foreignKey'; } var relation = new RelationshipReader( @@ -187,19 +237,21 @@ Future buildOrmContext( throughContext: throughContext, ); + // print('Relation on ${buildCtx.originalClassName}.${field.name} => ' + // 'foreignKey=$foreignKey, localKey=$localKey'); + if (relation.type == RelationshipType.belongsTo) { var name = new ReCase(relation.localKey).camelCase; ctx.buildContext.aliases[name] = relation.localKey; if (!ctx.effectiveFields.any((f) => f.name == field.name)) { - // TODO: Consequences of allowing ID to be a relation? (should be none) - // if (field.name != 'id' || - // !const TypeChecker.fromRuntime(Model) - // .isAssignableFromType(ctx.buildContext.clazz.type)) { - var rf = new RelationFieldImpl(name, - field.type.element.context.typeProvider.intType, field.name); + var foreignField = relation.findForeignField(ctx); + var foreign = relation.throughContext ?? relation.foreign; + var type = foreignField.type; + if (isSpecialId(foreign, foreignField)) + type = field.type.element.context.typeProvider.intType; + var rf = new RelationFieldImpl(name, relation, type, field); ctx.effectiveFields.add(rf); - // } } } @@ -239,17 +291,16 @@ ColumnType inferColumnType(DartType type) { } Column reviveColumn(ConstantReader cr) { - var args = cr.revive().namedArguments; - IndexType indexType = IndexType.none; ColumnType columnType; - if (args.containsKey('index')) { - indexType = - IndexType.values[args['indexType'].getField('index').toIntValue()]; - } + var columnObj = + cr.peek('type')?.objectValue?.getField('name')?.toStringValue(); + var indexType = IndexType.values[ + cr.peek('indexType')?.objectValue?.getField('index')?.toIntValue() ?? + IndexType.none.index]; - if (args.containsKey('type')) { - columnType = new _ColumnType(args['type'].getField('name').toStringValue()); + if (columnObj != null) { + columnType = new _ColumnType(columnObj); } return new Column( @@ -283,9 +334,15 @@ class _ColumnType implements ColumnType { } class RelationFieldImpl extends ShimFieldImpl { - final String originalFieldName; - RelationFieldImpl(String name, DartType type, this.originalFieldName) + final FieldElement originalField; + final RelationshipReader relationship; + RelationFieldImpl( + String name, this.relationship, DartType type, this.originalField) : super(name, type); + + String get originalFieldName => originalField.name; + + PropertyAccessorElement get getter => originalField.getter; } InterfaceType firstModelAncestor(DartType type) { diff --git a/angel_orm_generator/lib/src/orm_generator.dart b/angel_orm_generator/lib/src/orm_generator.dart index e294d9f5..cc27765b 100644 --- a/angel_orm_generator/lib/src/orm_generator.dart +++ b/angel_orm_generator/lib/src/orm_generator.dart @@ -8,7 +8,6 @@ import 'package:angel_serialize_generator/build_context.dart'; import 'package:build/build.dart'; import 'package:code_builder/code_builder.dart' hide LibraryBuilder; import 'package:source_gen/source_gen.dart'; - import 'orm_build_context.dart'; var floatTypes = [ @@ -434,21 +433,8 @@ class OrmGenerator extends GeneratorForAnnotation { var queryInstance = queryType.newInstance([]); // Next, we need to apply a cascade that sets the correct query value. - var localField = ctx.effectiveFields.firstWhere( - (f) => - ctx.buildContext.resolveFieldName(f.name) == - relation.localKey, orElse: () { - throw '${ctx.buildContext.clazz.name} has no field that maps to the name "${relation.localKey}", ' - 'but it has a @HasMany() relation that expects such a field.'; - }); - - var foreignField = foreign.effectiveFields.firstWhere( - (f) => - foreign.buildContext.resolveFieldName(f.name) == - relation.foreignKey, orElse: () { - throw '${foreign.buildContext.clazz.name} has no field that maps to the name "${relation.foreignKey}", ' - 'but ${ctx.buildContext.clazz.name} has a @HasMany() relation that expects such a field.'; - }); + var localField = relation.findLocalField(ctx); + var foreignField = relation.findForeignField(ctx); var queryValue = (isSpecialId(ctx, localField)) ? 'int.parse(model.id)' @@ -505,10 +491,17 @@ class OrmGenerator extends GeneratorForAnnotation { var merged = merge.join(', '); + 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.'; + } + b.body = new Code(''' return super.$methodName(executor).then((result) { return result.fold>([], (out, model) { - var idx = out.indexWhere((m) => m.id == model.id); + var idx = out.indexWhere((m) => m.$keyName == model.$keyName); if (idx == -1) { return out..add(model); @@ -525,14 +518,6 @@ class OrmGenerator extends GeneratorForAnnotation { }); } - bool isSpecialId(OrmBuildContext ctx, FieldElement field) { - return field is ShimFieldImpl && - field is! RelationFieldImpl && - (field.name == 'id' && - const TypeChecker.fromRuntime(Model) - .isAssignableFromType(ctx.buildContext.clazz.type)); - } - Class buildWhereClass(OrmBuildContext ctx) { return new Class((clazz) { var rc = ctx.buildContext.modelClassNameRecase; @@ -667,13 +652,11 @@ class OrmGenerator extends GeneratorForAnnotation { }); })); - // Each field generates a getter for setter + // Each field generates a getter and setter for (var field in ctx.effectiveFields) { var fType = field.type; var name = ctx.buildContext.resolveFieldName(field.name); - var type = isSpecialId(ctx, field) - ? refer('int') - : convertTypeReference(field.type); + var type = convertTypeReference(field.type); clazz.methods.add(new Method((b) { var value = refer('values').index(literalString(name)); @@ -748,9 +731,15 @@ class OrmGenerator extends GeneratorForAnnotation { // Add only if present var target = refer('values').index(literalString( ctx.buildContext.resolveFieldName(field.name))); - var parsedId = (refer('int') - .property('parse') - .call([prop.property('id')])); + var foreign = field.relationship.throughContext ?? + field.relationship.foreign; + var foreignField = field.relationship.findForeignField(ctx); + var parsedId = prop.property(foreignField.name); + + if (isSpecialId(foreign, field)) { + parsedId = (refer('int').property('parse').call([parsedId])); + } + var cond = prop.notEqualTo(literalNull); var condStr = cond.accept(new DartEmitter()); var blkStr = diff --git a/angel_orm_generator/lib/src/readers.dart b/angel_orm_generator/lib/src/readers.dart index b27c1d74..275b829d 100644 --- a/angel_orm_generator/lib/src/readers.dart +++ b/angel_orm_generator/lib/src/readers.dart @@ -1,4 +1,5 @@ import 'package:analyzer/dart/constant/value.dart'; +import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:angel_orm/angel_orm.dart'; import 'package:source_gen/source_gen.dart'; @@ -43,4 +44,23 @@ class RelationshipReader { bool get isManyToMany => type == RelationshipType.hasMany && throughContext != null; + + FieldElement findLocalField(OrmBuildContext ctx) { + return ctx.effectiveFields.firstWhere( + (f) => ctx.buildContext.resolveFieldName(f.name) == localKey, + orElse: () { + throw '${ctx.buildContext.clazz.name} has no field that maps to the name "$localKey", ' + 'but it has a @HasMany() relation that expects such a field.'; + }); + } + + FieldElement findForeignField(OrmBuildContext ctx) { + var foreign = throughContext ?? this.foreign; + return foreign.effectiveFields.firstWhere( + (f) => foreign.buildContext.resolveFieldName(f.name) == foreignKey, + orElse: () { + throw '${foreign.buildContext.clazz.name} has no field that maps to the name "$foreignKey", ' + 'but ${ctx.buildContext.clazz.name} has a @HasMany() relation that expects such a field.'; + }); + } } diff --git a/angel_orm_generator/test/belongs_to_test.dart b/angel_orm_generator/test/belongs_to_test.dart index c792e8c4..bb69b0e3 100644 --- a/angel_orm_generator/test/belongs_to_test.dart +++ b/angel_orm_generator/test/belongs_to_test.dart @@ -88,7 +88,7 @@ main() { }); test('union', () async { - var query1 = new BookQuery()..where.name.like((_) => 'Deathly%'); + var query1 = new BookQuery()..where.name.like('Deathly%'); var query2 = new BookQuery()..where.authorId.equals(-1); var query3 = new BookQuery() ..where.name.isIn(['Goblet of Fire', 'Order of the Phoenix']); diff --git a/angel_orm_generator/test/edge_case_test.dart b/angel_orm_generator/test/edge_case_test.dart new file mode 100644 index 00000000..092297cc --- /dev/null +++ b/angel_orm_generator/test/edge_case_test.dart @@ -0,0 +1,89 @@ +import 'package:test/test.dart'; +import 'models/unorthodox.dart'; +import 'common.dart'; + +main() { + PostgresExecutor executor; + + setUp(() async { + executor = + await connectToPostgres(['unorthodox', 'weird_join', 'song', 'numba']); + }); + + test('can create object with no id', () async { + var query = UnorthodoxQuery()..values.name = 'Hey'; + var model = await query.insert(executor); + expect(model, Unorthodox(name: 'Hey')); + }); + + group('relations on non-model', () { + Unorthodox unorthodox; + + setUp(() async { + var query = UnorthodoxQuery()..values.name = 'Hey'; + unorthodox = await query.insert(executor); + }); + + test('belongs to', () async { + var query = WeirdJoinQuery()..values.joinName = unorthodox.name; + var model = await query.insert(executor); + print(model.toJson()); + expect(model.id, isNotNull); // Postgres should set this. + expect(model.unorthodox, unorthodox); + }); + + group('layered', () { + WeirdJoin weirdJoin; + Song girlBlue; + + setUp(() async { + var wjQuery = WeirdJoinQuery()..values.joinName = unorthodox.name; + weirdJoin = await wjQuery.insert(executor); + + var gbQuery = SongQuery() + ..values.weirdJoinId = weirdJoin.id + ..values.title = 'Girl Blue'; + girlBlue = await gbQuery.insert(executor); + }); + + test('has one', () async { + var query = WeirdJoinQuery()..where.id.equals(weirdJoin.id); + var wj = await query.getOne(executor); + print(wj.toJson()); + expect(wj.song, girlBlue); + }); + + test('has many', () async { + var numbas = []; + + for (int i = 0; i < 15; i++) { + var query = NumbaQuery() + ..values.parent = weirdJoin.id + ..values.i = i; + var model = await query.insert(executor); + numbas.add(model); + } + + var query = WeirdJoinQuery()..where.id.equals(weirdJoin.id); + var wj = await query.getOne(executor); + print(wj.toJson()); + expect(wj.numbas, numbas); + }); + + test('many to many', () async { + var fooQuery = FooQuery()..values.bar = 'baz'; + var fooBar = await fooQuery.insert(executor).then((foo) => foo.bar); + var pivotQuery = FooPivotQuery() + ..values.weirdJoinId = weirdJoin.id + ..values.fooBar = fooBar; + await pivotQuery.insert(executor); + fooQuery = FooQuery()..where.bar.equals('baz'); + + var foo = await fooQuery.getOne(executor); + print(foo.toJson()); + print(weirdJoin.toJson()); + expect(foo.weirdJoins[0].id, weirdJoin.id); + }); + }); + }); +} diff --git a/angel_orm_generator/test/migrations/numba.sql b/angel_orm_generator/test/migrations/numba.sql new file mode 100644 index 00000000..5a69fbc2 --- /dev/null +++ b/angel_orm_generator/test/migrations/numba.sql @@ -0,0 +1,7 @@ +CREATE TEMPORARY TABLE "numbas" ( + "i" int, + "parent" int references weird_joins(id), + created_at TIMESTAMP, + updated_at TIMESTAMP, + PRIMARY KEY(i) +); \ No newline at end of file diff --git a/angel_orm_generator/test/migrations/song.sql b/angel_orm_generator/test/migrations/song.sql new file mode 100644 index 00000000..9683209a --- /dev/null +++ b/angel_orm_generator/test/migrations/song.sql @@ -0,0 +1,8 @@ +CREATE TEMPORARY TABLE "songs" ( + "id" serial, + "weird_join_id" int references weird_joins(id), + "title" varchar(255), + created_at TIMESTAMP, + updated_at TIMESTAMP, + PRIMARY KEY(id) +); \ No newline at end of file diff --git a/angel_orm_generator/test/migrations/unorthodox.sql b/angel_orm_generator/test/migrations/unorthodox.sql new file mode 100644 index 00000000..a448fdd3 --- /dev/null +++ b/angel_orm_generator/test/migrations/unorthodox.sql @@ -0,0 +1,4 @@ +CREATE TEMPORARY TABLE "unorthodoxes" ( + "name" varchar(255), + PRIMARY KEY(name) +); \ No newline at end of file diff --git a/angel_orm_generator/test/migrations/weird_join.sql b/angel_orm_generator/test/migrations/weird_join.sql new file mode 100644 index 00000000..fd9de7ea --- /dev/null +++ b/angel_orm_generator/test/migrations/weird_join.sql @@ -0,0 +1,13 @@ +CREATE TEMPORARY TABLE "weird_joins" ( + "id" serial, + "join_name" varchar(255) references unorthodoxes(name), + PRIMARY KEY(id) +); +CREATE TEMPORARY TABLE "foos" ( + "bar" varchar(255), + PRIMARY KEY(bar) +); +CREATE TEMPORARY TABLE "foo_pivots" ( + "weird_join_id" int references weird_joins(id), + "foo_bar" varchar(255) references foos(bar) +); \ No newline at end of file diff --git a/angel_orm_generator/test/models/author.g.dart b/angel_orm_generator/test/models/author.g.dart index 1442aa04..f64258e7 100644 --- a/angel_orm_generator/test/models/author.g.dart +++ b/angel_orm_generator/test/models/author.g.dart @@ -11,7 +11,9 @@ class AuthorMigration extends Migration { up(Schema schema) { schema.create('authors', (table) { table.serial('id')..primaryKey(); - table.varChar('name')..defaultsTo('Tobe Osakwe'); + table.varChar('name', length: 255) + ..defaultsTo('Tobe Osakwe') + ..unique(); table.timeStamp('created_at'); table.timeStamp('updated_at'); }); @@ -107,11 +109,11 @@ class AuthorQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; String get name { return (values['name'] as String); } diff --git a/angel_orm_generator/test/models/book.g.dart b/angel_orm_generator/test/models/book.g.dart index aee9e4a0..cbf349ed 100644 --- a/angel_orm_generator/test/models/book.g.dart +++ b/angel_orm_generator/test/models/book.g.dart @@ -137,11 +137,11 @@ class BookQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; int get authorId { return (values['author_id'] as int); } @@ -172,10 +172,10 @@ class BookQueryValues extends MapQueryValues { createdAt = model.createdAt; updatedAt = model.updatedAt; if (model.author != null) { - values['author_id'] = int.parse(model.author.id); + values['author_id'] = model.author.id; } if (model.partnerAuthor != null) { - values['partner_author_id'] = int.parse(model.partnerAuthor.id); + values['partner_author_id'] = model.partnerAuthor.id; } } } diff --git a/angel_orm_generator/test/models/car.g.dart b/angel_orm_generator/test/models/car.g.dart index bda68199..945402ad 100644 --- a/angel_orm_generator/test/models/car.g.dart +++ b/angel_orm_generator/test/models/car.g.dart @@ -138,11 +138,11 @@ class CarQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; String get make { return (values['make'] as String); } diff --git a/angel_orm_generator/test/models/customer.g.dart b/angel_orm_generator/test/models/customer.g.dart index 80962d9b..f246c189 100644 --- a/angel_orm_generator/test/models/customer.g.dart +++ b/angel_orm_generator/test/models/customer.g.dart @@ -102,11 +102,11 @@ class CustomerQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; DateTime get createdAt { return (values['created_at'] as DateTime); } diff --git a/angel_orm_generator/test/models/foot.g.dart b/angel_orm_generator/test/models/foot.g.dart index 5f8d050e..c23b674b 100644 --- a/angel_orm_generator/test/models/foot.g.dart +++ b/angel_orm_generator/test/models/foot.g.dart @@ -112,11 +112,11 @@ class FootQueryValues extends MapQueryValues { return {'n_toes': 'decimal'}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; int get legId { return (values['leg_id'] as int); } diff --git a/angel_orm_generator/test/models/fruit.g.dart b/angel_orm_generator/test/models/fruit.g.dart index 11f16b4a..cb34d70a 100644 --- a/angel_orm_generator/test/models/fruit.g.dart +++ b/angel_orm_generator/test/models/fruit.g.dart @@ -112,11 +112,11 @@ class FruitQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; int get treeId { return (values['tree_id'] as int); } diff --git a/angel_orm_generator/test/models/has_car.g.dart b/angel_orm_generator/test/models/has_car.g.dart index 8b0c4d70..e6f3b1b6 100644 --- a/angel_orm_generator/test/models/has_car.g.dart +++ b/angel_orm_generator/test/models/has_car.g.dart @@ -107,11 +107,11 @@ class HasCarQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; CarType get type { return CarType.values[(values['type'] as int)]; } diff --git a/angel_orm_generator/test/models/leg.g.dart b/angel_orm_generator/test/models/leg.g.dart index 734183b1..9c303253 100644 --- a/angel_orm_generator/test/models/leg.g.dart +++ b/angel_orm_generator/test/models/leg.g.dart @@ -119,11 +119,11 @@ class LegQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; String get name { return (values['name'] as String); } diff --git a/angel_orm_generator/test/models/order.g.dart b/angel_orm_generator/test/models/order.g.dart index fe290ff7..170eaf5a 100644 --- a/angel_orm_generator/test/models/order.g.dart +++ b/angel_orm_generator/test/models/order.g.dart @@ -144,11 +144,11 @@ class OrderQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; int get customerId { return (values['customer_id'] as int); } @@ -186,7 +186,7 @@ class OrderQueryValues extends MapQueryValues { createdAt = model.createdAt; updatedAt = model.updatedAt; if (model.customer != null) { - values['customer_id'] = int.parse(model.customer.id); + values['customer_id'] = model.customer.id; } } } diff --git a/angel_orm_generator/test/models/tree.g.dart b/angel_orm_generator/test/models/tree.g.dart index c672786d..e338054b 100644 --- a/angel_orm_generator/test/models/tree.g.dart +++ b/angel_orm_generator/test/models/tree.g.dart @@ -11,7 +11,7 @@ class TreeMigration extends Migration { up(Schema schema) { schema.create('trees', (table) { table.serial('id')..primaryKey(); - table.declare('rings', ColumnType('smallint')); + table.integer('rings'); table.timeStamp('created_at'); table.timeStamp('updated_at'); }); @@ -179,11 +179,11 @@ class TreeQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; int get rings { return (values['rings'] as int); } diff --git a/angel_orm_generator/test/models/unorthodox.dart b/angel_orm_generator/test/models/unorthodox.dart new file mode 100644 index 00000000..4a56f30a --- /dev/null +++ b/angel_orm_generator/test/models/unorthodox.dart @@ -0,0 +1,70 @@ +import 'package:angel_migration/angel_migration.dart'; +import 'package:angel_model/angel_model.dart'; +import 'package:angel_orm/angel_orm.dart'; +import 'package:angel_serialize/angel_serialize.dart'; +part 'unorthodox.g.dart'; + +@serializable +@orm +abstract class _Unorthodox { + @Column(indexType: IndexType.primaryKey) + String get name; +} + +@serializable +@orm +abstract class _WeirdJoin { + @primaryKey + int get id; + + @BelongsTo(localKey: 'join_name', foreignKey: 'name') + _Unorthodox get unorthodox; + + @hasOne + _Song get song; + + @HasMany(foreignKey: 'parent') + List<_Numba> get numbas; + + @ManyToMany(_FooPivot) + List<_Foo> get foos; +} + +@serializable +@orm +abstract class _Song extends Model { + int get weirdJoinId; + + String get title; +} + +@serializable +@orm +class _Numba implements Comparable<_Numba> { + @primaryKey + int i; + + int parent; + + int compareTo(_Numba other) => i.compareTo(other.i); +} + +@serializable +@orm +abstract class _Foo { + @primaryKey + String get bar; + + @ManyToMany(_FooPivot) + List<_WeirdJoin> get weirdJoins; +} + +@serializable +@orm +abstract class _FooPivot { + @belongsTo + _WeirdJoin get weirdJoin; + + @belongsTo + _Foo get foo; +} diff --git a/angel_orm_generator/test/models/unorthodox.g.dart b/angel_orm_generator/test/models/unorthodox.g.dart new file mode 100644 index 00000000..0f2f80ca --- /dev/null +++ b/angel_orm_generator/test/models/unorthodox.g.dart @@ -0,0 +1,1263 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'unorthodox.dart'; + +// ************************************************************************** +// MigrationGenerator +// ************************************************************************** + +class UnorthodoxMigration extends Migration { + @override + up(Schema schema) { + schema.create('unorthodoxes', (table) { + table.varChar('name')..primaryKey(); + }); + } + + @override + down(Schema schema) { + schema.drop('unorthodoxes'); + } +} + +class WeirdJoinMigration extends Migration { + @override + up(Schema schema) { + schema.create('weird_joins', (table) { + table.integer('join_name').references('unorthodoxes', 'name'); + }); + } + + @override + down(Schema schema) { + schema.drop('weird_joins'); + } +} + +class SongMigration extends Migration { + @override + up(Schema schema) { + schema.create('songs', (table) { + table.serial('id')..primaryKey(); + table.integer('weird_join_id'); + table.varChar('title'); + table.timeStamp('created_at'); + table.timeStamp('updated_at'); + }); + } + + @override + down(Schema schema) { + schema.drop('songs'); + } +} + +class NumbaMigration extends Migration { + @override + up(Schema schema) { + schema.create('numbas', (table) { + table.integer('i'); + table.integer('parent'); + }); + } + + @override + down(Schema schema) { + schema.drop('numbas'); + } +} + +class FooMigration extends Migration { + @override + up(Schema schema) { + schema.create('foos', (table) {}); + } + + @override + down(Schema schema) { + schema.drop('foos'); + } +} + +class FooPivotMigration extends Migration { + @override + up(Schema schema) { + schema.create('foo_pivots', (table) { + table.integer('weird_join_id').references('weird_joins', 'id'); + table.integer('foo_bar').references('foos', 'bar'); + }); + } + + @override + down(Schema schema) { + schema.drop('foo_pivots'); + } +} + +// ************************************************************************** +// OrmGenerator +// ************************************************************************** + +class UnorthodoxQuery extends Query { + UnorthodoxQuery({Set trampoline}) { + trampoline ??= Set(); + trampoline.add(tableName); + _where = UnorthodoxQueryWhere(this); + } + + @override + final UnorthodoxQueryValues values = UnorthodoxQueryValues(); + + UnorthodoxQueryWhere _where; + + @override + get casts { + return {}; + } + + @override + get tableName { + return 'unorthodoxes'; + } + + @override + get fields { + return const ['name']; + } + + @override + UnorthodoxQueryWhere get where { + return _where; + } + + @override + UnorthodoxQueryWhere newWhereClause() { + return UnorthodoxQueryWhere(this); + } + + static Unorthodox parseRow(List row) { + if (row.every((x) => x == null)) return null; + var model = Unorthodox(name: (row[0] as String)); + return model; + } + + @override + deserialize(List row) { + return parseRow(row); + } +} + +class UnorthodoxQueryWhere extends QueryWhere { + UnorthodoxQueryWhere(UnorthodoxQuery query) + : name = StringSqlExpressionBuilder(query, 'name'); + + final StringSqlExpressionBuilder name; + + @override + get expressionBuilders { + return [name]; + } +} + +class UnorthodoxQueryValues extends MapQueryValues { + @override + get casts { + return {}; + } + + String get name { + return (values['name'] as String); + } + + set name(String value) => values['name'] = value; + void copyFrom(Unorthodox model) { + name = model.name; + } +} + +class WeirdJoinQuery extends Query { + WeirdJoinQuery({Set trampoline}) { + trampoline ??= Set(); + trampoline.add(tableName); + _where = WeirdJoinQueryWhere(this); + leftJoin('unorthodoxes', 'join_name', 'name', + additionalFields: const ['name'], trampoline: trampoline); + leftJoin('songs', 'id', 'weird_join_id', + additionalFields: const [ + 'id', + 'weird_join_id', + 'title', + 'created_at', + 'updated_at' + ], + trampoline: trampoline); + leftJoin(NumbaQuery(trampoline: trampoline), 'id', 'parent', + additionalFields: const ['i', 'parent'], trampoline: trampoline); + leftJoin(FooPivotQuery(trampoline: trampoline), 'id', 'weird_join_id', + additionalFields: const ['bar'], trampoline: trampoline); + } + + @override + final WeirdJoinQueryValues values = WeirdJoinQueryValues(); + + WeirdJoinQueryWhere _where; + + @override + get casts { + return {}; + } + + @override + get tableName { + return 'weird_joins'; + } + + @override + get fields { + return const ['id', 'join_name']; + } + + @override + WeirdJoinQueryWhere get where { + return _where; + } + + @override + WeirdJoinQueryWhere newWhereClause() { + return WeirdJoinQueryWhere(this); + } + + static WeirdJoin parseRow(List row) { + if (row.every((x) => x == null)) return null; + var model = WeirdJoin(id: (row[0] as int)); + if (row.length > 2) { + model = model.copyWith( + unorthodox: UnorthodoxQuery.parseRow(row.skip(2).toList())); + } + if (row.length > 3) { + model = model.copyWith(song: SongQuery.parseRow(row.skip(3).toList())); + } + if (row.length > 8) { + model = model.copyWith( + numbas: [NumbaQuery.parseRow(row.skip(8).toList())] + .where((x) => x != null) + .toList()); + } + if (row.length > 10) { + model = model.copyWith( + foos: [FooQuery.parseRow(row.skip(10).toList())] + .where((x) => x != null) + .toList()); + } + return model; + } + + @override + deserialize(List row) { + return parseRow(row); + } + + @override + bool canCompile(trampoline) { + return (!(trampoline.contains('weird_joins') && + trampoline.contains('foo_pivots'))); + } + + @override + get(QueryExecutor executor) { + return super.get(executor).then((result) { + return result.fold>([], (out, model) { + var idx = out.indexWhere((m) => m.id == model.id); + + if (idx == -1) { + return out..add(model); + } else { + var l = out[idx]; + return out + ..[idx] = l.copyWith( + numbas: List<_Numba>.from(l.numbas ?? []) + ..addAll(model.numbas ?? []), + foos: List<_Foo>.from(l.foos ?? [])..addAll(model.foos ?? [])); + } + }); + }); + } + + @override + update(QueryExecutor executor) { + return super.update(executor).then((result) { + return result.fold>([], (out, model) { + var idx = out.indexWhere((m) => m.id == model.id); + + if (idx == -1) { + return out..add(model); + } else { + var l = out[idx]; + return out + ..[idx] = l.copyWith( + numbas: List<_Numba>.from(l.numbas ?? []) + ..addAll(model.numbas ?? []), + foos: List<_Foo>.from(l.foos ?? [])..addAll(model.foos ?? [])); + } + }); + }); + } + + @override + delete(QueryExecutor executor) { + return super.delete(executor).then((result) { + return result.fold>([], (out, model) { + var idx = out.indexWhere((m) => m.id == model.id); + + if (idx == -1) { + return out..add(model); + } else { + var l = out[idx]; + return out + ..[idx] = l.copyWith( + numbas: List<_Numba>.from(l.numbas ?? []) + ..addAll(model.numbas ?? []), + foos: List<_Foo>.from(l.foos ?? [])..addAll(model.foos ?? [])); + } + }); + }); + } +} + +class WeirdJoinQueryWhere extends QueryWhere { + WeirdJoinQueryWhere(WeirdJoinQuery query) + : id = NumericSqlExpressionBuilder(query, 'id'), + joinName = StringSqlExpressionBuilder(query, 'join_name'); + + final NumericSqlExpressionBuilder id; + + final StringSqlExpressionBuilder joinName; + + @override + get expressionBuilders { + return [id, joinName]; + } +} + +class WeirdJoinQueryValues extends MapQueryValues { + @override + get casts { + return {}; + } + + int get id { + return (values['id'] as int); + } + + set id(int value) => values['id'] = value; + String get joinName { + return (values['join_name'] as String); + } + + set joinName(String value) => values['join_name'] = value; + void copyFrom(WeirdJoin model) { + id = model.id; + if (model.unorthodox != null) { + values['join_name'] = model.unorthodox.name; + } + } +} + +class SongQuery extends Query { + SongQuery({Set trampoline}) { + trampoline ??= Set(); + trampoline.add(tableName); + _where = SongQueryWhere(this); + } + + @override + final SongQueryValues values = SongQueryValues(); + + SongQueryWhere _where; + + @override + get casts { + return {}; + } + + @override + get tableName { + return 'songs'; + } + + @override + get fields { + return const ['id', 'weird_join_id', 'title', 'created_at', 'updated_at']; + } + + @override + SongQueryWhere get where { + return _where; + } + + @override + SongQueryWhere newWhereClause() { + return SongQueryWhere(this); + } + + static Song parseRow(List row) { + if (row.every((x) => x == null)) return null; + var model = Song( + id: row[0].toString(), + weirdJoinId: (row[1] as int), + title: (row[2] as String), + createdAt: (row[3] as DateTime), + updatedAt: (row[4] as DateTime)); + return model; + } + + @override + deserialize(List row) { + return parseRow(row); + } +} + +class SongQueryWhere extends QueryWhere { + SongQueryWhere(SongQuery query) + : id = NumericSqlExpressionBuilder(query, 'id'), + weirdJoinId = NumericSqlExpressionBuilder(query, 'weird_join_id'), + title = StringSqlExpressionBuilder(query, 'title'), + createdAt = DateTimeSqlExpressionBuilder(query, 'created_at'), + updatedAt = DateTimeSqlExpressionBuilder(query, 'updated_at'); + + final NumericSqlExpressionBuilder id; + + final NumericSqlExpressionBuilder weirdJoinId; + + final StringSqlExpressionBuilder title; + + final DateTimeSqlExpressionBuilder createdAt; + + final DateTimeSqlExpressionBuilder updatedAt; + + @override + get expressionBuilders { + return [id, weirdJoinId, title, createdAt, updatedAt]; + } +} + +class SongQueryValues extends MapQueryValues { + @override + get casts { + return {}; + } + + String get id { + return (values['id'] as String); + } + + set id(String value) => values['id'] = value; + int get weirdJoinId { + return (values['weird_join_id'] as int); + } + + set weirdJoinId(int value) => values['weird_join_id'] = value; + String get title { + return (values['title'] as String); + } + + set title(String value) => values['title'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Song model) { + weirdJoinId = model.weirdJoinId; + title = model.title; + createdAt = model.createdAt; + updatedAt = model.updatedAt; + } +} + +class NumbaQuery extends Query { + NumbaQuery({Set trampoline}) { + trampoline ??= Set(); + trampoline.add(tableName); + _where = NumbaQueryWhere(this); + } + + @override + final NumbaQueryValues values = NumbaQueryValues(); + + NumbaQueryWhere _where; + + @override + get casts { + return {}; + } + + @override + get tableName { + return 'numbas'; + } + + @override + get fields { + return const ['i', 'parent']; + } + + @override + NumbaQueryWhere get where { + return _where; + } + + @override + NumbaQueryWhere newWhereClause() { + return NumbaQueryWhere(this); + } + + static Numba parseRow(List row) { + if (row.every((x) => x == null)) return null; + var model = Numba(i: (row[0] as int), parent: (row[1] as int)); + return model; + } + + @override + deserialize(List row) { + return parseRow(row); + } +} + +class NumbaQueryWhere extends QueryWhere { + NumbaQueryWhere(NumbaQuery query) + : i = NumericSqlExpressionBuilder(query, 'i'), + parent = NumericSqlExpressionBuilder(query, 'parent'); + + final NumericSqlExpressionBuilder i; + + final NumericSqlExpressionBuilder parent; + + @override + get expressionBuilders { + return [i, parent]; + } +} + +class NumbaQueryValues extends MapQueryValues { + @override + get casts { + return {}; + } + + int get i { + return (values['i'] as int); + } + + set i(int value) => values['i'] = value; + int get parent { + return (values['parent'] as int); + } + + set parent(int value) => values['parent'] = value; + void copyFrom(Numba model) { + i = model.i; + parent = model.parent; + } +} + +class FooQuery extends Query { + FooQuery({Set trampoline}) { + trampoline ??= Set(); + trampoline.add(tableName); + _where = FooQueryWhere(this); + leftJoin(FooPivotQuery(trampoline: trampoline), 'bar', 'foo_bar', + additionalFields: const ['id', 'join_name'], trampoline: trampoline); + } + + @override + final FooQueryValues values = FooQueryValues(); + + FooQueryWhere _where; + + @override + get casts { + return {}; + } + + @override + get tableName { + return 'foos'; + } + + @override + get fields { + return const ['bar']; + } + + @override + FooQueryWhere get where { + return _where; + } + + @override + FooQueryWhere newWhereClause() { + return FooQueryWhere(this); + } + + static Foo parseRow(List row) { + if (row.every((x) => x == null)) return null; + var model = Foo(bar: (row[0] as String)); + if (row.length > 1) { + model = model.copyWith( + weirdJoins: [WeirdJoinQuery.parseRow(row.skip(1).toList())] + .where((x) => x != null) + .toList()); + } + return model; + } + + @override + deserialize(List row) { + return parseRow(row); + } + + @override + bool canCompile(trampoline) { + return (!(trampoline.contains('foos') && + trampoline.contains('foo_pivots'))); + } + + @override + get(QueryExecutor executor) { + return super.get(executor).then((result) { + return result.fold>([], (out, model) { + var idx = out.indexWhere((m) => m.bar == model.bar); + + if (idx == -1) { + return out..add(model); + } else { + var l = out[idx]; + return out + ..[idx] = l.copyWith( + weirdJoins: List<_WeirdJoin>.from(l.weirdJoins ?? []) + ..addAll(model.weirdJoins ?? [])); + } + }); + }); + } + + @override + update(QueryExecutor executor) { + return super.update(executor).then((result) { + return result.fold>([], (out, model) { + var idx = out.indexWhere((m) => m.bar == model.bar); + + if (idx == -1) { + return out..add(model); + } else { + var l = out[idx]; + return out + ..[idx] = l.copyWith( + weirdJoins: List<_WeirdJoin>.from(l.weirdJoins ?? []) + ..addAll(model.weirdJoins ?? [])); + } + }); + }); + } + + @override + delete(QueryExecutor executor) { + return super.delete(executor).then((result) { + return result.fold>([], (out, model) { + var idx = out.indexWhere((m) => m.bar == model.bar); + + if (idx == -1) { + return out..add(model); + } else { + var l = out[idx]; + return out + ..[idx] = l.copyWith( + weirdJoins: List<_WeirdJoin>.from(l.weirdJoins ?? []) + ..addAll(model.weirdJoins ?? [])); + } + }); + }); + } +} + +class FooQueryWhere extends QueryWhere { + FooQueryWhere(FooQuery query) + : bar = StringSqlExpressionBuilder(query, 'bar'); + + final StringSqlExpressionBuilder bar; + + @override + get expressionBuilders { + return [bar]; + } +} + +class FooQueryValues extends MapQueryValues { + @override + get casts { + return {}; + } + + String get bar { + return (values['bar'] as String); + } + + set bar(String value) => values['bar'] = value; + void copyFrom(Foo model) { + bar = model.bar; + } +} + +class FooPivotQuery extends Query { + FooPivotQuery({Set trampoline}) { + trampoline ??= Set(); + trampoline.add(tableName); + _where = FooPivotQueryWhere(this); + leftJoin('weird_joins', 'weird_join_id', 'id', + additionalFields: const ['id', 'join_name'], trampoline: trampoline); + leftJoin('foos', 'foo_bar', 'bar', + additionalFields: const ['bar'], trampoline: trampoline); + } + + @override + final FooPivotQueryValues values = FooPivotQueryValues(); + + FooPivotQueryWhere _where; + + @override + get casts { + return {}; + } + + @override + get tableName { + return 'foo_pivots'; + } + + @override + get fields { + return const ['weird_join_id', 'foo_bar']; + } + + @override + FooPivotQueryWhere get where { + return _where; + } + + @override + FooPivotQueryWhere newWhereClause() { + return FooPivotQueryWhere(this); + } + + static FooPivot parseRow(List row) { + if (row.every((x) => x == null)) return null; + var model = FooPivot(); + if (row.length > 2) { + model = model.copyWith( + weirdJoin: WeirdJoinQuery.parseRow(row.skip(2).toList())); + } + if (row.length > 4) { + model = model.copyWith(foo: FooQuery.parseRow(row.skip(4).toList())); + } + return model; + } + + @override + deserialize(List row) { + return parseRow(row); + } +} + +class FooPivotQueryWhere extends QueryWhere { + FooPivotQueryWhere(FooPivotQuery query) + : weirdJoinId = NumericSqlExpressionBuilder(query, 'weird_join_id'), + fooBar = StringSqlExpressionBuilder(query, 'foo_bar'); + + final NumericSqlExpressionBuilder weirdJoinId; + + final StringSqlExpressionBuilder fooBar; + + @override + get expressionBuilders { + return [weirdJoinId, fooBar]; + } +} + +class FooPivotQueryValues extends MapQueryValues { + @override + get casts { + return {}; + } + + int get weirdJoinId { + return (values['weird_join_id'] as int); + } + + set weirdJoinId(int value) => values['weird_join_id'] = value; + String get fooBar { + return (values['foo_bar'] as String); + } + + set fooBar(String value) => values['foo_bar'] = value; + void copyFrom(FooPivot model) { + if (model.weirdJoin != null) { + values['weird_join_id'] = model.weirdJoin.id; + } + if (model.foo != null) { + values['foo_bar'] = model.foo.bar; + } + } +} + +// ************************************************************************** +// JsonModelGenerator +// ************************************************************************** + +@generatedSerializable +class Unorthodox implements _Unorthodox { + const Unorthodox({this.name}); + + @override + final String name; + + Unorthodox copyWith({String name}) { + return new Unorthodox(name: name ?? this.name); + } + + bool operator ==(other) { + return other is _Unorthodox && other.name == name; + } + + @override + int get hashCode { + return hashObjects([name]); + } + + Map toJson() { + return UnorthodoxSerializer.toMap(this); + } +} + +@generatedSerializable +class WeirdJoin implements _WeirdJoin { + const WeirdJoin( + {this.id, + this.unorthodox, + this.song, + List<_Numba> this.numbas, + List<_Foo> this.foos}); + + @override + final int id; + + @override + final _Unorthodox unorthodox; + + @override + final _Song song; + + @override + final List<_Numba> numbas; + + @override + final List<_Foo> foos; + + WeirdJoin copyWith( + {int id, + _Unorthodox unorthodox, + _Song song, + List<_Numba> numbas, + List<_Foo> foos}) { + return new WeirdJoin( + id: id ?? this.id, + unorthodox: unorthodox ?? this.unorthodox, + song: song ?? this.song, + numbas: numbas ?? this.numbas, + foos: foos ?? this.foos); + } + + bool operator ==(other) { + return other is _WeirdJoin && + other.id == id && + other.unorthodox == unorthodox && + other.song == song && + const ListEquality<_Numba>(const DefaultEquality<_Numba>()) + .equals(other.numbas, numbas) && + const ListEquality<_Foo>(const DefaultEquality<_Foo>()) + .equals(other.foos, foos); + } + + @override + int get hashCode { + return hashObjects([id, unorthodox, song, numbas, foos]); + } + + Map toJson() { + return WeirdJoinSerializer.toMap(this); + } +} + +@generatedSerializable +class Song extends _Song { + Song({this.id, this.weirdJoinId, this.title, this.createdAt, this.updatedAt}); + + @override + final String id; + + @override + final int weirdJoinId; + + @override + final String title; + + @override + final DateTime createdAt; + + @override + final DateTime updatedAt; + + Song copyWith( + {String id, + int weirdJoinId, + String title, + DateTime createdAt, + DateTime updatedAt}) { + return new Song( + id: id ?? this.id, + weirdJoinId: weirdJoinId ?? this.weirdJoinId, + title: title ?? this.title, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt); + } + + bool operator ==(other) { + return other is _Song && + other.id == id && + other.weirdJoinId == weirdJoinId && + other.title == title && + other.createdAt == createdAt && + other.updatedAt == updatedAt; + } + + @override + int get hashCode { + return hashObjects([id, weirdJoinId, title, createdAt, updatedAt]); + } + + Map toJson() { + return SongSerializer.toMap(this); + } +} + +@generatedSerializable +class Numba extends _Numba { + Numba({this.i, this.parent}); + + @override + final int i; + + @override + final int parent; + + Numba copyWith({int i, int parent}) { + return new Numba(i: i ?? this.i, parent: parent ?? this.parent); + } + + bool operator ==(other) { + return other is _Numba && other.i == i && other.parent == parent; + } + + @override + int get hashCode { + return hashObjects([i, parent]); + } + + Map toJson() { + return NumbaSerializer.toMap(this); + } +} + +@generatedSerializable +class Foo implements _Foo { + const Foo({this.bar, List<_WeirdJoin> this.weirdJoins}); + + @override + final String bar; + + @override + final List<_WeirdJoin> weirdJoins; + + Foo copyWith({String bar, List<_WeirdJoin> weirdJoins}) { + return new Foo( + bar: bar ?? this.bar, weirdJoins: weirdJoins ?? this.weirdJoins); + } + + bool operator ==(other) { + return other is _Foo && + other.bar == bar && + const ListEquality<_WeirdJoin>(const DefaultEquality<_WeirdJoin>()) + .equals(other.weirdJoins, weirdJoins); + } + + @override + int get hashCode { + return hashObjects([bar, weirdJoins]); + } + + Map toJson() { + return FooSerializer.toMap(this); + } +} + +@generatedSerializable +class FooPivot implements _FooPivot { + const FooPivot({this.weirdJoin, this.foo}); + + @override + final _WeirdJoin weirdJoin; + + @override + final _Foo foo; + + FooPivot copyWith({_WeirdJoin weirdJoin, _Foo foo}) { + return new FooPivot( + weirdJoin: weirdJoin ?? this.weirdJoin, foo: foo ?? this.foo); + } + + bool operator ==(other) { + return other is _FooPivot && + other.weirdJoin == weirdJoin && + other.foo == foo; + } + + @override + int get hashCode { + return hashObjects([weirdJoin, foo]); + } + + Map toJson() { + return FooPivotSerializer.toMap(this); + } +} + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +abstract class UnorthodoxSerializer { + static Unorthodox fromMap(Map map) { + return new Unorthodox(name: map['name'] as String); + } + + static Map toMap(_Unorthodox model) { + if (model == null) { + return null; + } + return {'name': model.name}; + } +} + +abstract class UnorthodoxFields { + static const List allFields = [name]; + + static const String name = 'name'; +} + +abstract class WeirdJoinSerializer { + static WeirdJoin fromMap(Map map) { + return new WeirdJoin( + id: map['id'] as int, + unorthodox: map['unorthodox'] != null + ? UnorthodoxSerializer.fromMap(map['unorthodox'] as Map) + : null, + song: map['song'] != null + ? SongSerializer.fromMap(map['song'] as Map) + : null, + numbas: map['numbas'] is Iterable + ? new List.unmodifiable( + ((map['numbas'] as Iterable).where((x) => x is Map)) + .cast() + .map(NumbaSerializer.fromMap)) + : null, + foos: map['foos'] is Iterable + ? new List.unmodifiable( + ((map['foos'] as Iterable).where((x) => x is Map)) + .cast() + .map(FooSerializer.fromMap)) + : null); + } + + static Map toMap(_WeirdJoin model) { + if (model == null) { + return null; + } + return { + 'id': model.id, + 'unorthodox': UnorthodoxSerializer.toMap(model.unorthodox), + 'song': SongSerializer.toMap(model.song), + 'numbas': model.numbas?.map((m) => NumbaSerializer.toMap(m))?.toList(), + 'foos': model.foos?.map((m) => FooSerializer.toMap(m))?.toList() + }; + } +} + +abstract class WeirdJoinFields { + static const List allFields = [ + id, + unorthodox, + song, + numbas, + foos + ]; + + static const String id = 'id'; + + static const String unorthodox = 'unorthodox'; + + static const String song = 'song'; + + static const String numbas = 'numbas'; + + static const String foos = 'foos'; +} + +abstract class SongSerializer { + static Song fromMap(Map map) { + return new Song( + id: map['id'] as String, + weirdJoinId: map['weird_join_id'] as int, + title: map['title'] as String, + createdAt: map['created_at'] != null + ? (map['created_at'] is DateTime + ? (map['created_at'] as DateTime) + : DateTime.parse(map['created_at'].toString())) + : null, + updatedAt: map['updated_at'] != null + ? (map['updated_at'] is DateTime + ? (map['updated_at'] as DateTime) + : DateTime.parse(map['updated_at'].toString())) + : null); + } + + static Map toMap(_Song model) { + if (model == null) { + return null; + } + return { + 'id': model.id, + 'weird_join_id': model.weirdJoinId, + 'title': model.title, + 'created_at': model.createdAt?.toIso8601String(), + 'updated_at': model.updatedAt?.toIso8601String() + }; + } +} + +abstract class SongFields { + static const List allFields = [ + id, + weirdJoinId, + title, + createdAt, + updatedAt + ]; + + static const String id = 'id'; + + static const String weirdJoinId = 'weird_join_id'; + + static const String title = 'title'; + + static const String createdAt = 'created_at'; + + static const String updatedAt = 'updated_at'; +} + +abstract class NumbaSerializer { + static Numba fromMap(Map map) { + return new Numba(i: map['i'] as int, parent: map['parent'] as int); + } + + static Map toMap(_Numba model) { + if (model == null) { + return null; + } + return {'i': model.i, 'parent': model.parent}; + } +} + +abstract class NumbaFields { + static const List allFields = [i, parent]; + + static const String i = 'i'; + + static const String parent = 'parent'; +} + +abstract class FooSerializer { + static Foo fromMap(Map map) { + return new Foo( + bar: map['bar'] as String, + weirdJoins: map['weird_joins'] is Iterable + ? new List.unmodifiable( + ((map['weird_joins'] as Iterable).where((x) => x is Map)) + .cast() + .map(WeirdJoinSerializer.fromMap)) + : null); + } + + static Map toMap(_Foo model) { + if (model == null) { + return null; + } + return { + 'bar': model.bar, + 'weird_joins': + model.weirdJoins?.map((m) => WeirdJoinSerializer.toMap(m))?.toList() + }; + } +} + +abstract class FooFields { + static const List allFields = [bar, weirdJoins]; + + static const String bar = 'bar'; + + static const String weirdJoins = 'weird_joins'; +} + +abstract class FooPivotSerializer { + static FooPivot fromMap(Map map) { + return new FooPivot( + weirdJoin: map['weird_join'] != null + ? WeirdJoinSerializer.fromMap(map['weird_join'] as Map) + : null, + foo: map['foo'] != null + ? FooSerializer.fromMap(map['foo'] as Map) + : null); + } + + static Map toMap(_FooPivot model) { + if (model == null) { + return null; + } + return { + 'weird_join': WeirdJoinSerializer.toMap(model.weirdJoin), + 'foo': FooSerializer.toMap(model.foo) + }; + } +} + +abstract class FooPivotFields { + static const List allFields = [weirdJoin, foo]; + + static const String weirdJoin = 'weird_join'; + + static const String foo = 'foo'; +} diff --git a/angel_orm_generator/test/models/user.g.dart b/angel_orm_generator/test/models/user.g.dart index 22c17b9f..5663ebe3 100644 --- a/angel_orm_generator/test/models/user.g.dart +++ b/angel_orm_generator/test/models/user.g.dart @@ -228,11 +228,11 @@ class UserQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; String get username { return (values['username'] as String); } @@ -368,10 +368,10 @@ class RoleUserQueryValues extends MapQueryValues { set userId(int value) => values['user_id'] = value; void copyFrom(RoleUser model) { if (model.role != null) { - values['role_id'] = int.parse(model.role.id); + values['role_id'] = model.role.id; } if (model.user != null) { - values['user_id'] = int.parse(model.user.id); + values['user_id'] = model.user.id; } } } @@ -535,11 +535,11 @@ class RoleQueryValues extends MapQueryValues { return {}; } - int get id { - return (values['id'] as int); + String get id { + return (values['id'] as String); } - set id(int value) => values['id'] = value; + set id(String value) => values['id'] = value; String get name { return (values['name'] as String); } diff --git a/angel_orm_generator/test/standalone_test.dart b/angel_orm_generator/test/standalone_test.dart index 5816961b..167ebd36 100644 --- a/angel_orm_generator/test/standalone_test.dart +++ b/angel_orm_generator/test/standalone_test.dart @@ -78,7 +78,7 @@ main() { }); test('union', () async { - var query1 = new CarQuery()..where.make.like((_) => '%Fer%'); + var query1 = new CarQuery()..where.make.like('%Fer%'); var query2 = new CarQuery()..where.familyFriendly.isTrue; var query3 = new CarQuery()..where.description.equals('Submarine'); var union = query1.union(query2).unionAll(query3); @@ -89,7 +89,7 @@ main() { test('or clause', () async { var query = new CarQuery() - ..where.make.like((_) => 'Fer%') + ..where.make.like('Fer%') ..orWhere((where) => where ..familyFriendly.isTrue ..make.equals('Honda'));