drift/moor_flutter/example/lib/database/database.dart

161 lines
4.4 KiB
Dart

import 'dart:async';
import 'package:moor_flutter/moor_flutter.dart';
part 'database.g.dart';
@DataClassName('TodoEntry')
class Todos extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get content => text()();
DateTimeColumn get targetDate => dateTime().nullable()();
IntColumn get category => integer()
.nullable()
.customConstraint('NULLABLE REFERENCES categories(id)')();
}
@DataClassName('Category')
class Categories extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get description => text().named('desc')();
}
class CategoryWithCount {
CategoryWithCount(this.category, this.count);
// can be null, in which case we count how many entries don't have a category
final Category category;
final int count; // amount of entries in this category
}
class EntryWithCategory {
EntryWithCategory(this.entry, this.category);
final TodoEntry entry;
final Category category;
}
@UseMoor(
tables: [Todos, Categories],
queries: {
'_resetCategory': 'UPDATE todos SET category = NULL WHERE category = ?',
'_categoriesWithCount': '''
SELECT
c.id,
c.desc,
(SELECT COUNT(*) FROM todos WHERE category = c.id) AS amount
FROM categories c
UNION ALL
SELECT null, null, (SELECT COUNT(*) FROM todos WHERE category IS NULL)
''',
},
)
class Database extends _$Database {
Database()
: super(FlutterQueryExecutor.inDatabaseFolder(
path: 'db.sqlite', logStatements: true));
@override
int get schemaVersion => 2;
@override
MigrationStrategy get migration {
return MigrationStrategy(
onCreate: (Migrator m) {
return m.createAll();
},
onUpgrade: (Migrator m, int from, int to) async {
if (from == 1) {
await m.addColumn(todos, todos.targetDate);
}
},
beforeOpen: (details) async {
if (details.wasCreated) {
// create default categories and entries
final workId = await into(categories)
.insert(const CategoriesCompanion(description: Value('Work')));
await into(todos).insert(TodosCompanion(
content: const Value('A first todo entry'),
targetDate: Value(DateTime.now()),
));
await into(todos).insert(
TodosCompanion(
content: const Value('Rework persistence code'),
category: Value(workId),
targetDate: Value(
DateTime.now().add(const Duration(days: 4)),
),
),
);
}
},
);
}
Stream<List<CategoryWithCount>> categoriesWithCount() {
// the _categoriesWithCount method has been generated automatically based
// on the query declared in the @UseMoor annotation
return _categoriesWithCount().map((row) {
final hasId = row.id != null;
final category =
hasId ? Category(id: row.id, description: row.desc) : null;
return CategoryWithCount(category, row.amount);
}).watch();
}
/// Watches all entries in the given [category]. If the category is null, all
/// entries will be shown instead.
Stream<List<EntryWithCategory>> watchEntriesInCategory(Category category) {
final query = select(todos).join(
[leftOuterJoin(categories, categories.id.equalsExp(todos.category))]);
if (category != null) {
query.where(categories.id.equals(category.id));
} else {
query.where(categories.id.isNull());
}
return query.watch().map((rows) {
// read both the entry and the associated category for each row
return rows.map((row) {
return EntryWithCategory(
row.readTable(todos),
row.readTableOrNull(categories),
);
}).toList();
});
}
Future createEntry(TodosCompanion entry) {
return into(todos).insert(entry);
}
/// Updates the row in the database represents this entry by writing the
/// updated data.
Future updateEntry(TodoEntry entry) {
return update(todos).replace(entry);
}
Future deleteEntry(TodoEntry entry) {
return delete(todos).delete(entry);
}
Future<int> createCategory(String description) {
return into(categories)
.insert(CategoriesCompanion(description: Value(description)));
}
Future deleteCategory(Category category) {
return transaction(() async {
await _resetCategory(category.id);
await delete(categories).delete(category);
});
}
}