diff --git a/angel_orm/CHANGELOG.md b/angel_orm/CHANGELOG.md index ee958cf5..4821a3cf 100644 --- a/angel_orm/CHANGELOG.md +++ b/angel_orm/CHANGELOG.md @@ -8,6 +8,7 @@ ORM queries to reference their joined subqueries. * Removed deprecated `Join`, `toSql`, `sanitizeExpression`, `isAscii`. * Always put `ORDER BY` before `LIMIT`. * `and`, `or`, `not` in `QueryWhere` include parentheses. +* Add `joinType` to `Relationship` class. # 2.0.2 * Place `LIMIT` and `OFFSET` after `ORDER BY`. diff --git a/angel_orm/lib/src/relations.dart b/angel_orm/lib/src/relations.dart index 5edeed39..1ca6e357 100644 --- a/angel_orm/lib/src/relations.dart +++ b/angel_orm/lib/src/relations.dart @@ -29,7 +29,7 @@ class HasMany extends Relationship { String foreignKey, String foreignTable, bool cascadeOnDelete = false, - JoinType joinType = JoinType.left}) + JoinType joinType}) : super(RelationshipType.hasMany, localKey: localKey, foreignKey: foreignKey, @@ -46,7 +46,7 @@ class HasOne extends Relationship { String foreignKey, String foreignTable, bool cascadeOnDelete = false, - JoinType joinType = JoinType.left}) + JoinType joinType}) : super(RelationshipType.hasOne, localKey: localKey, foreignKey: foreignKey, @@ -62,7 +62,7 @@ class BelongsTo extends Relationship { {String localKey, String foreignKey, String foreignTable, - JoinType joinType = JoinType.left}) + JoinType joinType}) : super(RelationshipType.belongsTo, localKey: localKey, foreignKey: foreignKey, @@ -80,7 +80,7 @@ class ManyToMany extends Relationship { String foreignKey, String foreignTable, bool cascadeOnDelete = false, - JoinType joinType = JoinType.left}) + JoinType joinType}) : super( RelationshipType.hasMany, // Many-to-Many is actually just a hasMany localKey: localKey, diff --git a/angel_orm_generator/CHANGELOG.md b/angel_orm_generator/CHANGELOG.md index a35e8481..68cfe691 100644 --- a/angel_orm_generator/CHANGELOG.md +++ b/angel_orm_generator/CHANGELOG.md @@ -1,3 +1,8 @@ +# 2.1.0 +* Relationships have always generated subqueries; now these subqueries are +available as `Query` objects on generated classes. +* Support explicitly-defined join types for relations. + # 2.0.5 * Remove `ShimFieldImpl` check, which broke relations. * Fix bug where primary key type would not be emitted in migrations. diff --git a/angel_orm_generator/lib/src/orm_build_context.dart b/angel_orm_generator/lib/src/orm_build_context.dart index 8a94dcb8..134b65cc 100644 --- a/angel_orm_generator/lib/src/orm_build_context.dart +++ b/angel_orm_generator/lib/src/orm_build_context.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/src/dart/constant/value.dart'; import 'package:angel_model/angel_model.dart'; import 'package:angel_orm/angel_orm.dart'; import 'package:angel_serialize/angel_serialize.dart'; @@ -240,9 +241,17 @@ Future buildOrmContext( var joinType = JoinType.left; var joinTypeRdr = cr.peek('joinType')?.objectValue; if (joinTypeRdr != null) { - var idx = joinTypeRdr.getField('index')?.toIntValue(); - if (idx != null) { - joinType = JoinType.values[idx]; + // Unfortunately, the analyzer library provides little to nothing + // in the way of reading enums from source, so here's a hack. + var joinTypeType = (joinTypeRdr.type as InterfaceType); + var enumFields = + joinTypeType.element.fields.where((f) => f.isEnumConstant).toList(); + + for (int i = 0; i < enumFields.length; i++) { + if (enumFields[i].constantValue == joinTypeRdr) { + joinType = JoinType.values[i]; + break; + } } } @@ -255,7 +264,7 @@ Future buildOrmContext( through: through, foreign: foreign, throughContext: throughContext, - joinType: joinType ?? JoinType.left, + joinType: joinType, ); // print('Relation on ${buildCtx.originalClassName}.${field.name} => ' diff --git a/angel_orm_generator/lib/src/orm_generator.dart b/angel_orm_generator/lib/src/orm_generator.dart index 87096780..6077456e 100644 --- a/angel_orm_generator/lib/src/orm_generator.dart +++ b/angel_orm_generator/lib/src/orm_generator.dart @@ -327,7 +327,8 @@ class OrmGenerator extends GeneratorForAnnotation { joinArgs.insert( 0, refer('_$fieldName').assign(queryInstantiation)); - b.addExpression(refer('leftJoin').call(joinArgs, { + var joinType = relation.joinTypeString; + b.addExpression(refer(joinType).call(joinArgs, { 'additionalFields': literalConstList(additionalFields.toList()), 'trampoline': refer('trampoline'), diff --git a/angel_orm_generator/lib/src/readers.dart b/angel_orm_generator/lib/src/readers.dart index a1039514..bef5f0f2 100644 --- a/angel_orm_generator/lib/src/readers.dart +++ b/angel_orm_generator/lib/src/readers.dart @@ -44,12 +44,27 @@ class RelationshipReader { this.through, this.foreign, this.throughContext, - this.joinType = JoinType.left}); + this.joinType}); bool get isManyToMany => type == RelationshipType.hasMany && throughContext != null; - String get joinTypeString => joinType.toString().replaceAll('JoinType.', ''); + String get joinTypeString { + switch (joinType ?? JoinType.left) { + case JoinType.inner: + return 'join'; + case JoinType.left: + return 'leftJoin'; + case JoinType.right: + return 'rightJoin'; + case JoinType.full: + return 'fullOuterJoin'; + case JoinType.self: + return 'selfJoin'; + default: + return 'join'; + } + } FieldElement findLocalField(OrmBuildContext ctx) { return ctx.effectiveFields.firstWhere( diff --git a/angel_orm_test/lib/src/models/book.g.dart b/angel_orm_test/lib/src/models/book.g.dart index b72cdd3f..9b0d1bdf 100644 --- a/angel_orm_test/lib/src/models/book.g.dart +++ b/angel_orm_test/lib/src/models/book.g.dart @@ -55,11 +55,11 @@ class BookQuery extends Query { trampoline ??= Set(); trampoline.add(tableName); _where = BookQueryWhere(this); - leftJoin(_author = AuthorQuery(trampoline: trampoline, parent: this), + join(_author = AuthorQuery(trampoline: trampoline, parent: this), 'author_id', 'id', additionalFields: const ['id', 'created_at', 'updated_at', 'name'], trampoline: trampoline); - leftJoin(_partnerAuthor = AuthorQuery(trampoline: trampoline, parent: this), + join(_partnerAuthor = AuthorQuery(trampoline: trampoline, parent: this), 'partner_author_id', 'id', additionalFields: const ['id', 'created_at', 'updated_at', 'name'], trampoline: trampoline);