diff --git a/docs/pages/docs/Getting started/advanced_dart_tables.md b/docs/pages/docs/Getting started/advanced_dart_tables.md index 37b4592c..a68dccc2 100644 --- a/docs/pages/docs/Getting started/advanced_dart_tables.md +++ b/docs/pages/docs/Getting started/advanced_dart_tables.md @@ -167,3 +167,66 @@ Applying a `customConstraint` will override all other constraints that would be particular, that means that we need to also include the `NOT NULL` constraint again. You can also add table-wide constraints by overriding the `customConstraints` getter in your table class. + +## References + +[Foreign key references](https://www.sqlite.org/foreignkeys.html) can be expressed +in Dart tables with the `references()` method when building a column: + +```dart +class Todos extends Table { + // ... + IntColumn get category => integer().nullable().references(Categories, #id)(); +} + +@DataClassName("Category") +class Categories extends Table { + IntColumn get id => integer().autoIncrement()(); + // and more columns... +} +``` + +The first parameter to `references` points to the table on which a reference should be created. +The second parameter is a [symbol](https://dart.dev/guides/language/language-tour#symbols) of the column to use for the reference. + +Optionally, the `onUpdate` and `onDelete` parameters can be used to describe what +should happen when the target row gets updated or deleted. + +Be aware that, in sqlite3, foreign key references aren't enabled by default. +They need to be enabled with `PRAGMA foreign_keys = ON`. +A suitable place to issue that pragma with drift is in a [post-migration callback]({{ '../Advanced Features/migrations.md#post-migration-callbacks' | pageUrl }}). + +## Views + +It is also possible to define [SQL views](https://www.sqlite.org/lang_createview.html) +as Dart classes. +To do so, write an abstract class extending `View`. This example declares a view reading +the amount of todo-items added to a category in the schema from [the example]({{ 'index.md' | pageUrl }}): + +```dart +abstract class CategoryTodoCount extends View { + TodosTable get todos; + Categories get categories; + + Expression get itemCount => todos.id.count(); + + @override + Query as() => select([categories.description, itemCount]) + .from(categories) + .join([innerJoin(todos, todos.category.equalsExp(categories.id))]); +} +``` + +Inside a Dart view, use + +- abstract getters to declare tables that you'll read from (e.g. `TodosTable get todos`) +- `Expression` getters to add columns: (e.g. `itemCount => todos.id.count()`); +- the overridden `as` method to define the select statement backing the view + +Finally, a view needs to be added to a database or accessor by including it in the +`views` parameter: + +```dart +@DriftDatabase(tables: [Todos, Categories], views: [CategoryTodoCount]) +class MyDatabase extends _$MyDatabase { +``` diff --git a/drift/lib/src/dsl/database.dart b/drift/lib/src/dsl/database.dart index 2163fee9..babb6ac1 100644 --- a/drift/lib/src/dsl/database.dart +++ b/drift/lib/src/dsl/database.dart @@ -95,7 +95,7 @@ class DriftAccessor { /// The tables accessed by this DAO. final List tables; - /// The views to include in the database + /// The views to make accessible in this DAO. final List views; /// {@macro drift_compile_queries_param} diff --git a/drift/lib/src/dsl/table.dart b/drift/lib/src/dsl/table.dart index 9eba7c86..c0ed6752 100644 --- a/drift/lib/src/dsl/table.dart +++ b/drift/lib/src/dsl/table.dart @@ -7,6 +7,8 @@ abstract class HasResultSet { } /// Subclasses represent a table in a database generated by drift. +/// +/// For more information on how to write tables, see [the documentation](https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/) abstract class Table extends HasResultSet { /// Defines a table to be used with drift. const Table(); @@ -119,19 +121,41 @@ abstract class Table extends HasResultSet { } /// Subclasses represent a view in a database generated by drift. +/// +/// For more information on how to define views in Dart, see +/// [the documentation](https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/#views) abstract class View extends HasResultSet { /// Defines a view to be used with drift. const View(); + /// The select method can be used in [as] to define the select query backing + /// this view. /// + /// The select statement should select all columns defined on this view. @protected View select(List columns) => _isGenerated(); + /// This method should be called on [select] to define the main table of this + /// view: /// + /// ```dart + /// abstract class CategoryTodoCount extends View { + /// TodosTable get todos; + /// Categories get categories; + /// + /// Expression get itemCount => todos.id.count(); + /// + /// @override + /// Query as() => select([categories.description, itemCount]) + /// .from(categories) + /// .join([innerJoin(todos, todos.category.equalsExp(categories.id))]); + /// } + /// ``` @protected SimpleSelectStatement from(Table table) => _isGenerated(); - /// + /// This method is overridden by Dart-defined views to declare the right + /// query to run. @visibleForOverriding Query as(); } @@ -210,13 +234,3 @@ class DriftView { /// Customize view name and data class name const DriftView({this.name, this.dataClassName}); } - -/// -@Target({TargetKind.getter}) -class Reference { - /// - final Type type; - - /// - const Reference(this.type); -} diff --git a/drift/lib/src/runtime/query_builder/schema/view_info.dart b/drift/lib/src/runtime/query_builder/schema/view_info.dart index 9043b8b3..a976d9de 100644 --- a/drift/lib/src/runtime/query_builder/schema/view_info.dart +++ b/drift/lib/src/runtime/query_builder/schema/view_info.dart @@ -9,7 +9,7 @@ part of '../query_builder.dart'; /// /// [sqlite-docs]: https://www.sqlite.org/lang_createview.html /// [sql-tut]: https://www.sqlitetutorial.net/sqlite-create-view/ -abstract class ViewInfo +abstract class ViewInfo implements ResultSetImplementation { @override String get entityName; @@ -17,6 +17,6 @@ abstract class ViewInfo /// The `CREATE VIEW` sql statement that can be used to create this view. String? get createViewStmt; - /// Predefined query from View.as() + /// Predefined query from `View.as()` Query? get query; } diff --git a/drift_dev/pubspec.yaml b/drift_dev/pubspec.yaml index 29f8dcc1..cfcdbbe4 100644 --- a/drift_dev/pubspec.yaml +++ b/drift_dev/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: io: ^1.0.3 # Moor-specific analysis and apis - drift: ^1.1.0-dev + drift: '>=1.1.0-dev <1.2.0' sqlite3: '>=0.1.6 <2.0.0' sqlparser: ^0.18.0