Moor: Is there a way to implement a model class into moor table

Created on 2 Dec 2019  路  2Comments  路  Source: simolus3/moor

I'm creating a db implementation which is decoupled from ui modules and contact with the domain layer through models and abstract classes, I was able to implement an abstract class into moor's dao. Any way for me to implement this model into moor table?

class UserModel {
  final int localId;
  final int serverId;
  final String hostname;
  final String token;
  final int syncFreqInSeconds;
  final String syncPeriod;
  final bool darkThemeEnabled;
  final String language;

  UserModel({
    @required this.localId,
    @required this.serverId,
    @required this.hostname,
    @required this.token,
    this.syncFreqInSeconds,
    this.syncPeriod,
    @required this.darkThemeEnabled,
    this.language,
  });
}

Most helpful comment

Since moor generates classes from table declarations and not Dart models, doing this directly is impossible at the moment (see also #144).
To give more context, I'm a bit hesitated to work on features like that because moor isn't supposed to be an ORM. I see it more as a typesafe and convenient wrapper around sql that's still relational. If we were to support entities, we'd also have to support all kinds of references between them, which increases complexity of the generator. It could also be confusing for users, who'd have to decide between the Dart extends Table api, moor files, or generated tables from models.

I'd recommend to write an independent moor table with the same fields and then map between those:

@DataClassName('MoorUserModel')
class UserModels extends Table {
  IntColumn get localId => integer().primaryKey()();
  IntColumn get serverId => integer()();
  TextColumn get hostname => text()();
  // ...
}

Without leaking information between the domain and the data layer, you can then write mapping methods as extensions:

extension DomainToMoorExtension on UserModel {
  MoorUserModel get asMoor => MoorUserModel(
    localId: localId,
    serverId: serverId,
    hostname: hostname,
    // ...
  );
} 

extension MoorToDomainExtension on MoorUserModel {
  UserModel get asDomain => UserModel(
    localid: localId,
    serverId: serverId,
    hostname: hostname 
  );
}

Finally, you can then use the moor models in your dao

@UseDao(tables: [UserModels])
class MyDao extends DatabaseAccessor implements SomeDomainRepository {

  @override
  Future<UserModel> loadModelById(int id) => select(...).map((row) => row.asDomain).getSingle();

  @override
  Future<void> writeModel(UserModel model) {
    return into(userModels).insert(model.asMoor, mode: InsertMode.orReplace);
  }
}

If you're using companions for inserts, your might want to skip the domain -> moor extension and instead create a UserModelsCompanion.forInsert based on the domain model class.
I know that boilerplate is always boring, but this kind of mapping code helps to keep a clean separation between the data and domain layer. Further, the mapping code is still typesafe.

All 2 comments

Since moor generates classes from table declarations and not Dart models, doing this directly is impossible at the moment (see also #144).
To give more context, I'm a bit hesitated to work on features like that because moor isn't supposed to be an ORM. I see it more as a typesafe and convenient wrapper around sql that's still relational. If we were to support entities, we'd also have to support all kinds of references between them, which increases complexity of the generator. It could also be confusing for users, who'd have to decide between the Dart extends Table api, moor files, or generated tables from models.

I'd recommend to write an independent moor table with the same fields and then map between those:

@DataClassName('MoorUserModel')
class UserModels extends Table {
  IntColumn get localId => integer().primaryKey()();
  IntColumn get serverId => integer()();
  TextColumn get hostname => text()();
  // ...
}

Without leaking information between the domain and the data layer, you can then write mapping methods as extensions:

extension DomainToMoorExtension on UserModel {
  MoorUserModel get asMoor => MoorUserModel(
    localId: localId,
    serverId: serverId,
    hostname: hostname,
    // ...
  );
} 

extension MoorToDomainExtension on MoorUserModel {
  UserModel get asDomain => UserModel(
    localid: localId,
    serverId: serverId,
    hostname: hostname 
  );
}

Finally, you can then use the moor models in your dao

@UseDao(tables: [UserModels])
class MyDao extends DatabaseAccessor implements SomeDomainRepository {

  @override
  Future<UserModel> loadModelById(int id) => select(...).map((row) => row.asDomain).getSingle();

  @override
  Future<void> writeModel(UserModel model) {
    return into(userModels).insert(model.asMoor, mode: InsertMode.orReplace);
  }
}

If you're using companions for inserts, your might want to skip the domain -> moor extension and instead create a UserModelsCompanion.forInsert based on the domain model class.
I know that boilerplate is always boring, but this kind of mapping code helps to keep a clean separation between the data and domain layer. Further, the mapping code is still typesafe.

@simolus3 got it, thank you!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Ltei picture Ltei  路  3Comments

apoleo88 picture apoleo88  路  3Comments

KKRoko picture KKRoko  路  3Comments

cadaniel picture cadaniel  路  4Comments

omidraha picture omidraha  路  3Comments