From acc30202f657e2ad5cc6d5909ce03ebe7f2a063d Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 8 Dec 2018 18:30:28 -0500 Subject: [PATCH] readme updates --- README.md | 122 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 5f30c299..a4711424 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Now you can combine the power and flexibility of Angel with a strongly-typed ORM * [Model Definitions](#models) * [MVC Example](#example) * [Relationships](#relations) + * [Many-to-Many Relationships](#many-to-many-relations) * [Columns (`@Column(...)`)](#columns) * [Column Types](#column-types) * [Indices](#indices) @@ -19,19 +20,18 @@ Now you can combine the power and flexibility of Angel with a strongly-typed ORM You'll need these dependencies in your `pubspec.yaml`: ```yaml dependencies: - angel_orm: ^1.0.0-alpha + angel_orm: ^2.0.0-dev dev_dependencies: - angel_orm_generator: ^1.0.0-alpha - build_runner: ^0.5.0 + angel_orm_generator: ^2.0.0-dev + build_runner: ^1.0.0 ``` -`package:angel_orm_generator` exports three classes that you can include +`package:angel_orm_generator` exports a class that you can include in a `package:build` flow: -* `PostgresOrmGenerator` - Fueled by `package:source_gen`; include this within a `LibraryBuilder`. -* `MigrationGenerator` - Builds a [`package:angel_migration`](https://github.com/angel-dart/migration) migration for your models automatically. -* `SqlMigrationBuilder` - This is its own `Builder`; it generates a SQL schema, as well as a SQL script to drop a generated table. +* `PostgresOrmGenerator` - Fueled by `package:source_gen`; include this within a `SharedPartBuilder`. -You should pass an `List` containing your project's models. +However, it also includes a `build.yaml` that builds ORM files automatically, so you shouldn't +have to do any configuration at all. # Models Your model, courtesy of `package:angel_serialize`: @@ -64,34 +64,43 @@ designed for such a workflow. Save yourself a headache and build models with `an https://github.com/angel-dart/serialize +Remember that if you don't need automatic id-and-date fields, you can do the following: + +```dart +@Serializable(autoIdAndDateFields: false) +abstract class _ThisIsNotAnAngelModel { + +} +``` + # Example MVC just got a whole lot easier: ```dart import 'package:angel_framework/angel_framework.dart'; -import 'package:postgres/postgres.dart'; +import 'package:angel_orm/angel_orm.dart'; import 'car.dart'; import 'car.orm.g.dart'; -/// Returns an Angel plug-in that connects to a PostgreSQL database, and sets up a controller connected to it... -AngelConfigurer connectToCarsTable(PostgreSQLConnection connection) { +/// Returns an Angel plug-in that connects to a database, and sets up a controller connected to it... +AngelConfigurer connectToCarsTable(QueryExecutor executor) { return (Angel app) async { // Register the connection with Angel's dependency injection system. // // This means that we can use it as a parameter in routes and controllers. - app.container.singleton(connection); + app.container.registerSingleton(executor); // Attach the controller we create below - await app.configure(new CarController(connection)); + await app.mountController(); }; } @Expose('/cars') class CarController extends Controller { - // The `connection` will be injected. + // The `executor` will be injected. @Expose('/recalled_since_2008') - carsRecalledSince2008(PostgreSQLConnection connection) { + carsRecalledSince2008(QueryExecutor executor) { // Instantiate a Car query, which is auto-generated. This class helps us build fluent queries easily. var cars = new CarQuery(); cars.where @@ -101,16 +110,19 @@ class CarController extends Controller { // Shorter syntax we could use instead... cars.where.recalledAt.year <= 2008; - // `get()` returns a Stream. - // `get().toList()` returns a Future. - return cars.get(connection).toList(); + // `get()` returns a Future>. + return await cars.get(executor); } @Expose('/create', method: 'POST') - createCar(PostgreSQLConnection connection) async { + createCar(QueryExecutor executor) async { // `package:angel_orm` generates a strongly-typed `insert` function on the query class. // Say goodbye to typos!!! - var car = await CarQuery.insert(connection, familyFriendly: true, make: 'Honda'); + var query = new CarQuery(); + query.values + ..familyFriendly = true + ..make 'Honda'; + var car = query.insert(executor); // Auto-serialized using code generated by `package:angel_serialize` return car; @@ -121,8 +133,8 @@ class CarController extends Controller { # Relations `angel_orm` supports the following relationships: -* `@HasOne()` -* `@HasMany()` +* `@HasOne()` (one-to-one) +* `@HasMany()` (one-to-many) * `@BelongsTo()` (one-to-one) The annotations can be abbreviated with the default options (ex. `@hasOne`), or supplied @@ -153,6 +165,68 @@ Relationships use joins when possible, but in the case of `@HasMany()`, two quer * One to fetch the object itself * One to fetch a list of related objects +## Many to Many Relations +A many-to-many relationship can now be modeled like so. +`UserRole` in this case is a pivot table joining `User` and `Role`. + +Note that in this case, the models must reference the private classes (`_User`, etc.), because the canonical versions (`User`, etc.) are not-yet-generated: + +```dart +library angel_orm_generator.test.models.user; + +import 'package:angel_model/angel_model.dart'; +import 'package:angel_orm/angel_orm.dart'; +import 'package:angel_serialize/angel_serialize.dart'; +import 'package:collection/collection.dart'; +part 'user.g.dart'; + +@serializable +@orm +abstract class _User extends Model { + String get username; + String get password; + String get email; + + @hasMany + List<_UserRole> get userRoles; + + List<_Role> get roles => userRoles.map((m) => m.role).toList(); +} + +@serializable +@orm +abstract class _Role extends Model { + String name; + + @hasMany + List<_UserRole> get userRoles; + + List<_User> get users => userRoles.map((m) => m.user).toList(); +} + +@Serializable(autoIdAndDateFields: false) +@orm +abstract class _UserRole { + int get id; + + @belongsTo + _User get user; + + @belongsTo + _Role get role; +} +``` + +TLDR: +1. Make a pivot table, C, between two tables, table A and B +2. C should `@belongsTo` both A and B. +3. Both A and B should `@hasMany` C. +4. For convenience, write a simple getter, like the above `User.roles`. + +Test: https://raw.githubusercontent.com/angel-dart/orm/master/angel_orm_generator/test/many_to_many_test.dart + +There are 2 tests there, but they are more or less a proof-of-concept. All the logic for the other relations have their own unit tests. + # Columns Use a `@Column()` annotation to change how a given field is handled within the ORM. @@ -163,7 +237,7 @@ Using the `@Column()` annotation, it is possible to explicitly declare the data @serializable @orm abstract class _Foo extends Model { - @Column(type: ColumnType.BIG_INT) + @Column(type: ColumnType.bigInt) int bar; } ``` @@ -175,7 +249,7 @@ Columns can also have an `index`: @serializable @orm abstract class _Foo extends Model { - @Column(index: IndexType.PRIMARY) + @Column(index: IndexType.primaryKey) String bar; } ```