Got many to many
This commit is contained in:
parent
261013aa7e
commit
78cd1086c9
14 changed files with 419 additions and 180 deletions
|
@ -146,6 +146,11 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
'This instance does not support creating new WHERE clauses.');
|
'This instance does not support creating new WHERE clauses.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether this query can be compiled.
|
||||||
|
///
|
||||||
|
/// Used to prevent ambiguities in joins.
|
||||||
|
bool canCompile(Set<String> trampoline) => true;
|
||||||
|
|
||||||
/// Shorthand for calling [where].or with a new [Where] clause.
|
/// Shorthand for calling [where].or with a new [Where] clause.
|
||||||
void andWhere(void Function(Where) f) {
|
void andWhere(void Function(Where) f) {
|
||||||
var w = newWhereClause();
|
var w = newWhereClause();
|
||||||
|
@ -192,27 +197,64 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
_crossJoin = tableName;
|
_crossJoin = tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _joinAlias() => 'a${_joins.length}';
|
String _joinAlias(Set<String> trampoline) {
|
||||||
|
int i = _joins.length;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var a = 'a$i';
|
||||||
|
if (trampoline.add(a)) {
|
||||||
|
return a;
|
||||||
|
} else
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _compileJoin(tableName, Set<String> trampoline) {
|
String _compileJoin(tableName, Set<String> trampoline) {
|
||||||
if (tableName is String) return tableName;
|
if (tableName is String)
|
||||||
if (tableName is Query) {
|
return tableName;
|
||||||
|
else if (tableName is Query) {
|
||||||
var c = tableName.compile(trampoline);
|
var c = tableName.compile(trampoline);
|
||||||
if (c == null) return c;
|
if (c == null) return c;
|
||||||
return '($c)';
|
return '($c)';
|
||||||
}
|
} else {
|
||||||
throw ArgumentError.value(
|
throw ArgumentError.value(
|
||||||
tableName, 'tableName', 'must be a String or Query');
|
tableName, 'tableName', 'must be a String or Query');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _makeJoin(
|
||||||
|
tableName,
|
||||||
|
Set<String> trampoline,
|
||||||
|
JoinType type,
|
||||||
|
String localKey,
|
||||||
|
String foreignKey,
|
||||||
|
String op,
|
||||||
|
List<String> additionalFields) {
|
||||||
|
trampoline ??= Set();
|
||||||
|
|
||||||
|
// Pivot tables guard against ambiguous fields by excluding tables
|
||||||
|
// that have already been queried in this scope.
|
||||||
|
if (trampoline.contains(tableName) && trampoline.contains(this.tableName)) {
|
||||||
|
// ex. if we have {roles, role_users}, then don't join "roles" again.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var to = _compileJoin(tableName, trampoline);
|
||||||
|
if (to != null) {
|
||||||
|
_joins.add(new JoinBuilder(type, this, to, localKey, foreignKey,
|
||||||
|
op: op,
|
||||||
|
alias: _joinAlias(trampoline),
|
||||||
|
additionalFields: additionalFields));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute an `INNER JOIN` against another table.
|
/// Execute an `INNER JOIN` against another table.
|
||||||
void join(tableName, String localKey, String foreignKey,
|
void join(tableName, String localKey, String foreignKey,
|
||||||
{String op: '=',
|
{String op: '=',
|
||||||
List<String> additionalFields: const [],
|
List<String> additionalFields: const [],
|
||||||
Set<String> trampoline}) {
|
Set<String> trampoline}) {
|
||||||
_joins.add(new JoinBuilder(JoinType.inner, this,
|
_makeJoin(tableName, trampoline, JoinType.inner, localKey, foreignKey, op,
|
||||||
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
|
additionalFields);
|
||||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a `LEFT JOIN` against another table.
|
/// Execute a `LEFT JOIN` against another table.
|
||||||
|
@ -220,9 +262,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
{String op: '=',
|
{String op: '=',
|
||||||
List<String> additionalFields: const [],
|
List<String> additionalFields: const [],
|
||||||
Set<String> trampoline}) {
|
Set<String> trampoline}) {
|
||||||
_joins.add(new JoinBuilder(JoinType.left, this,
|
_makeJoin(tableName, trampoline, JoinType.left, localKey, foreignKey, op,
|
||||||
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
|
additionalFields);
|
||||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a `RIGHT JOIN` against another table.
|
/// Execute a `RIGHT JOIN` against another table.
|
||||||
|
@ -230,9 +271,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
{String op: '=',
|
{String op: '=',
|
||||||
List<String> additionalFields: const [],
|
List<String> additionalFields: const [],
|
||||||
Set<String> trampoline}) {
|
Set<String> trampoline}) {
|
||||||
_joins.add(new JoinBuilder(JoinType.right, this,
|
_makeJoin(tableName, trampoline, JoinType.right, localKey, foreignKey, op,
|
||||||
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
|
additionalFields);
|
||||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a `FULL OUTER JOIN` against another table.
|
/// Execute a `FULL OUTER JOIN` against another table.
|
||||||
|
@ -240,9 +280,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
{String op: '=',
|
{String op: '=',
|
||||||
List<String> additionalFields: const [],
|
List<String> additionalFields: const [],
|
||||||
Set<String> trampoline}) {
|
Set<String> trampoline}) {
|
||||||
_joins.add(new JoinBuilder(JoinType.full, this,
|
_makeJoin(tableName, trampoline, JoinType.full, localKey, foreignKey, op,
|
||||||
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
|
additionalFields);
|
||||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a `SELF JOIN`.
|
/// Execute a `SELF JOIN`.
|
||||||
|
@ -250,9 +289,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
{String op: '=',
|
{String op: '=',
|
||||||
List<String> additionalFields: const [],
|
List<String> additionalFields: const [],
|
||||||
Set<String> trampoline}) {
|
Set<String> trampoline}) {
|
||||||
_joins.add(new JoinBuilder(JoinType.self, this,
|
_makeJoin(tableName, trampoline, JoinType.self, localKey, foreignKey, op,
|
||||||
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
|
additionalFields);
|
||||||
op: op, alias: _joinAlias(), additionalFields: additionalFields));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -261,7 +299,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
String preamble,
|
String preamble,
|
||||||
bool withFields: true,
|
bool withFields: true,
|
||||||
String fromQuery}) {
|
String fromQuery}) {
|
||||||
if (!trampoline.add(tableName)) {
|
// One table MAY appear multiple times in a query.
|
||||||
|
if (!canCompile(trampoline)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +333,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
||||||
if (preamble == null) {
|
if (preamble == null) {
|
||||||
if (_crossJoin != null) b.write(' CROSS JOIN $_crossJoin');
|
if (_crossJoin != null) b.write(' CROSS JOIN $_crossJoin');
|
||||||
for (var join in _joins) {
|
for (var join in _joins) {
|
||||||
var c = join.compile();
|
var c = join.compile(trampoline);
|
||||||
if (c != null) b.write(' $c');
|
if (c != null) b.write(' $c');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -565,7 +604,7 @@ class JoinBuilder {
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
||||||
String compile() {
|
String compile(Set<String> trampoline) {
|
||||||
if (to == null) return null;
|
if (to == null) return null;
|
||||||
var b = new StringBuffer();
|
var b = new StringBuffer();
|
||||||
var left = '${from.tableName}.$key';
|
var left = '${from.tableName}.$key';
|
||||||
|
|
|
@ -60,16 +60,16 @@ class BelongsTo extends Relationship {
|
||||||
const BelongsTo belongsTo = const BelongsTo();
|
const BelongsTo belongsTo = const BelongsTo();
|
||||||
|
|
||||||
class ManyToMany extends Relationship {
|
class ManyToMany extends Relationship {
|
||||||
const ManyToMany(
|
final Type through;
|
||||||
|
|
||||||
|
const ManyToMany(this.through,
|
||||||
{String localKey: 'id',
|
{String localKey: 'id',
|
||||||
String foreignKey,
|
String foreignKey,
|
||||||
String foreignTable,
|
String foreignTable,
|
||||||
bool cascadeOnDelete: false})
|
bool cascadeOnDelete: false})
|
||||||
: super(RelationshipType.manyToMany,
|
: super(RelationshipType.hasMany, // Many-to-Many is actually just a hasMany
|
||||||
localKey: localKey,
|
localKey: localKey,
|
||||||
foreignKey: foreignKey,
|
foreignKey: foreignKey,
|
||||||
foreignTable: foreignTable,
|
foreignTable: foreignTable,
|
||||||
cascadeOnDelete: cascadeOnDelete == true);
|
cascadeOnDelete: cascadeOnDelete == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ManyToMany manyToMany = const ManyToMany();
|
|
||||||
|
|
|
@ -105,7 +105,8 @@ Future<OrmBuildContext> buildOrmContext(
|
||||||
var foreignKey = cr.peek('foreignKey')?.stringValue;
|
var foreignKey = cr.peek('foreignKey')?.stringValue;
|
||||||
var foreignTable = cr.peek('foreignTable')?.stringValue;
|
var foreignTable = cr.peek('foreignTable')?.stringValue;
|
||||||
var cascadeOnDelete = cr.peek('cascadeOnDelete')?.boolValue == true;
|
var cascadeOnDelete = cr.peek('cascadeOnDelete')?.boolValue == true;
|
||||||
OrmBuildContext foreign;
|
var through = cr.peek('through')?.typeValue;
|
||||||
|
OrmBuildContext foreign, throughContext;
|
||||||
|
|
||||||
if (foreignTable == null) {
|
if (foreignTable == null) {
|
||||||
// if (!isModelClass(field.type) &&
|
// if (!isModelClass(field.type) &&
|
||||||
|
@ -137,6 +138,17 @@ Future<OrmBuildContext> buildOrmContext(
|
||||||
resolver,
|
resolver,
|
||||||
autoSnakeCaseNames);
|
autoSnakeCaseNames);
|
||||||
|
|
||||||
|
// Resolve throughType as well
|
||||||
|
if (through != null && through is InterfaceType) {
|
||||||
|
throughContext = await buildOrmContext(
|
||||||
|
through.element,
|
||||||
|
new ConstantReader(const TypeChecker.fromRuntime(Serializable)
|
||||||
|
.firstAnnotationOf(modelType.element)),
|
||||||
|
buildStep,
|
||||||
|
resolver,
|
||||||
|
autoSnakeCaseNames);
|
||||||
|
}
|
||||||
|
|
||||||
var ormAnn = const TypeChecker.fromRuntime(Orm)
|
var ormAnn = const TypeChecker.fromRuntime(Orm)
|
||||||
.firstAnnotationOf(modelType.element);
|
.firstAnnotationOf(modelType.element);
|
||||||
|
|
||||||
|
@ -164,12 +176,15 @@ Future<OrmBuildContext> buildOrmContext(
|
||||||
foreignKey ??= 'id';
|
foreignKey ??= 'id';
|
||||||
}
|
}
|
||||||
|
|
||||||
var relation = new Relationship(
|
var relation = new RelationshipReader(
|
||||||
type,
|
type,
|
||||||
localKey: localKey,
|
localKey: localKey,
|
||||||
foreignKey: foreignKey,
|
foreignKey: foreignKey,
|
||||||
foreignTable: foreignTable,
|
foreignTable: foreignTable,
|
||||||
cascadeOnDelete: cascadeOnDelete,
|
cascadeOnDelete: cascadeOnDelete,
|
||||||
|
through: through,
|
||||||
|
foreign: foreign,
|
||||||
|
throughContext: throughContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (relation.type == RelationshipType.belongsTo) {
|
if (relation.type == RelationshipType.belongsTo) {
|
||||||
|
@ -189,7 +204,6 @@ Future<OrmBuildContext> buildOrmContext(
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.relations[field.name] = relation;
|
ctx.relations[field.name] = relation;
|
||||||
ctx.relationTypes[relation] = foreign;
|
|
||||||
} else {
|
} else {
|
||||||
if (column?.type == null)
|
if (column?.type == null)
|
||||||
throw 'Cannot infer SQL column type for field "${ctx.buildContext.originalClassName}.${field.name}" with type "${field.type.displayName}".';
|
throw 'Cannot infer SQL column type for field "${ctx.buildContext.originalClassName}.${field.name}" with type "${field.type.displayName}".';
|
||||||
|
@ -256,8 +270,7 @@ class OrmBuildContext {
|
||||||
|
|
||||||
final Map<String, Column> columns = {};
|
final Map<String, Column> columns = {};
|
||||||
final List<FieldElement> effectiveFields = [];
|
final List<FieldElement> effectiveFields = [];
|
||||||
final Map<String, Relationship> relations = {};
|
final Map<String, RelationshipReader> relations = {};
|
||||||
final Map<Relationship, OrmBuildContext> relationTypes = {};
|
|
||||||
|
|
||||||
OrmBuildContext(this.buildContext, this.ormAnnotation, this.tableName);
|
OrmBuildContext(this.buildContext, this.ormAnnotation, this.tableName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,7 +211,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
||||||
RelationshipType.belongsTo,
|
RelationshipType.belongsTo,
|
||||||
RelationshipType.hasMany
|
RelationshipType.hasMany
|
||||||
].contains(relation.type)) return;
|
].contains(relation.type)) return;
|
||||||
var foreign = ctx.relationTypes[relation];
|
var foreign = relation.foreign;
|
||||||
var skipToList = refer('row')
|
var skipToList = refer('row')
|
||||||
.property('skip')
|
.property('skip')
|
||||||
.call([literalNum(i)])
|
.call([literalNum(i)])
|
||||||
|
@ -234,7 +234,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
||||||
var blockStr = block.accept(new DartEmitter());
|
var blockStr = block.accept(new DartEmitter());
|
||||||
var ifStr = 'if (row.length > $i) { $blockStr }';
|
var ifStr = 'if (row.length > $i) { $blockStr }';
|
||||||
b.statements.add(new Code(ifStr));
|
b.statements.add(new Code(ifStr));
|
||||||
i += ctx.relationTypes[relation].effectiveFields.length;
|
i += relation.foreign.effectiveFields.length;
|
||||||
});
|
});
|
||||||
|
|
||||||
b.addExpression(refer('model').returned);
|
b.addExpression(refer('model').returned);
|
||||||
|
@ -279,11 +279,14 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
||||||
if (relation.type == RelationshipType.belongsTo ||
|
if (relation.type == RelationshipType.belongsTo ||
|
||||||
relation.type == RelationshipType.hasOne ||
|
relation.type == RelationshipType.hasOne ||
|
||||||
relation.type == RelationshipType.hasMany) {
|
relation.type == RelationshipType.hasMany) {
|
||||||
var foreign = ctx.relationTypes[relation];
|
var foreign = relation.throughContext ?? relation.foreign;
|
||||||
var additionalFields = foreign.effectiveFields
|
|
||||||
|
// If this is a many-to-many, add the fields from the other object.
|
||||||
|
var additionalFields = relation.foreign.effectiveFields
|
||||||
// .where((f) => f.name != 'id' || !isSpecialId(ctx, f))
|
// .where((f) => f.name != 'id' || !isSpecialId(ctx, f))
|
||||||
.map((f) => literalString(
|
.map((f) => literalString(relation.foreign.buildContext
|
||||||
foreign.buildContext.resolveFieldName(f.name)));
|
.resolveFieldName(f.name)));
|
||||||
|
|
||||||
var joinArgs = [relation.localKey, relation.foreignKey]
|
var joinArgs = [relation.localKey, relation.foreignKey]
|
||||||
.map(literalString)
|
.map(literalString)
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -303,13 +306,43 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
||||||
|
|
||||||
b.addExpression(refer('leftJoin').call(joinArgs, {
|
b.addExpression(refer('leftJoin').call(joinArgs, {
|
||||||
'additionalFields':
|
'additionalFields':
|
||||||
literalConstList(additionalFields.toList())
|
literalConstList(additionalFields.toList()),
|
||||||
|
'trampoline': refer('trampoline'),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// If we have any many-to-many relations, we need to prevent
|
||||||
|
// fetching this table within their joins.
|
||||||
|
var manyToMany = ctx.relations.entries.where((e) => e.value.isManyToMany);
|
||||||
|
|
||||||
|
if (manyToMany.isNotEmpty) {
|
||||||
|
var outExprs = manyToMany.map<Expression>((e) {
|
||||||
|
var foreignTableName = e.value.throughContext.tableName;
|
||||||
|
return CodeExpression(Code('''
|
||||||
|
(!(
|
||||||
|
trampoline.contains('${ctx.tableName}')
|
||||||
|
&& trampoline.contains('$foreignTableName')
|
||||||
|
))
|
||||||
|
'''));
|
||||||
|
});
|
||||||
|
var out = outExprs.reduce((a, b) => a.and(b));
|
||||||
|
|
||||||
|
clazz.methods.add(new Method((b) {
|
||||||
|
b
|
||||||
|
..name = 'canCompile'
|
||||||
|
..annotations.add(refer('override'))
|
||||||
|
..requiredParameters
|
||||||
|
.add(new Parameter((b) => b..name = 'trampoline'))
|
||||||
|
..returns = refer('bool')
|
||||||
|
..body = Block((b) {
|
||||||
|
b.addExpression(out.returned);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Ultimately remove the insert override
|
// TODO: Ultimately remove the insert override
|
||||||
if (false && ctx.relations.isNotEmpty) {
|
if (false && ctx.relations.isNotEmpty) {
|
||||||
clazz.methods.add(new Method((b) {
|
clazz.methods.add(new Method((b) {
|
||||||
|
@ -391,10 +424,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
||||||
var args = <String, Expression>{};
|
var args = <String, Expression>{};
|
||||||
|
|
||||||
ctx.relations.forEach((name, relation) {
|
ctx.relations.forEach((name, relation) {
|
||||||
if (false && relation.type == RelationshipType.hasMany) {
|
// TODO: Should this be entirely removed?
|
||||||
|
if (relation.type == RelationshipType.hasMany) {
|
||||||
// For each hasMany, we need to create a query of
|
// For each hasMany, we need to create a query of
|
||||||
// the corresponding type.
|
// the corresponding type.
|
||||||
var foreign = ctx.relationTypes[relation];
|
var foreign = relation.foreign;
|
||||||
var queryType = refer(
|
var queryType = refer(
|
||||||
'${foreign.buildContext.modelClassNameRecase.pascalCase}Query');
|
'${foreign.buildContext.modelClassNameRecase.pascalCase}Query');
|
||||||
var queryInstance = queryType.newInstance([]);
|
var queryInstance = queryType.newInstance([]);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:analyzer/dart/constant/value.dart';
|
import 'package:analyzer/dart/constant/value.dart';
|
||||||
|
import 'package:analyzer/dart/element/type.dart';
|
||||||
import 'package:angel_orm/angel_orm.dart';
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
import 'package:source_gen/source_gen.dart';
|
import 'package:source_gen/source_gen.dart';
|
||||||
|
import 'orm_build_context.dart';
|
||||||
|
|
||||||
const TypeChecker columnTypeChecker = const TypeChecker.fromRuntime(Column);
|
const TypeChecker columnTypeChecker = const TypeChecker.fromRuntime(Column);
|
||||||
|
|
||||||
|
@ -19,3 +21,26 @@ class ColumnReader {
|
||||||
|
|
||||||
DartObject get defaultValue => reader.peek('defaultValue')?.objectValue;
|
DartObject get defaultValue => reader.peek('defaultValue')?.objectValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RelationshipReader {
|
||||||
|
final int type;
|
||||||
|
final String localKey;
|
||||||
|
final String foreignKey;
|
||||||
|
final String foreignTable;
|
||||||
|
final bool cascadeOnDelete;
|
||||||
|
final DartType through;
|
||||||
|
final OrmBuildContext foreign;
|
||||||
|
final OrmBuildContext throughContext;
|
||||||
|
|
||||||
|
const RelationshipReader(this.type,
|
||||||
|
{this.localKey,
|
||||||
|
this.foreignKey,
|
||||||
|
this.foreignTable,
|
||||||
|
this.cascadeOnDelete,
|
||||||
|
this.through,
|
||||||
|
this.foreign,
|
||||||
|
this.throughContext});
|
||||||
|
|
||||||
|
bool get isManyToMany =>
|
||||||
|
type == RelationshipType.hasMany && throughContext != null;
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,6 @@ dev_dependencies:
|
||||||
collection: ^1.0.0
|
collection: ^1.0.0
|
||||||
postgres: ^1.0.0
|
postgres: ^1.0.0
|
||||||
test: ^1.0.0
|
test: ^1.0.0
|
||||||
# dependency_overrides:
|
dependency_overrides:
|
||||||
# angel_orm:
|
angel_orm:
|
||||||
# path: ../angel_orm
|
path: ../angel_orm
|
||||||
|
|
|
@ -25,7 +25,7 @@ class PostgresExecutor extends QueryExecutor {
|
||||||
@override
|
@override
|
||||||
Future<List<List>> query(
|
Future<List<List>> query(
|
||||||
String tableName, String query, Map<String, dynamic> substitutionValues,
|
String tableName, String query, Map<String, dynamic> substitutionValues,
|
||||||
[List<String> returningFields]) {
|
[List<String> returningFields]) async {
|
||||||
if (returningFields != null) {
|
if (returningFields != null) {
|
||||||
var fields = returningFields.join(', ');
|
var fields = returningFields.join(', ');
|
||||||
var returning = 'RETURNING $fields';
|
var returning = 'RETURNING $fields';
|
||||||
|
@ -37,7 +37,16 @@ class PostgresExecutor extends QueryExecutor {
|
||||||
if (substitutionValues.isNotEmpty) print('Values: $substitutionValues');
|
if (substitutionValues.isNotEmpty) print('Values: $substitutionValues');
|
||||||
print(substitutionValues.map((k, v) => MapEntry(k, v.runtimeType)));
|
print(substitutionValues.map((k, v) => MapEntry(k, v.runtimeType)));
|
||||||
}
|
}
|
||||||
return connection.query(query, substitutionValues: substitutionValues);
|
|
||||||
|
var rows =
|
||||||
|
await connection.query(query, substitutionValues: substitutionValues);
|
||||||
|
|
||||||
|
if (!Platform.environment.containsKey('STFU')) {
|
||||||
|
print('Got ${rows.length} row(s):');
|
||||||
|
rows.forEach(print);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,45 +1,86 @@
|
||||||
library angel_orm_generator.test;
|
library angel_orm_generator.test;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:angel_orm/angel_orm.dart';
|
import 'dart:io';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'models/user.dart';
|
import 'models/user.dart';
|
||||||
import 'common.dart';
|
import 'common.dart';
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
QueryExecutor executor;
|
PostgresExecutor executor;
|
||||||
Role canPub, canSub;
|
Role canPub, canSub;
|
||||||
User thosakwe;
|
User thosakwe;
|
||||||
|
|
||||||
|
Future<void> dumpQuery(String query) async {
|
||||||
|
if (Platform.environment.containsKey('STFU')) return;
|
||||||
|
print('\n');
|
||||||
|
print('==================================================');
|
||||||
|
print(' DUMPING QUERY');
|
||||||
|
print(query);
|
||||||
|
var rows = await executor.connection.query(query);
|
||||||
|
print('\n${rows.length} row(s):');
|
||||||
|
rows.forEach((r) => print(' * $r'));
|
||||||
|
print('==================================================\n\n');
|
||||||
|
}
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
executor = await connectToPostgres(['user', 'role', 'user_role']);
|
executor = await connectToPostgres(['user', 'role', 'user_role']);
|
||||||
|
|
||||||
var canPubQuery = new RoleQuery()..values.name = 'can_pub';
|
// await dumpQuery("""
|
||||||
var canSubQuery = new RoleQuery()..values.name = 'can_sub';
|
// WITH roles as
|
||||||
canPub = await canPubQuery.insert(executor);
|
// (INSERT INTO roles (name)
|
||||||
canSub = await canSubQuery.insert(executor);
|
// VALUES ('pyt')
|
||||||
|
// RETURNING roles.id, roles.name, roles.created_at, roles.updated_at)
|
||||||
|
// SELECT
|
||||||
|
// roles.id, roles.name, roles.created_at, roles.updated_at
|
||||||
|
// FROM roles
|
||||||
|
// LEFT JOIN
|
||||||
|
// (SELECT
|
||||||
|
// role_users.role_id, role_users.user_id,
|
||||||
|
// a0.id, a0.username, a0.password, a0.email, a0.created_at, a0.updated_at
|
||||||
|
// FROM role_users
|
||||||
|
// LEFT JOIN
|
||||||
|
// users a0 ON role_users.user_id=a0.id)
|
||||||
|
// a1 ON roles.id=a1.role_id
|
||||||
|
// """);
|
||||||
|
|
||||||
var thosakweQuery = new UserQuery();
|
var canPubQuery = RoleQuery()..values.name = 'can_pub';
|
||||||
|
var canSubQuery = RoleQuery()..values.name = 'can_sub';
|
||||||
|
canPub = await canPubQuery.insert(executor);
|
||||||
|
print('=== CANPUB: ${canPub?.toJson()}');
|
||||||
|
// await dumpQuery(canPubQuery.compile(Set()));
|
||||||
|
canSub = await canSubQuery.insert(executor);
|
||||||
|
print('=== CANSUB: ${canSub?.toJson()}');
|
||||||
|
|
||||||
|
var thosakweQuery = UserQuery();
|
||||||
thosakweQuery.values
|
thosakweQuery.values
|
||||||
..username = 'thosakwe'
|
..username = 'thosakwe'
|
||||||
..password = 'Hahahahayoureallythoughtiwasstupidenoughtotypethishere'
|
..password = 'Hahahahayoureallythoughtiwasstupidenoughtotypethishere'
|
||||||
..email = 'thosakwe AT gmail.com';
|
..email = 'thosakwe AT gmail.com';
|
||||||
thosakwe = await thosakweQuery.insert(executor);
|
thosakwe = await thosakweQuery.insert(executor);
|
||||||
|
print('=== THOSAKWE: ${thosakwe?.toJson()}');
|
||||||
|
|
||||||
// Allow thosakwe to publish...
|
// Allow thosakwe to publish...
|
||||||
var thosakwePubQuery = new RoleUserQuery();
|
var thosakwePubQuery = RoleUserQuery();
|
||||||
thosakwePubQuery.values
|
thosakwePubQuery.values
|
||||||
..userId = int.parse(thosakwe.id)
|
..userId = int.parse(thosakwe.id)
|
||||||
..roleId = int.parse(canPub.id);
|
..roleId = int.parse(canPub.id);
|
||||||
await thosakwePubQuery.insert(executor);
|
await thosakwePubQuery.insert(executor);
|
||||||
|
|
||||||
// Allow thosakwe to subscribe...
|
// Allow thosakwe to subscribe...
|
||||||
var thosakweSubQuery = new RoleUserQuery();
|
var thosakweSubQuery = RoleUserQuery();
|
||||||
thosakweSubQuery.values
|
thosakweSubQuery.values
|
||||||
..userId = int.parse(thosakwe.id)
|
..userId = int.parse(thosakwe.id)
|
||||||
..roleId = int.parse(canSub.id);
|
..roleId = int.parse(canSub.id);
|
||||||
await thosakweSubQuery.insert(executor);
|
await thosakweSubQuery.insert(executor);
|
||||||
|
|
||||||
|
// Print all users...
|
||||||
|
// await dumpQuery('select * from users;');
|
||||||
|
// await dumpQuery('select * from roles;');
|
||||||
|
// await dumpQuery('select * from role_users;');
|
||||||
|
var query = RoleQuery()..where.id.equals(canPub.idAsInt);
|
||||||
|
await dumpQuery(query.compile(Set()));
|
||||||
|
|
||||||
print('\n');
|
print('\n');
|
||||||
print('==================================================');
|
print('==================================================');
|
||||||
print(' GOOD STUFF BEGINS HERE ');
|
print(' GOOD STUFF BEGINS HERE ');
|
||||||
|
@ -47,7 +88,7 @@ main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<User> fetchThosakwe() async {
|
Future<User> fetchThosakwe() async {
|
||||||
var query = new UserQuery()..where.id.equals(int.parse(thosakwe.id));
|
var query = UserQuery()..where.id.equals(int.parse(thosakwe.id));
|
||||||
return await query.getOne(executor);
|
return await query.getOne(executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,9 +101,9 @@ main() {
|
||||||
|
|
||||||
test('fetch users for role', () async {
|
test('fetch users for role', () async {
|
||||||
for (var role in [canPub, canSub]) {
|
for (var role in [canPub, canSub]) {
|
||||||
var query = new RoleQuery()..where.id.equals(int.parse(role.id));
|
var query = RoleQuery()..where.id.equals(role.idAsInt);
|
||||||
var r = await query.getOne(executor);
|
var r = await query.getOne(executor);
|
||||||
expect(r.users, [thosakwe]);
|
expect(r.users.toList(), [thosakwe]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,11 @@ class BookQuery extends Query<Book, BookQueryWhere> {
|
||||||
trampoline.add(tableName);
|
trampoline.add(tableName);
|
||||||
_where = BookQueryWhere(this);
|
_where = BookQueryWhere(this);
|
||||||
leftJoin('authors', 'author_id', 'id',
|
leftJoin('authors', 'author_id', 'id',
|
||||||
additionalFields: const ['id', 'name', 'created_at', 'updated_at']);
|
additionalFields: const ['id', 'name', 'created_at', 'updated_at'],
|
||||||
|
trampoline: trampoline);
|
||||||
leftJoin('authors', 'partner_author_id', 'id',
|
leftJoin('authors', 'partner_author_id', 'id',
|
||||||
additionalFields: const ['id', 'name', 'created_at', 'updated_at']);
|
additionalFields: const ['id', 'name', 'created_at', 'updated_at'],
|
||||||
|
trampoline: trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -32,13 +32,15 @@ class LegQuery extends Query<Leg, LegQueryWhere> {
|
||||||
trampoline ??= Set();
|
trampoline ??= Set();
|
||||||
trampoline.add(tableName);
|
trampoline.add(tableName);
|
||||||
_where = LegQueryWhere(this);
|
_where = LegQueryWhere(this);
|
||||||
leftJoin('feet', 'id', 'leg_id', additionalFields: const [
|
leftJoin('feet', 'id', 'leg_id',
|
||||||
|
additionalFields: const [
|
||||||
'id',
|
'id',
|
||||||
'leg_id',
|
'leg_id',
|
||||||
'n_toes',
|
'n_toes',
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at'
|
'updated_at'
|
||||||
]);
|
],
|
||||||
|
trampoline: trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -36,7 +36,8 @@ class OrderQuery extends Query<Order, OrderQueryWhere> {
|
||||||
trampoline.add(tableName);
|
trampoline.add(tableName);
|
||||||
_where = OrderQueryWhere(this);
|
_where = OrderQueryWhere(this);
|
||||||
leftJoin('customers', 'customer_id', 'id',
|
leftJoin('customers', 'customer_id', 'id',
|
||||||
additionalFields: const ['id', 'created_at', 'updated_at']);
|
additionalFields: const ['id', 'created_at', 'updated_at'],
|
||||||
|
trampoline: trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -39,7 +39,8 @@ class TreeQuery extends Query<Tree, TreeQueryWhere> {
|
||||||
'common_name',
|
'common_name',
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at'
|
'updated_at'
|
||||||
]);
|
],
|
||||||
|
trampoline: trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -14,13 +14,13 @@ abstract class _User extends Model {
|
||||||
String get password;
|
String get password;
|
||||||
String get email;
|
String get email;
|
||||||
|
|
||||||
@manyToMany
|
@ManyToMany(_RoleUser)
|
||||||
List<_Role> get roles;
|
List<_Role> get roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@serializable
|
@serializable
|
||||||
@orm
|
@orm
|
||||||
abstract class _RoleUser extends Model {
|
abstract class _RoleUser {
|
||||||
@belongsTo
|
@belongsTo
|
||||||
_Role get role;
|
_Role get role;
|
||||||
|
|
||||||
|
@ -33,6 +33,6 @@ abstract class _RoleUser extends Model {
|
||||||
abstract class _Role extends Model {
|
abstract class _Role extends Model {
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
@manyToMany
|
@ManyToMany(_RoleUser)
|
||||||
List<_User> get users;
|
List<_User> get users;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,6 @@ class RoleUserMigration extends Migration {
|
||||||
@override
|
@override
|
||||||
up(Schema schema) {
|
up(Schema schema) {
|
||||||
schema.create('role_users', (table) {
|
schema.create('role_users', (table) {
|
||||||
table.serial('id')..primaryKey();
|
|
||||||
table.timeStamp('created_at');
|
|
||||||
table.timeStamp('updated_at');
|
|
||||||
table.integer('role_id').references('roles', 'id');
|
table.integer('role_id').references('roles', 'id');
|
||||||
table.integer('user_id').references('users', 'id');
|
table.integer('user_id').references('users', 'id');
|
||||||
});
|
});
|
||||||
|
@ -69,6 +66,9 @@ class UserQuery extends Query<User, UserQueryWhere> {
|
||||||
trampoline ??= Set();
|
trampoline ??= Set();
|
||||||
trampoline.add(tableName);
|
trampoline.add(tableName);
|
||||||
_where = UserQueryWhere(this);
|
_where = UserQueryWhere(this);
|
||||||
|
leftJoin(RoleUserQuery(trampoline: trampoline), 'id', 'user_id',
|
||||||
|
additionalFields: const ['id', 'name', 'created_at', 'updated_at'],
|
||||||
|
trampoline: trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -117,6 +117,12 @@ class UserQuery extends Query<User, UserQueryWhere> {
|
||||||
email: (row[3] as String),
|
email: (row[3] as String),
|
||||||
createdAt: (row[4] as DateTime),
|
createdAt: (row[4] as DateTime),
|
||||||
updatedAt: (row[5] as DateTime));
|
updatedAt: (row[5] as DateTime));
|
||||||
|
if (row.length > 6) {
|
||||||
|
model = model.copyWith(
|
||||||
|
roles: [RoleQuery.parseRow(row.skip(6).toList())]
|
||||||
|
.where((x) => x != null)
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +130,69 @@ class UserQuery extends Query<User, UserQueryWhere> {
|
||||||
deserialize(List row) {
|
deserialize(List row) {
|
||||||
return parseRow(row);
|
return parseRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool canCompile(trampoline) {
|
||||||
|
return (!(trampoline.contains('users') &&
|
||||||
|
trampoline.contains('role_users')));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
get(QueryExecutor executor) {
|
||||||
|
return super.get(executor).then((result) {
|
||||||
|
return result.fold<List<User>>([], (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(
|
||||||
|
roles: List<_Role>.from(l.roles ?? [])
|
||||||
|
..addAll(model.roles ?? []));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
update(QueryExecutor executor) {
|
||||||
|
return super.update(executor).then((result) {
|
||||||
|
return result.fold<List<User>>([], (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(
|
||||||
|
roles: List<_Role>.from(l.roles ?? [])
|
||||||
|
..addAll(model.roles ?? []));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
delete(QueryExecutor executor) {
|
||||||
|
return super.delete(executor).then((result) {
|
||||||
|
return result.fold<List<User>>([], (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(
|
||||||
|
roles: List<_Role>.from(l.roles ?? [])
|
||||||
|
..addAll(model.roles ?? []));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserQueryWhere extends QueryWhere {
|
class UserQueryWhere extends QueryWhere {
|
||||||
|
@ -204,15 +273,18 @@ class RoleUserQuery extends Query<RoleUser, RoleUserQueryWhere> {
|
||||||
trampoline.add(tableName);
|
trampoline.add(tableName);
|
||||||
_where = RoleUserQueryWhere(this);
|
_where = RoleUserQueryWhere(this);
|
||||||
leftJoin('roles', 'role_id', 'id',
|
leftJoin('roles', 'role_id', 'id',
|
||||||
additionalFields: const ['id', 'name', 'created_at', 'updated_at']);
|
additionalFields: const ['id', 'name', 'created_at', 'updated_at'],
|
||||||
leftJoin('users', 'user_id', 'id', additionalFields: const [
|
trampoline: trampoline);
|
||||||
|
leftJoin('users', 'user_id', 'id',
|
||||||
|
additionalFields: const [
|
||||||
'id',
|
'id',
|
||||||
'username',
|
'username',
|
||||||
'password',
|
'password',
|
||||||
'email',
|
'email',
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at'
|
'updated_at'
|
||||||
]);
|
],
|
||||||
|
trampoline: trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -232,7 +304,7 @@ class RoleUserQuery extends Query<RoleUser, RoleUserQueryWhere> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
get fields {
|
get fields {
|
||||||
return const ['id', 'role_id', 'user_id', 'created_at', 'updated_at'];
|
return const ['role_id', 'user_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -247,15 +319,12 @@ class RoleUserQuery extends Query<RoleUser, RoleUserQueryWhere> {
|
||||||
|
|
||||||
static RoleUser parseRow(List row) {
|
static RoleUser parseRow(List row) {
|
||||||
if (row.every((x) => x == null)) return null;
|
if (row.every((x) => x == null)) return null;
|
||||||
var model = RoleUser(
|
var model = RoleUser();
|
||||||
id: row[0].toString(),
|
if (row.length > 2) {
|
||||||
createdAt: (row[3] as DateTime),
|
model = model.copyWith(role: RoleQuery.parseRow(row.skip(2).toList()));
|
||||||
updatedAt: (row[4] as DateTime));
|
|
||||||
if (row.length > 5) {
|
|
||||||
model = model.copyWith(role: RoleQuery.parseRow(row.skip(5).toList()));
|
|
||||||
}
|
}
|
||||||
if (row.length > 9) {
|
if (row.length > 6) {
|
||||||
model = model.copyWith(user: UserQuery.parseRow(row.skip(9).toList()));
|
model = model.copyWith(user: UserQuery.parseRow(row.skip(6).toList()));
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -268,25 +337,16 @@ class RoleUserQuery extends Query<RoleUser, RoleUserQueryWhere> {
|
||||||
|
|
||||||
class RoleUserQueryWhere extends QueryWhere {
|
class RoleUserQueryWhere extends QueryWhere {
|
||||||
RoleUserQueryWhere(RoleUserQuery query)
|
RoleUserQueryWhere(RoleUserQuery query)
|
||||||
: id = NumericSqlExpressionBuilder<int>(query, 'id'),
|
: roleId = NumericSqlExpressionBuilder<int>(query, 'role_id'),
|
||||||
roleId = NumericSqlExpressionBuilder<int>(query, 'role_id'),
|
userId = NumericSqlExpressionBuilder<int>(query, 'user_id');
|
||||||
userId = NumericSqlExpressionBuilder<int>(query, 'user_id'),
|
|
||||||
createdAt = DateTimeSqlExpressionBuilder(query, 'created_at'),
|
|
||||||
updatedAt = DateTimeSqlExpressionBuilder(query, 'updated_at');
|
|
||||||
|
|
||||||
final NumericSqlExpressionBuilder<int> id;
|
|
||||||
|
|
||||||
final NumericSqlExpressionBuilder<int> roleId;
|
final NumericSqlExpressionBuilder<int> roleId;
|
||||||
|
|
||||||
final NumericSqlExpressionBuilder<int> userId;
|
final NumericSqlExpressionBuilder<int> userId;
|
||||||
|
|
||||||
final DateTimeSqlExpressionBuilder createdAt;
|
|
||||||
|
|
||||||
final DateTimeSqlExpressionBuilder updatedAt;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
get expressionBuilders {
|
get expressionBuilders {
|
||||||
return [id, roleId, userId, createdAt, updatedAt];
|
return [roleId, userId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,11 +356,6 @@ class RoleUserQueryValues extends MapQueryValues {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int get id {
|
|
||||||
return (values['id'] as int);
|
|
||||||
}
|
|
||||||
|
|
||||||
set id(int value) => values['id'] = value;
|
|
||||||
int get roleId {
|
int get roleId {
|
||||||
return (values['role_id'] as int);
|
return (values['role_id'] as int);
|
||||||
}
|
}
|
||||||
|
@ -311,19 +366,7 @@ class RoleUserQueryValues extends MapQueryValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
set userId(int value) => values['user_id'] = value;
|
set userId(int value) => values['user_id'] = 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(RoleUser model) {
|
void copyFrom(RoleUser model) {
|
||||||
createdAt = model.createdAt;
|
|
||||||
updatedAt = model.updatedAt;
|
|
||||||
if (model.role != null) {
|
if (model.role != null) {
|
||||||
values['role_id'] = int.parse(model.role.id);
|
values['role_id'] = int.parse(model.role.id);
|
||||||
}
|
}
|
||||||
|
@ -338,6 +381,16 @@ class RoleQuery extends Query<Role, RoleQueryWhere> {
|
||||||
trampoline ??= Set();
|
trampoline ??= Set();
|
||||||
trampoline.add(tableName);
|
trampoline.add(tableName);
|
||||||
_where = RoleQueryWhere(this);
|
_where = RoleQueryWhere(this);
|
||||||
|
leftJoin(RoleUserQuery(trampoline: trampoline), 'id', 'role_id',
|
||||||
|
additionalFields: const [
|
||||||
|
'id',
|
||||||
|
'username',
|
||||||
|
'password',
|
||||||
|
'email',
|
||||||
|
'created_at',
|
||||||
|
'updated_at'
|
||||||
|
],
|
||||||
|
trampoline: trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -377,6 +430,12 @@ class RoleQuery extends Query<Role, RoleQueryWhere> {
|
||||||
name: (row[1] as String),
|
name: (row[1] as String),
|
||||||
createdAt: (row[2] as DateTime),
|
createdAt: (row[2] as DateTime),
|
||||||
updatedAt: (row[3] as DateTime));
|
updatedAt: (row[3] as DateTime));
|
||||||
|
if (row.length > 4) {
|
||||||
|
model = model.copyWith(
|
||||||
|
users: [UserQuery.parseRow(row.skip(4).toList())]
|
||||||
|
.where((x) => x != null)
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,6 +443,69 @@ class RoleQuery extends Query<Role, RoleQueryWhere> {
|
||||||
deserialize(List row) {
|
deserialize(List row) {
|
||||||
return parseRow(row);
|
return parseRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool canCompile(trampoline) {
|
||||||
|
return (!(trampoline.contains('roles') &&
|
||||||
|
trampoline.contains('role_users')));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
get(QueryExecutor executor) {
|
||||||
|
return super.get(executor).then((result) {
|
||||||
|
return result.fold<List<Role>>([], (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(
|
||||||
|
users: List<_User>.from(l.users ?? [])
|
||||||
|
..addAll(model.users ?? []));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
update(QueryExecutor executor) {
|
||||||
|
return super.update(executor).then((result) {
|
||||||
|
return result.fold<List<Role>>([], (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(
|
||||||
|
users: List<_User>.from(l.users ?? [])
|
||||||
|
..addAll(model.users ?? []));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
delete(QueryExecutor executor) {
|
||||||
|
return super.delete(executor).then((result) {
|
||||||
|
return result.fold<List<Role>>([], (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(
|
||||||
|
users: List<_User>.from(l.users ?? [])
|
||||||
|
..addAll(model.users ?? []));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RoleQueryWhere extends QueryWhere {
|
class RoleQueryWhere extends QueryWhere {
|
||||||
|
@ -519,11 +641,8 @@ class User extends _User {
|
||||||
}
|
}
|
||||||
|
|
||||||
@generatedSerializable
|
@generatedSerializable
|
||||||
class RoleUser extends _RoleUser {
|
class RoleUser implements _RoleUser {
|
||||||
RoleUser({this.id, this.role, this.user, this.createdAt, this.updatedAt});
|
const RoleUser({this.role, this.user});
|
||||||
|
|
||||||
@override
|
|
||||||
final String id;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final _Role role;
|
final _Role role;
|
||||||
|
@ -531,38 +650,17 @@ class RoleUser extends _RoleUser {
|
||||||
@override
|
@override
|
||||||
final _User user;
|
final _User user;
|
||||||
|
|
||||||
@override
|
RoleUser copyWith({_Role role, _User user}) {
|
||||||
final DateTime createdAt;
|
return new RoleUser(role: role ?? this.role, user: user ?? this.user);
|
||||||
|
|
||||||
@override
|
|
||||||
final DateTime updatedAt;
|
|
||||||
|
|
||||||
RoleUser copyWith(
|
|
||||||
{String id,
|
|
||||||
_Role role,
|
|
||||||
_User user,
|
|
||||||
DateTime createdAt,
|
|
||||||
DateTime updatedAt}) {
|
|
||||||
return new RoleUser(
|
|
||||||
id: id ?? this.id,
|
|
||||||
role: role ?? this.role,
|
|
||||||
user: user ?? this.user,
|
|
||||||
createdAt: createdAt ?? this.createdAt,
|
|
||||||
updatedAt: updatedAt ?? this.updatedAt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator ==(other) {
|
bool operator ==(other) {
|
||||||
return other is _RoleUser &&
|
return other is _RoleUser && other.role == role && other.user == user;
|
||||||
other.id == id &&
|
|
||||||
other.role == role &&
|
|
||||||
other.user == user &&
|
|
||||||
other.createdAt == createdAt &&
|
|
||||||
other.updatedAt == updatedAt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return hashObjects([id, role, user, createdAt, updatedAt]);
|
return hashObjects([role, user]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -698,22 +796,11 @@ abstract class UserFields {
|
||||||
abstract class RoleUserSerializer {
|
abstract class RoleUserSerializer {
|
||||||
static RoleUser fromMap(Map map) {
|
static RoleUser fromMap(Map map) {
|
||||||
return new RoleUser(
|
return new RoleUser(
|
||||||
id: map['id'] as String,
|
|
||||||
role: map['role'] != null
|
role: map['role'] != null
|
||||||
? RoleSerializer.fromMap(map['role'] as Map)
|
? RoleSerializer.fromMap(map['role'] as Map)
|
||||||
: null,
|
: null,
|
||||||
user: map['user'] != null
|
user: map['user'] != null
|
||||||
? UserSerializer.fromMap(map['user'] as Map)
|
? UserSerializer.fromMap(map['user'] as Map)
|
||||||
: null,
|
|
||||||
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);
|
: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,33 +809,18 @@ abstract class RoleUserSerializer {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
'id': model.id,
|
|
||||||
'role': RoleSerializer.toMap(model.role),
|
'role': RoleSerializer.toMap(model.role),
|
||||||
'user': UserSerializer.toMap(model.user),
|
'user': UserSerializer.toMap(model.user)
|
||||||
'created_at': model.createdAt?.toIso8601String(),
|
|
||||||
'updated_at': model.updatedAt?.toIso8601String()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class RoleUserFields {
|
abstract class RoleUserFields {
|
||||||
static const List<String> allFields = <String>[
|
static const List<String> allFields = <String>[role, user];
|
||||||
id,
|
|
||||||
role,
|
|
||||||
user,
|
|
||||||
createdAt,
|
|
||||||
updatedAt
|
|
||||||
];
|
|
||||||
|
|
||||||
static const String id = 'id';
|
|
||||||
|
|
||||||
static const String role = 'role';
|
static const String role = 'role';
|
||||||
|
|
||||||
static const String user = 'user';
|
static const String user = 'user';
|
||||||
|
|
||||||
static const String createdAt = 'created_at';
|
|
||||||
|
|
||||||
static const String updatedAt = 'updated_at';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class RoleSerializer {
|
abstract class RoleSerializer {
|
||||||
|
|
Loading…
Reference in a new issue