Source-generated ORM for use with the Angel framework.
Switch branches/tags
Nothing to show
Clone or download
Latest commit 6f33f28 Oct 23, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.idea Remove old build scripts Aug 24, 2018
angel_orm Bump to 2.0.0-dev.2 Aug 24, 2018
angel_orm_generator un-private Oct 22, 2018
tool Restructured Jul 10, 2017
.gitignore Ignore .dart_tool May 4, 2018
.travis.yml Restructured Jul 10, 2017
README.md README Nov 18, 2017

README.md

orm

Pub build status

Source-generated PostgreSQL ORM for use with the Angel framework. Now you can combine the power and flexibility of Angel with a strongly-typed ORM.

Usage

You'll need these dependencies in your pubspec.yaml:

dependencies:
  angel_orm: ^1.0.0-alpha
dev_dependencies:
  angel_orm_generator: ^1.0.0-alpha
  build_runner: ^0.5.0

package:angel_orm_generator exports three classes 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 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.

You should pass an List<String> containing your project's models.

Models

Your model, courtesy of package:angel_serialize:

library angel_orm.test.models.car;

import 'package:angel_framework/common.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';
part 'car.g.dart';

@serializable
@orm
class _Car extends Model {
  String make;
  String description;
  bool familyFriendly;
  DateTime recalledAt;
}

Models can use the @Alias() annotation; package:angel_orm obeys it.

After building, you'll have access to a Query class with strongly-typed methods that allow to run asynchronous queries without a headache.

IMPORTANT: The ORM assumes that you are using package:angel_serialize, and will only generate code designed for such a workflow. Save yourself a headache and build models with angel_serialize:

https://github.com/angel-dart/serialize

Example

MVC just got a whole lot easier:

import 'package:angel_framework/angel_framework.dart';
import 'package:postgres/postgres.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) {
  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);
    
    // Attach the controller we create below
    await app.configure(new CarController(connection));
  };
}

@Expose('/cars')
class CarController extends Controller {
  // The `connection` will be injected.
  @Expose('/recalled_since_2008')
  carsRecalledSince2008(PostgreSQLConnection connection) {
    // Instantiate a Car query, which is auto-generated. This class helps us build fluent queries easily.
    var cars = new CarQuery();
    cars.where
      ..familyFriendly.equals(false)
      ..recalledAt.year.greaterThanOrEqualTo(2008);
    
    // 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();
  }
  
  @Expose('/create', method: 'POST')
  createCar(PostgreSQLConnection connection) 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');
    
    // Auto-serialized using code generated by `package:angel_serialize`
    return car;
  }
}

Relations

angel_orm supports the following relationships:

  • @HasOne()
  • @HasMany()
  • @BelongsTo() (one-to-one)

The annotations can be abbreviated with the default options (ex. @hasOne), or supplied with custom parameters (ex. @HasOne(foreignKey: 'foreign_id')).

@serializable
@orm
abstract class _Author extends Model {
  @hasMany // Use the defaults, and auto-compute `foreignKey`
  List<Book> books;
  
  // Also supports parameters...
  @HasMany(localKey: 'id', foreignKey: 'author_id', cascadeOnDelete: true)
  List<Book> books;
  
  @Alias('writing_utensil')
  @hasOne
  Pen pen;
}

The relationships will "just work" out-of-the-box, following any operation. For example, after fetching an Author from the database in the above example, the books field would be populated with a set of deserialized Book objects, also fetched from the database.

Relationships use joins when possible, but in the case of @HasMany(), two queries are used:

  • One to fetch the object itself
  • One to fetch a list of related objects

Columns

Use a @Column() annotation to change how a given field is handled within the ORM.

Column Types

Using the @Column() annotation, it is possible to explicitly declare the data type of any given field:

@serializable
@orm
abstract class _Foo extends Model {
  @Column(type: ColumnType.BIG_INT)
  int bar;
}

Indices

Columns can also have an index:

@serializable
@orm
abstract class _Foo extends Model {
  @Column(index: IndexType.PRIMARY)
  String bar;
}

Default Values

It is also possible to specify the default value of a field. Note that this only works with primitive objects.

If a default value is supplied, the SqlMigrationBuilder will include it in the generated schema. The PostgresOrmGenerator ignores default values; it does not need them to function properly.

@serializable
@orm
abstract class _Foo extends Model {
  @Column(defaultValue: 'baz')
  String bar;
}