Skip to content

Commit

Permalink
Simplify n:m example with json functions
Browse files Browse the repository at this point in the history
  • Loading branch information
simolus3 committed Sep 24, 2023
1 parent 14ace17 commit 8d05c9e
Show file tree
Hide file tree
Showing 8 changed files with 1,068 additions and 50 deletions.
139 changes: 139 additions & 0 deletions docs/lib/snippets/modular/many_to_many/json.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import 'package:drift/drift.dart';
import 'package:drift/extensions/json1.dart';
import 'package:json_annotation/json_annotation.dart';

import 'json.drift.dart';
import 'shared.dart' show BuyableItems;
import 'shared.drift.dart';

part 'json.g.dart';

typedef ShoppingCartWithItems = ({
ShoppingCart cart,
List<BuyableItem> items,
});

// #docregion tables
@DataClassName('ShoppingCart')
class ShoppingCarts extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get entries => text().map(ShoppingCartEntries.converter)();

// we could also store some further information about the user creating
// this cart etc.
}

@JsonSerializable()
class ShoppingCartEntries {
final List<int> items;

ShoppingCartEntries({required this.items});

factory ShoppingCartEntries.fromJson(Map<String, Object?> json) =>
_$ShoppingCartEntriesFromJson(json);

Map<String, Object?> toJson() {
return _$ShoppingCartEntriesToJson(this);
}

static JsonTypeConverter<ShoppingCartEntries, String> converter =
TypeConverter.json(
fromJson: (json) =>
ShoppingCartEntries.fromJson(json as Map<String, Object?>),
toJson: (entries) => entries.toJson(),
);
}

// #enddocregion tables

@DriftDatabase(tables: [BuyableItems, ShoppingCarts])
class JsonBasedDatabase extends $JsonBasedDatabase {
JsonBasedDatabase(QueryExecutor e) : super(e);

@override
int get schemaVersion => 1;

// #docregion createEmptyCart
Future<ShoppingCartWithItems> createEmptyCart() async {
final cart = await into(shoppingCarts)
.insertReturning(const ShoppingCartsCompanion());

// we set the items property to [] because we've just created the cart - it
// will be empty
return (cart: cart, items: <BuyableItem>[]);
}
// #enddocregion createEmptyCart

// #docregion updateCart
Future<void> updateCart(ShoppingCartWithItems entry) async {
await update(shoppingCarts).replace(entry.cart.copyWith(
entries: ShoppingCartEntries(items: [
for (final item in entry.items) item.id,
])));
}
// #enddocregion updateCart

// #docregion watchCart
Stream<ShoppingCartWithItems> watchCart(int id) {
final referencedItems = shoppingCarts.entries.jsonEach(this, r'#$.items');

final cartWithEntries = select(shoppingCarts).join(
[
// Join every referenced item from the json array
innerJoin(referencedItems, const Constant(true), useColumns: false),
// And use that to join the items
innerJoin(
buyableItems,
buyableItems.id.equalsExp(referencedItems.value.cast()),
),
],
)..where(shoppingCarts.id.equals(id));

return cartWithEntries.watch().map((rows) {
late ShoppingCart cart;
final entries = <BuyableItem>[];

for (final row in rows) {
cart = row.readTable(shoppingCarts);
entries.add(row.readTable(buyableItems));
}

return (cart: cart, items: entries);
});
}
// #enddocregion watchCart

// #docregion watchAllCarts
Stream<List<ShoppingCartWithItems>> watchAllCarts() {
final referencedItems = shoppingCarts.entries.jsonEach(this, r'#$.items');

final cartWithEntries = select(shoppingCarts).join(
[
// Join every referenced item from the json array
innerJoin(referencedItems, const Constant(true), useColumns: false),
// And use that to join the items
innerJoin(
buyableItems,
buyableItems.id.equalsExp(referencedItems.value.cast()),
),
],
);

return cartWithEntries.watch().map((rows) {
final entriesByCart = <ShoppingCart, List<BuyableItem>>{};

for (final row in rows) {
final cart = row.readTable(shoppingCarts);
final item = row.readTable(buyableItems);

entriesByCart.putIfAbsent(cart, () => []).add(item);
}

return [
for (final entry in entriesByCart.entries)
(cart: entry.key, items: entry.value)
];
});
}
// #enddocregion watchAllCarts
}
203 changes: 203 additions & 0 deletions docs/lib/snippets/modular/many_to_many/json.drift.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:drift_docs/snippets/modular/many_to_many/shared.drift.dart'
as i1;
import 'package:drift_docs/snippets/modular/many_to_many/json.drift.dart' as i2;
import 'package:drift_docs/snippets/modular/many_to_many/json.dart' as i3;

