Skip to content

Instantly share code, notes, and snippets.

@MillerAdulu
Forked from simolus3/shopping_cart.dart
Last active July 10, 2020 10:12
Show Gist options
  • Select an option

  • Save MillerAdulu/c7e3190666359fae8521e30deb1e81c7 to your computer and use it in GitHub Desktop.

Select an option

Save MillerAdulu/c7e3190666359fae8521e30deb1e81c7 to your computer and use it in GitHub Desktop.
Shopping carts in moor
import 'package:moor/moor.dart';
import 'package:rxdart/rxdart.dart';
part 'shopping_cart.g.dart';
class ShoppingCarts extends Table {
IntColumn get id => integer().autoIncrement()();
}
class BuyableItems extends Table {
IntColumn get id => integer().autoIncrement()();
// price, description, idk
}
@DataClassName('ShoppingCartEntry')
class ShoppingCartEntries extends Table {
IntColumn get shoppingCart => integer()();
IntColumn get item => integer()();
}
class CartWithItems {
final ShoppingCart cart;
final List<BuyableItem> items;
CartWithItems(this.cart, this.items);
}
@UseMoor(tables: [ShoppingCarts, BuyableItems, ShoppingCartEntries])
class Database extends _$Database {
@override
final int schemaVersion = 1;
Database(QueryExecutor e) : super(e);
@override
MigrationStrategy get migration {
return MigrationStrategy(
beforeOpen: (_, details) async {
if (details.wasCreated) {
// write some fake items
for (var i = 0; i < 10; i++) {
await into(buyableItems).insert(const BuyableItemsCompanion());
}
}
},
);
}
/// Create a new empty shopping cart
Future<ShoppingCart> createShoppingCart() async {
final id = await into(shoppingCarts).insert(const ShoppingCartsCompanion());
return ShoppingCart(id: id);
}
Future<List<BuyableItem>> loadAllItems() async {
return await select(buyableItems).get();
}
Stream<CartWithItems> watchCart(int id) {
final cartQuery = select(shoppingCarts)
..where((cart) => cart.id.equals(id));
final contentQuery = select(shoppingCartEntries).join(
[
innerJoin(
buyableItems,
buyableItems.id.equalsExp(shoppingCartEntries.item),
),
],
)..where(shoppingCartEntries.shoppingCart.equals(id));
final cartStream = cartQuery.watchSingle();
final contentStream = contentQuery.watch().map((rows) {
return rows.map((row) => row.readTable(buyableItems)).toList();
});
return Rx.combineLatest2(cartStream, contentStream,
(ShoppingCart cart, List<BuyableItem> items) {
return CartWithItems(cart, items);
});
}
Stream<List<CartWithItems>> watchAllCarts() {
// start by watching all carts
final cartStream = select(shoppingCarts).watch();
return cartStream.switchMap((carts) {
// this method is called whenever the list of carts changes. For each
// cart, we want to load all the items in it.
// (we create a map from id to cart here just for performance reasons)
final idToCart = {for (var cart in carts) cart.id: cart};
final ids = idToCart.keys;
// select all entries that are included in a cart that we found
final entryQuery = select(shoppingCartEntries).join(
[
innerJoin(
buyableItems,
buyableItems.id.equalsExp(shoppingCartEntries.item),
)
],
)..where(shoppingCartEntries.shoppingCart.isIn(ids));
return entryQuery.watch().map((rows) {
// Store the list of entries for each cart, again using maps for faster
// lookups.
final idToItems = <int, List<BuyableItem>>{};
// for each entry (row) that is included in a cart, put it in the map
// of items.
for (var row in rows) {
final item = row.readTable(buyableItems);
final id = row.readTable(shoppingCartEntries).shoppingCart;
idToItems.putIfAbsent(id, () => []).add(item);
}
// finally, all that's left is to merge the map of carts with the map of
// entries
return [
for (var id in ids)
CartWithItems(idToCart[id], idToItems[id] ?? []),
];
});
});
}
Future<void> writeShoppingCart(CartWithItems entry) {
return transaction((_) async {
final cart = entry.cart;
// first, we write the shopping cart
await into(shoppingCarts).insert(cart, orReplace: true);
// replace entries of the cart
await (delete(shoppingCartEntries)
..where((entry) => entry.shoppingCart.equals(cart.id)))
.go();
await into(shoppingCartEntries).insertAll([
for (var item in entry.items)
ShoppingCartEntry(shoppingCart: cart.id, item: item.id),
]);
});
}
}
void main() async {
final db = Database(VMDatabase.memory());
final cart = await db.createShoppingCart();
final availableItems = await db.loadAllItems();
final sub = db.watchCart(cart.id).listen((updated) {
print('Cart ${updated.cart} now consists of ${updated.items}');
});
await db.writeShoppingCart(CartWithItems(cart, availableItems.sublist(0, 3)));
await db.writeShoppingCart(CartWithItems(cart, availableItems.sublist(3, 6)));
await db.writeShoppingCart(CartWithItems(cart, availableItems.sublist(6)));
await Future.delayed(const Duration(seconds: 2));
await sub.cancel();
await db.close();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment