belongs to
This commit is contained in:
parent
207cd50afe
commit
ff12ed6bc0
23 changed files with 694 additions and 135 deletions
|
@ -1,3 +1,12 @@
|
|||
# 2.0.0-dev.12
|
||||
* Always apply `toSql` escapes.
|
||||
|
||||
# 2.0.0-dev.11
|
||||
* Remove `limit(1)` except on `getOne`
|
||||
|
||||
# 2.0.0-dev.10
|
||||
* Add `withFields` to `compile()`
|
||||
|
||||
# 2.0.0-dev.9
|
||||
* Permanent preamble fix
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:intl/intl.dart' show DateFormat;
|
||||
import 'package:string_scanner/string_scanner.dart';
|
||||
import 'query.dart';
|
||||
|
||||
final DateFormat dateYmd = new DateFormat('yyyy-MM-dd');
|
||||
final DateFormat dateYmdHms = new DateFormat('yyyy-MM-dd HH:mm:ss');
|
||||
|
@ -148,8 +149,8 @@ class StringSqlExpressionBuilder implements SqlExpressionBuilder<String> {
|
|||
String compile() {
|
||||
if (_raw != null) return _raw;
|
||||
if (_value == null) return null;
|
||||
var v = sanitizeExpression(_value);
|
||||
return "$_op '$v'";
|
||||
var v = toSql(_value);
|
||||
return "$_op $v";
|
||||
}
|
||||
|
||||
void isEmpty() => equals('');
|
||||
|
|
|
@ -15,7 +15,8 @@ abstract class QueryBase<T> {
|
|||
/// A String of all [fields], joined by a comma (`,`).
|
||||
String get fieldSet => fields.join(', ');
|
||||
|
||||
String compile({bool includeTableName: false, String preamble});
|
||||
String compile(
|
||||
{bool includeTableName: false, String preamble, bool withFields: true});
|
||||
|
||||
T deserialize(List row);
|
||||
|
||||
|
@ -54,6 +55,8 @@ String toSql(Object obj) {
|
|||
} else if (obj == null) {
|
||||
return 'NULL';
|
||||
} else if (obj is String) {
|
||||
var s = obj.replaceAll("'", "\\'");
|
||||
return "'$s'";
|
||||
var b = new StringBuffer();
|
||||
var it = obj.runes.iterator;
|
||||
|
||||
|
@ -82,10 +85,10 @@ String toSql(Object obj) {
|
|||
|
||||
/// A SQL `SELECT` query builder.
|
||||
abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||
final List<JoinBuilder> _joins = [];
|
||||
final List<OrderBy> _orderBy = [];
|
||||
String _crossJoin, _groupBy;
|
||||
int _limit, _offset;
|
||||
JoinBuilder _join;
|
||||
|
||||
/// The table against which to execute this query.
|
||||
String get tableName;
|
||||
|
@ -152,53 +155,68 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
|||
_crossJoin = tableName;
|
||||
}
|
||||
|
||||
String _joinAlias() => 'a${_joins.length}';
|
||||
|
||||
/// Execute an `INNER JOIN` against another table.
|
||||
void join(String tableName, String localKey, String foreignKey,
|
||||
{String op: '='}) {
|
||||
_join = new JoinBuilder(
|
||||
{String op: '=', List<String> additionalFields: const []}) {
|
||||
_joins.add(new JoinBuilder(
|
||||
JoinType.inner, this, tableName, localKey, foreignKey,
|
||||
op: op);
|
||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
||||
}
|
||||
|
||||
/// Execute a `LEFT JOIN` against another table.
|
||||
void leftJoin(String tableName, String localKey, String foreignKey,
|
||||
{String op: '='}) {
|
||||
_join = new JoinBuilder(
|
||||
{String op: '=', List<String> additionalFields: const []}) {
|
||||
_joins.add(new JoinBuilder(
|
||||
JoinType.left, this, tableName, localKey, foreignKey,
|
||||
op: op);
|
||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
||||
}
|
||||
|
||||
/// Execute a `RIGHT JOIN` against another table.
|
||||
void rightJoin(String tableName, String localKey, String foreignKey,
|
||||
{String op: '='}) {
|
||||
_join = new JoinBuilder(
|
||||
{String op: '=', List<String> additionalFields: const []}) {
|
||||
_joins.add(new JoinBuilder(
|
||||
JoinType.right, this, tableName, localKey, foreignKey,
|
||||
op: op);
|
||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
||||
}
|
||||
|
||||
/// Execute a `FULL OUTER JOIN` against another table.
|
||||
void fullOuterJoin(String tableName, String localKey, String foreignKey,
|
||||
{String op: '='}) {
|
||||
_join = new JoinBuilder(
|
||||
{String op: '=', List<String> additionalFields: const []}) {
|
||||
_joins.add(new JoinBuilder(
|
||||
JoinType.full, this, tableName, localKey, foreignKey,
|
||||
op: op);
|
||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
||||
}
|
||||
|
||||
/// Execute a `SELF JOIN`.
|
||||
void selfJoin(String tableName, String localKey, String foreignKey,
|
||||
{String op: '='}) {
|
||||
_join = new JoinBuilder(
|
||||
{String op: '=', List<String> additionalFields: const []}) {
|
||||
_joins.add(new JoinBuilder(
|
||||
JoinType.self, this, tableName, localKey, foreignKey,
|
||||
op: op);
|
||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
||||
}
|
||||
|
||||
@override
|
||||
String compile({bool includeTableName: false, String preamble}) {
|
||||
String compile(
|
||||
{bool includeTableName: false, String preamble, bool withFields: true}) {
|
||||
includeTableName = includeTableName || _joins.isNotEmpty;
|
||||
var b = new StringBuffer(preamble ?? 'SELECT');
|
||||
b.write(' ');
|
||||
var f = fields ?? ['*'];
|
||||
if (includeTableName) f = f.map((s) => '$tableName.$s').toList();
|
||||
b.write(f.join(', '));
|
||||
List<String> f;
|
||||
|
||||
if (fields == null) {
|
||||
f = ['*'];
|
||||
} else {
|
||||
f = new List<String>.from(
|
||||
fields.map((s) => includeTableName ? '$tableName.$s' : s));
|
||||
_joins.forEach((j) {
|
||||
f
|
||||
..add(j.fieldName)
|
||||
..addAll(j.additionalFields.map((s) => j.nameFor(s)));
|
||||
});
|
||||
}
|
||||
if (withFields) b.write(f.join(', '));
|
||||
b.write(' FROM $tableName');
|
||||
var whereClause =
|
||||
where.compile(tableName: includeTableName ? tableName : null);
|
||||
|
@ -208,7 +226,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
|||
if (_groupBy != null) b.write(' GROUP BY $_groupBy');
|
||||
for (var item in _orderBy) b.write(' ${item.compile()}');
|
||||
if (_crossJoin != null) b.write(' CROSS JOIN $_crossJoin');
|
||||
if (_join != null) b.write(' ${_join.compile()}');
|
||||
for (var join in _joins) b.write(' ${join.compile()}');
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
|
@ -219,14 +237,13 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
|||
}
|
||||
|
||||
Future<List<T>> delete(QueryExecutor executor) async {
|
||||
var sql = compile(preamble: 'DELETE FROM $tableName');
|
||||
var sql = compile(preamble: 'DELETE', withFields: false);
|
||||
return executor
|
||||
.query(sql, fields)
|
||||
.then((it) => it.map(deserialize).toList());
|
||||
}
|
||||
|
||||
Future<T> deleteOne(QueryExecutor executor) {
|
||||
limit(1);
|
||||
return delete(executor).then((it) => it.isEmpty ? null : it.first);
|
||||
}
|
||||
|
||||
|
@ -260,7 +277,6 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
|||
}
|
||||
|
||||
Future<T> updateOne(QueryExecutor executor) {
|
||||
limit(1);
|
||||
return update(executor).then((it) => it.isEmpty ? null : it.first);
|
||||
}
|
||||
}
|
||||
|
@ -381,7 +397,8 @@ class Union<T> extends QueryBase<T> {
|
|||
T deserialize(List row) => left.deserialize(row);
|
||||
|
||||
@override
|
||||
String compile({bool includeTableName: false, String preamble}) {
|
||||
String compile(
|
||||
{bool includeTableName: false, String preamble, bool withFields: true}) {
|
||||
var selector = all == true ? 'UNION ALL' : 'UNION';
|
||||
return '(${left.compile(includeTableName: includeTableName)}) $selector (${right.compile(includeTableName: includeTableName)})';
|
||||
}
|
||||
|
@ -391,15 +408,28 @@ class Union<T> extends QueryBase<T> {
|
|||
class JoinBuilder {
|
||||
final JoinType type;
|
||||
final Query from;
|
||||
final String to, key, value, op;
|
||||
final String to, key, value, op, alias;
|
||||
final List<String> additionalFields;
|
||||
|
||||
JoinBuilder(this.type, this.from, this.to, this.key, this.value,
|
||||
{this.op: '='});
|
||||
{this.op: '=', this.alias, this.additionalFields: const []});
|
||||
|
||||
String get fieldName {
|
||||
var right = '$to.$value';
|
||||
if (alias != null) right = '$alias.$value';
|
||||
return right;
|
||||
}
|
||||
|
||||
String nameFor(String name) {
|
||||
var right = '$to.$name';
|
||||
if (alias != null) right = '$alias.$name';
|
||||
return right;
|
||||
}
|
||||
|
||||
String compile() {
|
||||
var b = new StringBuffer();
|
||||
var left = '${from.tableName}.$key';
|
||||
var right = '$to.$value';
|
||||
var right = fieldName;
|
||||
|
||||
switch (type) {
|
||||
case JoinType.inner:
|
||||
|
@ -419,7 +449,9 @@ class JoinBuilder {
|
|||
break;
|
||||
}
|
||||
|
||||
b.write(' $to ON $left$op$right');
|
||||
b.write(' $to');
|
||||
if (alias != null) b.write(' $alias');
|
||||
b.write(' ON $left$op$right');
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,8 +50,7 @@ class HasOne extends Relationship {
|
|||
const HasOne hasOne = const HasOne();
|
||||
|
||||
class BelongsTo extends Relationship {
|
||||
const BelongsTo(
|
||||
{String localKey: 'id', String foreignKey, String foreignTable})
|
||||
const BelongsTo({String localKey, String foreignKey, String foreignTable})
|
||||
: super(RelationshipType.belongsTo,
|
||||
localKey: localKey,
|
||||
foreignKey: foreignKey,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel_orm
|
||||
version: 2.0.0-dev.9
|
||||
version: 2.0.0-dev.12
|
||||
description: Runtime support for Angel's ORM. Includes base classes for queries.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/orm
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
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:angel_orm/angel_orm.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:angel_serialize_generator/angel_serialize_generator.dart';
|
||||
import 'package:angel_serialize_generator/build_context.dart';
|
||||
import 'package:angel_serialize_generator/context.dart';
|
||||
import 'package:build/build.dart';
|
||||
|
@ -12,6 +15,15 @@ import 'package:source_gen/source_gen.dart';
|
|||
|
||||
import 'readers.dart';
|
||||
|
||||
bool isHasRelation(Relationship r) =>
|
||||
r.type == RelationshipType.hasOne || r.type == RelationshipType.hasMany;
|
||||
|
||||
bool isBelongsRelation(Relationship r) =>
|
||||
r.type == RelationshipType.belongsTo ||
|
||||
r.type == RelationshipType.belongsToMany;
|
||||
|
||||
final Map<Uri, OrmBuildContext> _cache = {};
|
||||
|
||||
Future<OrmBuildContext> buildOrmContext(
|
||||
ClassElement clazz,
|
||||
ConstantReader annotation,
|
||||
|
@ -20,6 +32,21 @@ Future<OrmBuildContext> buildOrmContext(
|
|||
bool autoSnakeCaseNames,
|
||||
bool autoIdAndDateFields,
|
||||
{bool heedExclude: true}) async {
|
||||
// Check for @generatedSerializable
|
||||
// ignore: unused_local_variable
|
||||
DartObject generatedSerializable;
|
||||
|
||||
while ((generatedSerializable =
|
||||
const TypeChecker.fromRuntime(GeneratedSerializable)
|
||||
.firstAnnotationOf(clazz)) !=
|
||||
null) {
|
||||
clazz = clazz.supertype.element;
|
||||
}
|
||||
|
||||
var uri = clazz.source.uri;
|
||||
if (_cache.containsKey(uri)) {
|
||||
return _cache[uri];
|
||||
}
|
||||
var buildCtx = await buildContext(clazz, annotation, buildStep, resolver,
|
||||
autoSnakeCaseNames, autoIdAndDateFields,
|
||||
heedExclude: heedExclude);
|
||||
|
@ -30,6 +57,7 @@ Future<OrmBuildContext> buildOrmContext(
|
|||
(ormAnnotation.tableName?.isNotEmpty == true)
|
||||
? ormAnnotation.tableName
|
||||
: pluralize(new ReCase(clazz.name).snakeCase));
|
||||
_cache[uri] = ctx;
|
||||
|
||||
// Read all fields
|
||||
for (var field in buildCtx.fields) {
|
||||
|
@ -65,9 +93,84 @@ Future<OrmBuildContext> buildOrmContext(
|
|||
);
|
||||
}
|
||||
|
||||
if (column?.type == null)
|
||||
throw 'Cannot infer SQL column type for field "${field.name}" with type "${field.type.name}".';
|
||||
ctx.columns[field.name] = column;
|
||||
// Try to find a relationship
|
||||
var ann = relationshipTypeChecker.firstAnnotationOf(field);
|
||||
|
||||
if (ann != null) {
|
||||
var cr = new ConstantReader(ann);
|
||||
var rc = ctx.buildContext.modelClassNameRecase;
|
||||
var type = cr.read('type').intValue;
|
||||
var localKey = cr.peek('localKey')?.stringValue;
|
||||
var foreignKey = cr.peek('foreignKey')?.stringValue;
|
||||
var foreignTable = cr.peek('foreignTable')?.stringValue;
|
||||
var cascadeOnDelete = cr.peek('cascadeOnDelete')?.boolValue == true;
|
||||
OrmBuildContext foreign;
|
||||
|
||||
if (foreignTable == null) {
|
||||
if (!isModelClass(field.type)) {
|
||||
throw new UnsupportedError(
|
||||
'Cannot apply relationship to field "${field.name}" - ${field.type.name} is not assignable to Model.');
|
||||
} else {
|
||||
try {
|
||||
foreign = await buildOrmContext(
|
||||
field.type.element as ClassElement,
|
||||
new ConstantReader(const TypeChecker.fromRuntime(Serializable)
|
||||
.firstAnnotationOf(field.type.element)),
|
||||
buildStep,
|
||||
resolver,
|
||||
autoSnakeCaseNames,
|
||||
autoIdAndDateFields);
|
||||
var ormAnn = const TypeChecker.fromRuntime(Orm)
|
||||
.firstAnnotationOf(field.type.element);
|
||||
|
||||
if (ormAnn != null) {
|
||||
foreignTable =
|
||||
new ConstantReader(ormAnn).peek('tableName')?.stringValue;
|
||||
}
|
||||
|
||||
foreignTable ??=
|
||||
pluralize(foreign.buildContext.modelClassNameRecase.snakeCase);
|
||||
} on StackOverflowError {
|
||||
throw new UnsupportedError(
|
||||
'There is an infinite cycle between ${clazz.name} and ${field.type.name}. This triggered a stack overflow.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in missing keys
|
||||
var rcc = new ReCase(field.name);
|
||||
if (type == RelationshipType.hasOne || type == RelationshipType.hasMany) {
|
||||
localKey ??= 'id';
|
||||
foreignKey ??= '${rc.snakeCase}_id';
|
||||
} else if (type == RelationshipType.belongsTo ||
|
||||
type == RelationshipType.belongsToMany) {
|
||||
localKey ??= '${rcc.snakeCase}_id';
|
||||
foreignKey ??= 'id';
|
||||
}
|
||||
|
||||
var relation = new Relationship(
|
||||
type,
|
||||
localKey: localKey,
|
||||
foreignKey: foreignKey,
|
||||
foreignTable: foreignTable,
|
||||
cascadeOnDelete: cascadeOnDelete,
|
||||
);
|
||||
|
||||
if (isBelongsRelation(relation)) {
|
||||
var name = new ReCase(relation.localKey).camelCase;
|
||||
ctx.buildContext.aliases[name] = relation.localKey;
|
||||
ctx.effectiveFields.add(new RelationFieldImpl(
|
||||
name, field.type.element.context.typeProvider.intType, field.name));
|
||||
}
|
||||
|
||||
ctx.relations[field.name] = relation;
|
||||
ctx.relationTypes[relation] = foreign;
|
||||
} else {
|
||||
if (column?.type == null)
|
||||
throw 'Cannot infer SQL column type for field "${field.name}" with type "${field.type.name}".';
|
||||
ctx.columns[field.name] = column;
|
||||
ctx.effectiveFields.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
return ctx;
|
||||
|
@ -112,12 +215,18 @@ Column reviveColumn(ConstantReader cr) {
|
|||
);
|
||||
}
|
||||
|
||||
const TypeChecker relationshipTypeChecker =
|
||||
const TypeChecker.fromRuntime(Relationship);
|
||||
|
||||
class OrmBuildContext {
|
||||
final BuildContext buildContext;
|
||||
final Orm ormAnnotation;
|
||||
final String tableName;
|
||||
|
||||
final Map<String, Column> columns = {};
|
||||
final List<FieldElement> effectiveFields = [];
|
||||
final Map<String, Relationship> relations = {};
|
||||
final Map<Relationship, OrmBuildContext> relationTypes = {};
|
||||
|
||||
OrmBuildContext(this.buildContext, this.ormAnnotation, this.tableName);
|
||||
}
|
||||
|
@ -128,3 +237,9 @@ class _ColumnType implements ColumnType {
|
|||
|
||||
_ColumnType(this.name);
|
||||
}
|
||||
|
||||
class RelationFieldImpl extends ShimFieldImpl {
|
||||
final String originalFieldName;
|
||||
RelationFieldImpl(String name, DartType type, this.originalFieldName)
|
||||
: super(name, type);
|
||||
}
|
||||
|
|
|
@ -98,8 +98,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
..annotations.add(refer('override'))
|
||||
..type = MethodType.getter
|
||||
..body = new Block((b) {
|
||||
b.addExpression(
|
||||
refer('${rc.pascalCase}Fields').property('allFields').returned);
|
||||
var names = ctx.effectiveFields
|
||||
.map((f) =>
|
||||
literalString(ctx.buildContext.resolveFieldName(f.name)))
|
||||
.toList();
|
||||
b.addExpression(literalConstList(names).returned);
|
||||
});
|
||||
}));
|
||||
|
||||
|
@ -125,8 +128,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
// Add deserialize()
|
||||
clazz.methods.add(new Method((m) {
|
||||
m
|
||||
..name = 'deserialize'
|
||||
..annotations.add(refer('override'))
|
||||
..name = 'parseRow'
|
||||
..static = true
|
||||
..returns = ctx.buildContext.modelClassType
|
||||
..requiredParameters.add(new Parameter((b) => b
|
||||
..name = 'row'
|
||||
..type = refer('List')))
|
||||
|
@ -134,23 +138,95 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
int i = 0;
|
||||
var args = <String, Expression>{};
|
||||
|
||||
for (var field in ctx.buildContext.fields) {
|
||||
for (var field in ctx.effectiveFields) {
|
||||
Reference type = convertTypeReference(field.type);
|
||||
if (isSpecialId(field)) type = refer('int');
|
||||
|
||||
var expr = (refer('row').index(literalNum(i++)));
|
||||
if (isSpecialId(field))
|
||||
expr = expr.property('toString').call([]);
|
||||
else if (field is RelationFieldImpl)
|
||||
continue;
|
||||
else
|
||||
expr = expr.asA(type);
|
||||
|
||||
args[field.name] = expr;
|
||||
}
|
||||
|
||||
b.addExpression(
|
||||
ctx.buildContext.modelClassType.newInstance([], args).returned);
|
||||
b.addExpression(ctx.buildContext.modelClassType
|
||||
.newInstance([], args).assignVar('model'));
|
||||
|
||||
ctx.relations.forEach((name, relation) {
|
||||
var foreign = ctx.relationTypes[relation];
|
||||
var skipToList = refer('row')
|
||||
.property('skip')
|
||||
.call([literalNum(i)])
|
||||
.property('toList')
|
||||
.call([]);
|
||||
var parsed = refer(
|
||||
'${foreign.buildContext.modelClassNameRecase.pascalCase}Query')
|
||||
.property('parseRow')
|
||||
.call([skipToList]);
|
||||
var expr =
|
||||
refer('model').property('copyWith').call([], {name: parsed});
|
||||
var block = new Block(
|
||||
(b) => b.addExpression(refer('model').assign(expr)));
|
||||
var blockStr = block.accept(new DartEmitter());
|
||||
var ifStr = 'if (row.length > $i) { $blockStr }';
|
||||
b.statements.add(new Code(ifStr));
|
||||
i += ctx.relationTypes[relation].effectiveFields.length;
|
||||
});
|
||||
|
||||
b.addExpression(refer('model').returned);
|
||||
});
|
||||
}));
|
||||
|
||||
clazz.methods.add(new Method((m) {
|
||||
m
|
||||
..name = 'deserialize'
|
||||
..annotations.add(refer('override'))
|
||||
..requiredParameters.add(new Parameter((b) => b
|
||||
..name = 'row'
|
||||
..type = refer('List')))
|
||||
..body = new Block((b) {
|
||||
b.addExpression(refer('parseRow').call([refer('row')]).returned);
|
||||
});
|
||||
}));
|
||||
|
||||
// If there are any relations, we need some overrides.
|
||||
if (ctx.relations.isNotEmpty) {
|
||||
clazz.methods.add(new Method((b) {
|
||||
b
|
||||
..name = 'get'
|
||||
..annotations.add(refer('override'))
|
||||
..requiredParameters.add(new Parameter((b) => b..name = 'executor'))
|
||||
..body = new Block((b) {
|
||||
ctx.relations.forEach((fieldName, relation) {
|
||||
//var name = ctx.buildContext.resolveFieldName(fieldName);
|
||||
if (relation.type == RelationshipType.belongsTo) {
|
||||
var foreign = ctx.relationTypes[relation];
|
||||
var additionalFields = foreign.effectiveFields
|
||||
.where((f) => f.name != 'id' || !isSpecialId(f))
|
||||
.map((f) => literalString(
|
||||
foreign.buildContext.resolveFieldName(f.name)));
|
||||
var joinArgs = [
|
||||
relation.foreignTable,
|
||||
relation.localKey,
|
||||
relation.foreignKey
|
||||
].map(literalString);
|
||||
b.addExpression(refer('leftJoin').call(joinArgs, {
|
||||
'additionalFields':
|
||||
literalConstList(additionalFields.toList())
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
b.addExpression(refer('super')
|
||||
.property('get')
|
||||
.call([refer('executor')]).returned);
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -172,14 +248,15 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
..annotations.add(refer('override'))
|
||||
..type = MethodType.getter
|
||||
..body = new Block((b) {
|
||||
var references = ctx.buildContext.fields.map((f) => refer(f.name));
|
||||
var references = ctx.effectiveFields.map((f) => refer(f.name));
|
||||
b.addExpression(literalList(references).returned);
|
||||
});
|
||||
}));
|
||||
|
||||
// Add builders for each field
|
||||
for (var field in ctx.buildContext.fields) {
|
||||
for (var field in ctx.effectiveFields) {
|
||||
// TODO: Handle fields with relations
|
||||
var name = field.name;
|
||||
Reference builderType;
|
||||
|
||||
if (const TypeChecker.fromRuntime(int).isExactlyType(field.type) ||
|
||||
|
@ -197,6 +274,16 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
} else if (const TypeChecker.fromRuntime(DateTime)
|
||||
.isExactlyType(field.type)) {
|
||||
builderType = refer('DateTimeSqlExpressionBuilder');
|
||||
} else if (ctx.relations.containsKey(field.name)) {
|
||||
var relation = ctx.relations[field.name];
|
||||
if (!isBelongsRelation(relation))
|
||||
continue;
|
||||
else {
|
||||
builderType = new TypeReference((b) => b
|
||||
..symbol = 'NumericSqlExpressionBuilder'
|
||||
..types.add(refer('int')));
|
||||
name = relation.localKey;
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedError(
|
||||
'Cannot generate ORM code for field of type ${field.type.name}.');
|
||||
|
@ -204,7 +291,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
|
||||
clazz.fields.add(new Field((b) {
|
||||
b
|
||||
..name = field.name
|
||||
..name = name
|
||||
..modifier = FieldModifier.final$
|
||||
..type = builderType
|
||||
..assignment = builderType.newInstance([
|
||||
|
@ -223,7 +310,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
..extend = refer('MapQueryValues');
|
||||
|
||||
// Each field generates a getter for setter
|
||||
for (var field in ctx.buildContext.fields) {
|
||||
for (var field in ctx.effectiveFields) {
|
||||
var name = ctx.buildContext.resolveFieldName(field.name);
|
||||
var type = isSpecialId(field)
|
||||
? refer('int')
|
||||
|
@ -264,14 +351,34 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
..body = new Block((b) {
|
||||
var args = <String, Expression>{};
|
||||
|
||||
for (var field in ctx.buildContext.fields) {
|
||||
if (isSpecialId(field)) continue;
|
||||
for (var field in ctx.effectiveFields) {
|
||||
if (isSpecialId(field) || field is RelationFieldImpl) continue;
|
||||
args[ctx.buildContext.resolveFieldName(field.name)] =
|
||||
refer('model').property(field.name);
|
||||
}
|
||||
|
||||
b.addExpression(
|
||||
refer('values').property('addAll').call([literalMap(args)]));
|
||||
|
||||
for (var field in ctx.effectiveFields) {
|
||||
if (field is RelationFieldImpl) {
|
||||
var original = field.originalFieldName;
|
||||
var prop = refer('model').property(original);
|
||||
// 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 cond = prop.notEqualTo(literalNull);
|
||||
var condStr = cond.accept(new DartEmitter());
|
||||
var blkStr =
|
||||
new Block((b) => b.addExpression(target.assign(parsedId)))
|
||||
.accept(new DartEmitter());
|
||||
var ifStmt = new Code('if ($condStr) { $blkStr }');
|
||||
b.statements.add(ifStmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -6,7 +6,8 @@ homepage: https://github.com/angel-dart/orm
|
|||
environment:
|
||||
sdk: ">=2.0.0-dev <3.0.0"
|
||||
dependencies:
|
||||
angel_orm: ^2.0.0-dev
|
||||
angel_orm:
|
||||
path: ../angel_orm
|
||||
angel_serialize_generator: ^2.0.0
|
||||
build: ">=0.12.0 <2.0.0"
|
||||
build_config: ^0.3.0
|
||||
|
|
|
@ -7,31 +7,37 @@ import 'models/book.dart';
|
|||
import 'common.dart';
|
||||
|
||||
main() {
|
||||
PostgresExecutor connection;
|
||||
Author rowling;
|
||||
PostgresExecutor executor;
|
||||
Author jkRowling;
|
||||
Author jameson;
|
||||
Book deathlyHallows;
|
||||
|
||||
setUp(() async {
|
||||
connection = await connectToPostgres(['author', 'book']);
|
||||
executor = await connectToPostgres(['author', 'book']);
|
||||
|
||||
// Insert an author
|
||||
rowling = await AuthorQuery.insert(connection, name: 'J.K. Rowling');
|
||||
jameson = await AuthorQuery.insert(connection, name: 'J.K. Jameson');
|
||||
var query = new AuthorQuery()..values.name = 'J.K. Rowling';
|
||||
jkRowling = await query.insert(executor);
|
||||
|
||||
query.values.name = 'J.K. Jameson';
|
||||
jameson = await query.insert(executor);
|
||||
|
||||
// And a book
|
||||
deathlyHallows = await BookQuery.insert(connection,
|
||||
authorId: int.parse(rowling.id),
|
||||
name: 'Deathly Hallows',
|
||||
partnerAuthorId: int.parse(jameson.id));
|
||||
var bookQuery = new BookQuery();
|
||||
bookQuery.values
|
||||
..authorId = int.parse(jkRowling.id)
|
||||
..partnerAuthorId = int.parse(jameson.id)
|
||||
..name = 'Deathly Hallows';
|
||||
|
||||
deathlyHallows = await bookQuery.insert(executor);
|
||||
});
|
||||
|
||||
tearDown(() => connection.close());
|
||||
tearDown(() => executor.close());
|
||||
|
||||
group('selects', () {
|
||||
test('select all', () async {
|
||||
var query = new BookQuery();
|
||||
var books = await query.get(connection).toList();
|
||||
var books = await query.get(executor);
|
||||
expect(books, hasLength(1));
|
||||
|
||||
var book = books.first;
|
||||
|
@ -39,36 +45,35 @@ main() {
|
|||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.name, deathlyHallows.name);
|
||||
|
||||
var author = book.author as Author;
|
||||
var author = book.author;
|
||||
print(author.toJson());
|
||||
expect(author.id, rowling.id);
|
||||
expect(author.name, rowling.name);
|
||||
expect(author.id, jkRowling.id);
|
||||
expect(author.name, jkRowling.name);
|
||||
});
|
||||
|
||||
test('select one', () async {
|
||||
var query = new BookQuery();
|
||||
query.where.id.equals(int.parse(deathlyHallows.id));
|
||||
print(query.toSql());
|
||||
print(query.compile());
|
||||
|
||||
var book =
|
||||
await BookQuery.getOne(int.parse(deathlyHallows.id), connection);
|
||||
var book = await query.getOne(executor);
|
||||
print(book.toJson());
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.name, deathlyHallows.name);
|
||||
|
||||
var author = book.author as Author;
|
||||
var author = book.author;
|
||||
print(author.toJson());
|
||||
expect(author.id, rowling.id);
|
||||
expect(author.name, rowling.name);
|
||||
expect(author.id, jkRowling.id);
|
||||
expect(author.name, jkRowling.name);
|
||||
});
|
||||
|
||||
test('where clause', () async {
|
||||
var query = new BookQuery()
|
||||
..where.name.equals('Goblet of Fire')
|
||||
..or(new BookQueryWhere()..authorId.equals(int.parse(rowling.id)));
|
||||
print(query.toSql());
|
||||
..orWhere((w) => w.authorId.equals(int.parse(jkRowling.id)));
|
||||
print(query.compile());
|
||||
|
||||
var books = await query.get(connection).toList();
|
||||
var books = await query.get(executor);
|
||||
expect(books, hasLength(1));
|
||||
|
||||
var book = books.first;
|
||||
|
@ -76,10 +81,10 @@ main() {
|
|||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.name, deathlyHallows.name);
|
||||
|
||||
var author = book.author as Author;
|
||||
var author = book.author;
|
||||
print(author.toJson());
|
||||
expect(author.id, rowling.id);
|
||||
expect(author.name, rowling.name);
|
||||
expect(author.id, jkRowling.id);
|
||||
expect(author.name, jkRowling.name);
|
||||
});
|
||||
|
||||
test('union', () async {
|
||||
|
@ -90,9 +95,9 @@ main() {
|
|||
query1
|
||||
..union(query2)
|
||||
..unionAll(query3);
|
||||
print(query1.toSql());
|
||||
print(query1.compile());
|
||||
|
||||
var books = await query1.get(connection).toList();
|
||||
var books = await query1.get(executor);
|
||||
expect(books, hasLength(1));
|
||||
|
||||
var book = books.first;
|
||||
|
@ -100,36 +105,38 @@ main() {
|
|||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.name, deathlyHallows.name);
|
||||
|
||||
var author = book.author as Author;
|
||||
var author = book.author;
|
||||
print(author.toJson());
|
||||
expect(author.id, rowling.id);
|
||||
expect(author.name, rowling.name);
|
||||
expect(author.id, jkRowling.id);
|
||||
expect(author.name, jkRowling.name);
|
||||
});
|
||||
});
|
||||
|
||||
test('insert sets relationship', () {
|
||||
expect(deathlyHallows.author, isNotNull);
|
||||
expect((deathlyHallows.author).name, rowling.name);
|
||||
expect(deathlyHallows.author, jkRowling);
|
||||
//expect(deathlyHallows.author, isNotNull);
|
||||
//expect(deathlyHallows.author.name, rowling.name);
|
||||
});
|
||||
|
||||
test('delete stream', () async {
|
||||
var query = new BookQuery()..where.name.equals(deathlyHallows.name);
|
||||
print(query.toSql());
|
||||
var books = await query.delete(connection).toList();
|
||||
print(query.compile());
|
||||
var books = await query.delete(executor);
|
||||
expect(books, hasLength(1));
|
||||
|
||||
var book = books.first;
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.author, isNotNull);
|
||||
expect((book.author).name, rowling.name);
|
||||
expect((book.author).name, jkRowling.name);
|
||||
});
|
||||
|
||||
test('update book', () async {
|
||||
var cloned = deathlyHallows.clone()..name = 'Sorcerer\'s Stone';
|
||||
var book = await BookQuery.updateBook(connection, cloned);
|
||||
var cloned = deathlyHallows.copyWith(name: "Sorcerer's Stone");
|
||||
var query = new BookQuery()..values.copyFrom(cloned);
|
||||
var book = await query.updateOne(executor);
|
||||
print(book.toJson());
|
||||
expect(book.name, cloned.name);
|
||||
expect(book.author, isNotNull);
|
||||
expect((book.author as Author).name, rowling.name);
|
||||
expect(book.author.name, jkRowling.name);
|
||||
});
|
||||
}
|
||||
|
|
6
angel_orm_generator/test/migrations/author.sql
Normal file
6
angel_orm_generator/test/migrations/author.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
CREATE TEMPORARY TABLE "authors" (
|
||||
id serial PRIMARY KEY,
|
||||
name varchar(255) UNIQUE NOT NULL,
|
||||
created_at timestamp,
|
||||
updated_at timestamp
|
||||
);
|
8
angel_orm_generator/test/migrations/book.sql
Normal file
8
angel_orm_generator/test/migrations/book.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
CREATE TEMPORARY TABLE "books" (
|
||||
id serial PRIMARY KEY,
|
||||
author_id int NOT NULL,
|
||||
partner_author_id int,
|
||||
name varchar(255),
|
||||
created_at timestamp,
|
||||
updated_at timestamp
|
||||
);
|
|
@ -20,7 +20,7 @@ class AuthorQuery extends Query<Author, AuthorQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return AuthorFields.allFields;
|
||||
return const ['id', 'name', 'created_at', 'updated_at'];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -28,13 +28,18 @@ class AuthorQuery extends Query<Author, AuthorQueryWhere> {
|
|||
return new AuthorQueryWhere();
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return new Author(
|
||||
static Author parseRow(List row) {
|
||||
var model = new Author(
|
||||
id: row[0].toString(),
|
||||
name: (row[1] as String),
|
||||
createdAt: (row[2] as DateTime),
|
||||
updatedAt: (row[3] as DateTime));
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,5 @@ class _Book extends Model {
|
|||
@BelongsTo(localKey: "partner_author_id")
|
||||
Author partnerAuthor;
|
||||
|
||||
int authorId;
|
||||
String name;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,142 @@
|
|||
|
||||
part of angel_orm.generator.models.book;
|
||||
|
||||
// **************************************************************************
|
||||
// OrmGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class BookQuery extends Query<Book, BookQueryWhere> {
|
||||
@override
|
||||
final BookQueryValues values = new BookQueryValues();
|
||||
|
||||
@override
|
||||
final BookQueryWhere where = new BookQueryWhere();
|
||||
|
||||
@override
|
||||
get tableName {
|
||||
return 'books';
|
||||
}
|
||||
|
||||
@override
|
||||
get fields {
|
||||
return const [
|
||||
'id',
|
||||
'author_id',
|
||||
'partner_author_id',
|
||||
'name',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
BookQueryWhere newWhereClause() {
|
||||
return new BookQueryWhere();
|
||||
}
|
||||
|
||||
static Book parseRow(List row) {
|
||||
var model = new Book(
|
||||
id: row[0].toString(),
|
||||
name: (row[3] as String),
|
||||
createdAt: (row[4] as DateTime),
|
||||
updatedAt: (row[5] as DateTime));
|
||||
if (row.length > 6) {
|
||||
model =
|
||||
model.copyWith(author: AuthorQuery.parseRow(row.skip(6).toList()));
|
||||
}
|
||||
if (row.length > 10) {
|
||||
model = model.copyWith(
|
||||
partnerAuthor: AuthorQuery.parseRow(row.skip(10).toList()));
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
|
||||
@override
|
||||
get(executor) {
|
||||
leftJoin('authors', 'author_id', 'id',
|
||||
additionalFields: const ['name', 'created_at', 'updated_at']);
|
||||
leftJoin('authors', 'partner_author_id', 'id',
|
||||
additionalFields: const ['name', 'created_at', 'updated_at']);
|
||||
return super.get(executor);
|
||||
}
|
||||
}
|
||||
|
||||
class BookQueryWhere extends QueryWhere {
|
||||
final NumericSqlExpressionBuilder<int> id =
|
||||
new NumericSqlExpressionBuilder<int>('id');
|
||||
|
||||
final NumericSqlExpressionBuilder<int> authorId =
|
||||
new NumericSqlExpressionBuilder<int>('author_id');
|
||||
|
||||
final NumericSqlExpressionBuilder<int> partnerAuthorId =
|
||||
new NumericSqlExpressionBuilder<int>('partner_author_id');
|
||||
|
||||
final StringSqlExpressionBuilder name =
|
||||
new StringSqlExpressionBuilder('name');
|
||||
|
||||
final DateTimeSqlExpressionBuilder createdAt =
|
||||
new DateTimeSqlExpressionBuilder('created_at');
|
||||
|
||||
final DateTimeSqlExpressionBuilder updatedAt =
|
||||
new DateTimeSqlExpressionBuilder('updated_at');
|
||||
|
||||
@override
|
||||
get expressionBuilders {
|
||||
return [id, authorId, partnerAuthorId, name, createdAt, updatedAt];
|
||||
}
|
||||
}
|
||||
|
||||
class BookQueryValues extends MapQueryValues {
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
||||
void set id(int value) => values['id'] = value;
|
||||
int get authorId {
|
||||
return (values['author_id'] as int);
|
||||
}
|
||||
|
||||
void set authorId(int value) => values['author_id'] = value;
|
||||
int get partnerAuthorId {
|
||||
return (values['partner_author_id'] as int);
|
||||
}
|
||||
|
||||
void set partnerAuthorId(int value) => values['partner_author_id'] = value;
|
||||
String get name {
|
||||
return (values['name'] as String);
|
||||
}
|
||||
|
||||
void set name(String value) => values['name'] = value;
|
||||
DateTime get createdAt {
|
||||
return (values['created_at'] as DateTime);
|
||||
}
|
||||
|
||||
void set createdAt(DateTime value) => values['created_at'] = value;
|
||||
DateTime get updatedAt {
|
||||
return (values['updated_at'] as DateTime);
|
||||
}
|
||||
|
||||
void set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Book model) {
|
||||
values.addAll({
|
||||
'name': model.name,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
if (model.author != null) {
|
||||
values['author_id'] = int.parse(model.author.id);
|
||||
}
|
||||
if (model.partnerAuthor != null) {
|
||||
values['partner_author_id'] = int.parse(model.partnerAuthor.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
@ -12,7 +148,6 @@ class Book extends _Book {
|
|||
{this.id,
|
||||
this.author,
|
||||
this.partnerAuthor,
|
||||
this.authorId,
|
||||
this.name,
|
||||
this.createdAt,
|
||||
this.updatedAt});
|
||||
|
@ -26,9 +161,6 @@ class Book extends _Book {
|
|||
@override
|
||||
final Author partnerAuthor;
|
||||
|
||||
@override
|
||||
final int authorId;
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
|
@ -42,7 +174,6 @@ class Book extends _Book {
|
|||
{String id,
|
||||
Author author,
|
||||
Author partnerAuthor,
|
||||
int authorId,
|
||||
String name,
|
||||
DateTime createdAt,
|
||||
DateTime updatedAt}) {
|
||||
|
@ -50,7 +181,6 @@ class Book extends _Book {
|
|||
id: id ?? this.id,
|
||||
author: author ?? this.author,
|
||||
partnerAuthor: partnerAuthor ?? this.partnerAuthor,
|
||||
authorId: authorId ?? this.authorId,
|
||||
name: name ?? this.name,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt);
|
||||
|
@ -61,7 +191,6 @@ class Book extends _Book {
|
|||
other.id == id &&
|
||||
other.author == author &&
|
||||
other.partnerAuthor == partnerAuthor &&
|
||||
other.authorId == authorId &&
|
||||
other.name == name &&
|
||||
other.createdAt == createdAt &&
|
||||
other.updatedAt == updatedAt;
|
||||
|
@ -69,8 +198,7 @@ class Book extends _Book {
|
|||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects(
|
||||
[id, author, partnerAuthor, authorId, name, createdAt, updatedAt]);
|
||||
return hashObjects([id, author, partnerAuthor, name, createdAt, updatedAt]);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
|
@ -16,7 +16,6 @@ abstract class BookSerializer {
|
|||
partnerAuthor: map['partner_author'] != null
|
||||
? AuthorSerializer.fromMap(map['partner_author'] as Map)
|
||||
: null,
|
||||
authorId: map['author_id'] as int,
|
||||
name: map['name'] as String,
|
||||
createdAt: map['created_at'] != null
|
||||
? (map['created_at'] is DateTime
|
||||
|
@ -38,7 +37,6 @@ abstract class BookSerializer {
|
|||
'id': model.id,
|
||||
'author': AuthorSerializer.toMap(model.author),
|
||||
'partner_author': AuthorSerializer.toMap(model.partnerAuthor),
|
||||
'author_id': model.authorId,
|
||||
'name': model.name,
|
||||
'created_at': model.createdAt?.toIso8601String(),
|
||||
'updated_at': model.updatedAt?.toIso8601String()
|
||||
|
@ -51,7 +49,6 @@ abstract class BookFields {
|
|||
id,
|
||||
author,
|
||||
partnerAuthor,
|
||||
authorId,
|
||||
name,
|
||||
createdAt,
|
||||
updatedAt
|
||||
|
@ -63,8 +60,6 @@ abstract class BookFields {
|
|||
|
||||
static const String partnerAuthor = 'partner_author';
|
||||
|
||||
static const String authorId = 'author_id';
|
||||
|
||||
static const String name = 'name';
|
||||
|
||||
static const String createdAt = 'created_at';
|
||||
|
|
|
@ -20,7 +20,15 @@ class CarQuery extends Query<Car, CarQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return CarFields.allFields;
|
||||
return const [
|
||||
'id',
|
||||
'make',
|
||||
'description',
|
||||
'family_friendly',
|
||||
'recalled_at',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -28,9 +36,8 @@ class CarQuery extends Query<Car, CarQueryWhere> {
|
|||
return new CarQueryWhere();
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return new Car(
|
||||
static Car parseRow(List row) {
|
||||
var model = new Car(
|
||||
id: row[0].toString(),
|
||||
make: (row[1] as String),
|
||||
description: (row[2] as String),
|
||||
|
@ -38,6 +45,12 @@ class CarQuery extends Query<Car, CarQueryWhere> {
|
|||
recalledAt: (row[4] as DateTime),
|
||||
createdAt: (row[5] as DateTime),
|
||||
updatedAt: (row[6] as DateTime));
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class CustomerQuery extends Query<Customer, CustomerQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return CustomerFields.allFields;
|
||||
return const ['id', 'created_at', 'updated_at'];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -28,12 +28,17 @@ class CustomerQuery extends Query<Customer, CustomerQueryWhere> {
|
|||
return new CustomerQueryWhere();
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return new Customer(
|
||||
static Customer parseRow(List row) {
|
||||
var model = new Customer(
|
||||
id: row[0].toString(),
|
||||
createdAt: (row[1] as DateTime),
|
||||
updatedAt: (row[2] as DateTime));
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class FootQuery extends Query<Foot, FootQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return FootFields.allFields;
|
||||
return const ['id', 'leg_id', 'n_toes', 'created_at', 'updated_at'];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -28,14 +28,19 @@ class FootQuery extends Query<Foot, FootQueryWhere> {
|
|||
return new FootQueryWhere();
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return new Foot(
|
||||
static Foot parseRow(List row) {
|
||||
var model = new Foot(
|
||||
id: row[0].toString(),
|
||||
legId: (row[1] as int),
|
||||
nToes: (row[2] as int),
|
||||
createdAt: (row[3] as DateTime),
|
||||
updatedAt: (row[4] as DateTime));
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class FruitQuery extends Query<Fruit, FruitQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return FruitFields.allFields;
|
||||
return const ['id', 'tree_id', 'common_name', 'created_at', 'updated_at'];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -28,14 +28,19 @@ class FruitQuery extends Query<Fruit, FruitQueryWhere> {
|
|||
return new FruitQueryWhere();
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return new Fruit(
|
||||
static Fruit parseRow(List row) {
|
||||
var model = new Fruit(
|
||||
id: row[0].toString(),
|
||||
treeId: (row[1] as int),
|
||||
commonName: (row[2] as String),
|
||||
createdAt: (row[3] as DateTime),
|
||||
updatedAt: (row[4] as DateTime));
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,104 @@
|
|||
|
||||
part of angel_orm_generator.test.models.leg;
|
||||
|
||||
// **************************************************************************
|
||||
// OrmGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class LegQuery extends Query<Leg, LegQueryWhere> {
|
||||
@override
|
||||
final LegQueryValues values = new LegQueryValues();
|
||||
|
||||
@override
|
||||
final LegQueryWhere where = new LegQueryWhere();
|
||||
|
||||
@override
|
||||
get tableName {
|
||||
return 'legs';
|
||||
}
|
||||
|
||||
@override
|
||||
get fields {
|
||||
return const ['id', 'name', 'created_at', 'updated_at'];
|
||||
}
|
||||
|
||||
@override
|
||||
LegQueryWhere newWhereClause() {
|
||||
return new LegQueryWhere();
|
||||
}
|
||||
|
||||
static Leg parseRow(List row) {
|
||||
var model = new Leg(
|
||||
id: row[0].toString(),
|
||||
name: (row[1] as String),
|
||||
createdAt: (row[2] as DateTime),
|
||||
updatedAt: (row[3] as DateTime));
|
||||
if (row.length > 4) {
|
||||
model = model.copyWith(foot: FootQuery.parseRow(row.skip(4).toList()));
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
|
||||
@override
|
||||
get(executor) {
|
||||
return super.get(executor);
|
||||
}
|
||||
}
|
||||
|
||||
class LegQueryWhere extends QueryWhere {
|
||||
final NumericSqlExpressionBuilder<int> id =
|
||||
new NumericSqlExpressionBuilder<int>('id');
|
||||
|
||||
final StringSqlExpressionBuilder name =
|
||||
new StringSqlExpressionBuilder('name');
|
||||
|
||||
final DateTimeSqlExpressionBuilder createdAt =
|
||||
new DateTimeSqlExpressionBuilder('created_at');
|
||||
|
||||
final DateTimeSqlExpressionBuilder updatedAt =
|
||||
new DateTimeSqlExpressionBuilder('updated_at');
|
||||
|
||||
@override
|
||||
get expressionBuilders {
|
||||
return [id, name, createdAt, updatedAt];
|
||||
}
|
||||
}
|
||||
|
||||
class LegQueryValues extends MapQueryValues {
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
||||
void set id(int value) => values['id'] = value;
|
||||
String get name {
|
||||
return (values['name'] as String);
|
||||
}
|
||||
|
||||
void set name(String value) => values['name'] = value;
|
||||
DateTime get createdAt {
|
||||
return (values['created_at'] as DateTime);
|
||||
}
|
||||
|
||||
void set createdAt(DateTime value) => values['created_at'] = value;
|
||||
DateTime get updatedAt {
|
||||
return (values['updated_at'] as DateTime);
|
||||
}
|
||||
|
||||
void set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Leg model) {
|
||||
values.addAll({
|
||||
'name': model.name,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
|
|
@ -20,7 +20,15 @@ class OrderQuery extends Query<Order, OrderQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return OrderFields.allFields;
|
||||
return const [
|
||||
'id',
|
||||
'customer_id',
|
||||
'employee_id',
|
||||
'order_date',
|
||||
'shipper_id',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -28,9 +36,8 @@ class OrderQuery extends Query<Order, OrderQueryWhere> {
|
|||
return new OrderQueryWhere();
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return new Order(
|
||||
static Order parseRow(List row) {
|
||||
var model = new Order(
|
||||
id: row[0].toString(),
|
||||
customerId: (row[1] as int),
|
||||
employeeId: (row[2] as int),
|
||||
|
@ -38,6 +45,12 @@ class OrderQuery extends Query<Order, OrderQueryWhere> {
|
|||
shipperId: (row[4] as int),
|
||||
createdAt: (row[5] as DateTime),
|
||||
updatedAt: (row[6] as DateTime));
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class RoleQuery extends Query<Role, RoleQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return RoleFields.allFields;
|
||||
return const ['id', 'name', 'created_at', 'updated_at'];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -28,13 +28,18 @@ class RoleQuery extends Query<Role, RoleQueryWhere> {
|
|||
return new RoleQueryWhere();
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return new Role(
|
||||
static Role parseRow(List row) {
|
||||
var model = new Role(
|
||||
id: row[0].toString(),
|
||||
name: (row[1] as String),
|
||||
createdAt: (row[2] as DateTime),
|
||||
updatedAt: (row[3] as DateTime));
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
deserialize(List row) {
|
||||
return parseRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,11 +155,14 @@ main() {
|
|||
test('insert', () async {
|
||||
var recalledAt = new DateTime.now();
|
||||
var query = new CarQuery();
|
||||
var now = new DateTime.now();
|
||||
query.values
|
||||
..make = 'Honda'
|
||||
..description = 'Hello'
|
||||
..familyFriendly = true
|
||||
..recalledAt = recalledAt;
|
||||
..recalledAt = recalledAt
|
||||
..createdAt = now
|
||||
..updatedAt = now;
|
||||
var car = await query.insert(connection);
|
||||
expect(car.id, isNotNull);
|
||||
expect(car.make, 'Honda');
|
||||
|
|
Loading…
Reference in a new issue