abstract class $JsonBasedDatabase extends i0.GeneratedDatabase {
$JsonBasedDatabase(i0.QueryExecutor e) : super(e);
late final i1.$BuyableItemsTable buyableItems = i1.$BuyableItemsTable(this);
late final i2.$ShoppingCartsTable shoppingCarts =
i2.$ShoppingCartsTable(this);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@override
List<i0.DatabaseSchemaEntity> get allSchemaEntities =>
[buyableItems, shoppingCarts];
}

class $ShoppingCartsTable extends i3.ShoppingCarts
with i0.TableInfo<$ShoppingCartsTable, i2.ShoppingCart> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$ShoppingCartsTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
i0.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const i0.VerificationMeta _entriesMeta =
const i0.VerificationMeta('entries');
@override
late final i0.GeneratedColumnWithTypeConverter<i3.ShoppingCartEntries, String>
entries = i0.GeneratedColumn<String>('entries', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true)
.withConverter<i3.ShoppingCartEntries>(
i2.$ShoppingCartsTable.$converterentries);
@override
List<i0.GeneratedColumn> get $columns => [id, entries];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'shopping_carts';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i2.ShoppingCart> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
context.handle(_entriesMeta, const i0.VerificationResult.success());
return context;
}

@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i2.ShoppingCart map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i2.ShoppingCart(
id: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!,
entries: i2.$ShoppingCartsTable.$converterentries.fromSql(attachedDatabase
.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}entries'])!),
);
}

@override
$ShoppingCartsTable createAlias(String alias) {
return $ShoppingCartsTable(attachedDatabase, alias);
}

static i0.JsonTypeConverter2<i3.ShoppingCartEntries, String, String>
$converterentries = i3.ShoppingCartEntries.converter;
}

class ShoppingCart extends i0.DataClass
implements i0.Insertable<i2.ShoppingCart> {
final int id;
final i3.ShoppingCartEntries entries;
const ShoppingCart({required this.id, required this.entries});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<int>(id);
{
final converter = i2.$ShoppingCartsTable.$converterentries;
map['entries'] = i0.Variable<String>(converter.toSql(entries));
}
return map;
}

i2.ShoppingCartsCompanion toCompanion(bool nullToAbsent) {
return i2.ShoppingCartsCompanion(
id: i0.Value(id),
entries: i0.Value(entries),
);
}

factory ShoppingCart.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return ShoppingCart(
id: serializer.fromJson<int>(json['id']),
entries: i2.$ShoppingCartsTable.$converterentries
.fromJson(serializer.fromJson<String>(json['entries'])),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'entries': serializer.toJson<String>(
i2.$ShoppingCartsTable.$converterentries.toJson(entries)),
};
}

i2.ShoppingCart copyWith({int? id, i3.ShoppingCartEntries? entries}) =>
i2.ShoppingCart(
id: id ?? this.id,
entries: entries ?? this.entries,
);
@override
String toString() {
return (StringBuffer('ShoppingCart(')
..write('id: $id, ')
..write('entries: $entries')
..write(')'))
.toString();
}

@override
int get hashCode => Object.hash(id, entries);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i2.ShoppingCart &&
other.id == this.id &&
other.entries == this.entries);
}

class ShoppingCartsCompanion extends i0.UpdateCompanion<i2.ShoppingCart> {
final i0.Value<int> id;
final i0.Value<i3.ShoppingCartEntries> entries;
const ShoppingCartsCompanion({
this.id = const i0.Value.absent(),
this.entries = const i0.Value.absent(),
});
ShoppingCartsCompanion.insert({
this.id = const i0.Value.absent(),
required i3.ShoppingCartEntries entries,
}) : entries = i0.Value(entries);
static i0.Insertable<i2.ShoppingCart> custom({
i0.Expression<int>? id,
i0.Expression<String>? entries,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (entries != null) 'entries': entries,
});
}

i2.ShoppingCartsCompanion copyWith(
{i0.Value<int>? id, i0.Value<i3.ShoppingCartEntries>? entries}) {
return i2.ShoppingCartsCompanion(
id: id ?? this.id,
entries: entries ?? this.entries,
);
}

@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<int>(id.value);
}
if (entries.present) {
final converter = i2.$ShoppingCartsTable.$converterentries;
map['entries'] = i0.Variable<String>(converter.toSql(entries.value));
}
return map;
}

@override
String toString() {
return (StringBuffer('ShoppingCartsCompanion(')
..write('id: $id, ')
..write('entries: $entries')
..write(')'))
.toString();
}
}
Loading

0 comments on commit 8d05c9e

Please sign in to comment.