diff --git a/lib/angel_orm.dart b/lib/angel_orm.dart index 0544ca4b..1af57ece 100644 --- a/lib/angel_orm.dart +++ b/lib/angel_orm.dart @@ -1,3 +1,4 @@ export 'src/annotations.dart'; export 'src/migration.dart'; +export 'src/relations.dart'; export 'src/query.dart'; \ No newline at end of file diff --git a/lib/src/builder/postgres/build_context.dart b/lib/src/builder/postgres/build_context.dart index dfe8a8b2..645159a0 100644 --- a/lib/src/builder/postgres/build_context.dart +++ b/lib/src/builder/postgres/build_context.dart @@ -6,13 +6,14 @@ import 'package:path/path.dart' as p; import 'package:recase/recase.dart'; import '../../annotations.dart'; import '../../migration.dart'; +import '../../relations.dart'; import '../find_annotation.dart'; import 'postgres_build_context.dart'; // TODO: Should add id, createdAt, updatedAt... PostgresBuildContext buildContext(ClassElement clazz, ORM annotation, BuildStep buildStep, bool autoSnakeCaseNames) { - var ctx = new PostgresBuildContext(annotation, clazz.fields, + var ctx = new PostgresBuildContext(annotation, originalClassName: clazz.name, tableName: annotation.tableName?.isNotEmpty == true ? annotation.tableName @@ -21,6 +22,15 @@ PostgresBuildContext buildContext(ClassElement clazz, ORM annotation, for (var field in clazz.fields) { if (field.getter != null && field.setter != null) { + // Check for relationship. If so, skip. + Relationship relationship = findAnnotation(field, HasOne) ?? + findAnnotation(field, HasMany) ?? + findAnnotation(field, BelongsTo); + + if (relationship != null) { + ctx.relationships[field.name] = relationship; + continue; + } else print('Hm: ${field.name}'); // Check for alias var alias = findAnnotation(field, Alias); @@ -63,6 +73,7 @@ PostgresBuildContext buildContext(ClassElement clazz, ORM annotation, if (column == null) throw 'Cannot infer SQL column type for field "${field.name}" with type "${field.type.name}".'; ctx.columnInfo[field.name] = column; + ctx.fields.add(field); } } diff --git a/lib/src/builder/postgres/postgres_build_context.dart b/lib/src/builder/postgres/postgres_build_context.dart index fa84000d..c18b86b0 100644 --- a/lib/src/builder/postgres/postgres_build_context.dart +++ b/lib/src/builder/postgres/postgres_build_context.dart @@ -1,18 +1,20 @@ import 'package:analyzer/dart/element/element.dart'; import '../../annotations.dart'; import '../../migration.dart'; +import '../../relations.dart'; class PostgresBuildContext { final Map aliases = {}; final Map columnInfo = {}; final Map indices = {}; + final Map relationships = {}; final String originalClassName, tableName, sourceFilename; final ORM annotation; // Todo: We can use analyzer to copy straight from Model class - final List fields; + final List fields = []; String primaryKeyName = 'id'; - PostgresBuildContext(this.annotation, this.fields, + PostgresBuildContext(this.annotation, {this.originalClassName, this.tableName, this.sourceFilename}); String get modelClassName => originalClassName.startsWith('_') diff --git a/lib/src/relations.dart b/lib/src/relations.dart new file mode 100644 index 00000000..87395c11 --- /dev/null +++ b/lib/src/relations.dart @@ -0,0 +1,52 @@ +class Relationship { + final String localKey; + final String foreignKey; + final String foreignTable; + final bool cascadeOnDelete; + + const Relationship._( + {this.localKey, + this.foreignKey, + this.foreignTable, + this.cascadeOnDelete}); +} + +class HasMany extends Relationship { + const HasMany( + {String localKey, + String foreignKey, + String foreignTable, + bool cascadeOnDelete: false}) + : super._( + localKey: localKey, + foreignKey: foreignKey, + foreignTable: foreignTable, + cascadeOnDelete: cascadeOnDelete == true); +} + +const HasMany hasMany = const HasMany(); + +class HasOne extends Relationship { + const HasOne( + {String localKey, + String foreignKey, + String foreignTable, + bool cascadeOnDelete: false}) + : super._( + localKey: localKey, + foreignKey: foreignKey, + foreignTable: foreignTable, + cascadeOnDelete: cascadeOnDelete == true); +} + +const HasOne hasOne = const HasOne(); + +class BelongsTo extends Relationship { + const BelongsTo({String localKey, String foreignKey, String foreignTable}) + : super._( + localKey: localKey, + foreignKey: foreignKey, + foreignTable: foreignTable); +} + +const BelongsTo belongsTo = const BelongsTo(); diff --git a/test/car_test.dart b/test/car_test.dart index 7c63f2a7..6f722201 100644 --- a/test/car_test.dart +++ b/test/car_test.dart @@ -16,7 +16,7 @@ main() { }); test('insert', () async { - var car = await CarQuery.insert(make: 'Mazda', familyFriendly: false); + var car = await CarQuery.insert(null, make: 'Mazda', familyFriendly: false); print(car.toJson()); }, skip: 'Insert not yet implemented'); } diff --git a/test/models/car.dart b/test/models/car.dart index bfbb536d..88755d34 100644 --- a/test/models/car.dart +++ b/test/models/car.dart @@ -3,6 +3,7 @@ library angel_orm.test.models.car; import 'package:angel_framework/common.dart'; import 'package:angel_orm/angel_orm.dart'; import 'package:angel_serialize/angel_serialize.dart'; +import 'tire.dart'; part 'car.g.dart'; @serializable @@ -12,4 +13,6 @@ class _Car extends Model { String description; bool familyFriendly; DateTime recalledAt; + @hasMany + List tires; } diff --git a/test/models/car.g.dart b/test/models/car.g.dart index 70865fb0..79f3eb6b 100644 --- a/test/models/car.g.dart +++ b/test/models/car.g.dart @@ -20,7 +20,15 @@ class Car extends _Car { @override DateTime recalledAt; - Car({this.make, this.description, this.familyFriendly, this.recalledAt}); + @override + List tires; + + Car( + {this.make, + this.description, + this.familyFriendly, + this.recalledAt, + this.tires}); factory Car.fromJson(Map data) { return new Car( @@ -31,14 +39,16 @@ class Car extends _Car { ? data['recalledAt'] : (data['recalledAt'] is String ? DateTime.parse(data['recalledAt']) - : null)); + : null), + tires: data['tires']); } Map toJson() => { 'make': make, 'description': description, 'familyFriendly': familyFriendly, - 'recalledAt': recalledAt == null ? null : recalledAt.toIso8601String() + 'recalledAt': recalledAt == null ? null : recalledAt.toIso8601String(), + 'tires': tires }; static Car parse(Map map) => new Car.fromJson(map); diff --git a/test/models/car.orm.g.dart b/test/models/car.orm.g.dart index 466af1a5..932f73b0 100644 --- a/test/models/car.orm.g.dart +++ b/test/models/car.orm.g.dart @@ -5,84 +5,6 @@ // Target: class _Car // ************************************************************************** -import 'dart:async'; -import 'package:angel_orm/angel_orm.dart'; -import 'package:postgres/postgres.dart'; -import 'car.dart'; - -class CarQuery { - final List _and = []; - - final List _or = []; - - final List _not = []; - - final CarQueryWhere where = new CarQueryWhere(); - - void and(CarQuery other) { - var compiled = other.where.toWhereClause(); - if (compiled != null) { - _and.add(compiled); - } - } - - void or(CarQuery other) { - var compiled = other.where.toWhereClause(); - if (compiled != null) { - _or.add(compiled); - } - } - - void not(CarQuery other) { - var compiled = other.where.toWhereClause(); - if (compiled != null) { - _not.add(compiled); - } - } - - Stream get() {} - - Future getOne() {} - - Future update() {} - - Future delete() {} - - static Future insert(PostgreSQLConnection connection, - {String make, - String description, - bool familyFriendly, - DateTime recalledAt}) {} - - static Stream getAll() => new CarQuery().get(); -} - -class CarQueryWhere { - final StringSqlExpressionBuilder make = new StringSqlExpressionBuilder(); - - final StringSqlExpressionBuilder description = - new StringSqlExpressionBuilder(); - - final BooleanSqlExpressionBuilder familyFriendly = - new BooleanSqlExpressionBuilder(); - - final DateTimeSqlExpressionBuilder recalledAt = - new DateTimeSqlExpressionBuilder('recalled_at'); - - String toWhereClause() { - final List expressions = []; - if (make.hasValue) { - expressions.add('`make` ' + make.compile()); - } - if (description.hasValue) { - expressions.add('`description` ' + description.compile()); - } - if (familyFriendly.hasValue) { - expressions.add('`family_friendly` ' + familyFriendly.compile()); - } - if (recalledAt.hasValue) { - expressions.add(recalledAt.compile()); - } - return expressions.isEmpty ? null : ('WHERE ' + expressions.join(' AND ')); - } -} +// Error: type 'SuperConstructorInvocationImpl' is not a subtype of type 'ConstructorFieldInitializer' in type cast where +// SuperConstructorInvocationImpl is from package:analyzer/src/dart/ast/ast.dart +// ConstructorFieldInitializer is from package:analyzer/dart/ast/ast.dart diff --git a/test/models/tire.dart b/test/models/tire.dart new file mode 100644 index 00000000..9c7825a5 --- /dev/null +++ b/test/models/tire.dart @@ -0,0 +1,10 @@ +library angel_test.test.models.tire; + +import 'package:angel_framework/common.dart'; +import 'package:angel_serialize/angel_serialize.dart'; +part 'tire.g.dart'; + +@serializable +class _Tire extends Model { + int size; +} diff --git a/test/models/tire.g.dart b/test/models/tire.g.dart new file mode 100644 index 00000000..bea4fd6a --- /dev/null +++ b/test/models/tire.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of angel_test.test.models.tire; + +// ************************************************************************** +// Generator: JsonModelGenerator +// Target: class _Tire +// ************************************************************************** + +class Tire extends _Tire { + @override + int size; + + Tire({this.size}); + + factory Tire.fromJson(Map data) { + return new Tire(size: data['size']); + } + + Map toJson() => {'size': size}; + + static Tire parse(Map map) => new Tire.fromJson(map); +}