phorm_annotations 1.2.0
phorm_annotations: ^1.2.0 copied to clipboard
Annotation library for declarative SQL table and schema definitions in Dart. Designed to be used with code generators that produce SQL schemas, migrations, or runtime metadata.
PHORM Annotations 🚀 #
Annotation library for declarative SQL table and schema definitions in Dart.
phorm_annotations provides the building blocks for defining your database schema using standard Dart classes. It is designed to work with phorm_generator to automate SQL generation and runtime metadata.
Features #
- Declarative Schemas: Define tables and columns using
@Schema,@Column, and@ID. - Relationships: Annotations for
HasMany,BelongsTo,HasOne, andManyToMany(with pivot tables). - Type Safety: Database-agnostic logical types mapped to SQLite.
- Constraints: Support for
UNIQUE,CHECK, andDEFAULTvalues. - Paranoid Mode: Built-in support for soft deletes via
deletedAt. - Factories:
Factory<T>interface for generating test/seed data. - Value Converters:
ValueConverter<D, S>for custom Dart ↔ SQL type transformations.
Basic Usage #
1. Annotate your model #
import 'package:phorm_annotations/phorm_annotations.dart';
part 'user.sql.g.dart';
@Schema(
tableName: 'users',
paranoid: true,
columnNaming: ColumnNamingStrategy.snakeCase,
indexes: [
Index(columns: ['email'], unique: true),
Index(columns: ['first_name', 'last_name']),
],
)
class User extends Model with _$PhormUserMixin {
@ID(autoIncrement: false, unique: true)
@override
final String id;
@Column()
final String firstName;
@Column()
final String lastName;
@Column(unique: true)
final String email;
@Column(
validators: [
ContainsValidator(['M', 'F', 'Other'], constraint: 'gender_check'),
],
)
final String gender;
User({
required this.id,
required this.firstName,
required this.lastName,
required this.email,
required this.gender,
});
factory User.fromJson(Map<String, dynamic> json) => _$PhormUserFromJson(json);
}
2. Run the generator #
dart run build_runner build
Annotations Reference #
@Schema #
Defines table-level configuration.
| Property | Type | Description |
|---|---|---|
tableName |
String? |
Explicit SQL table name. |
indexes |
List<Index> |
List of table indexes. |
paranoid |
bool |
Enables soft deletes (requires deletedAt column). |
columnNaming |
ColumnNamingStrategy |
Strategy for mapping field names to SQL (snakeCase, camelCase, pascalCase). |
dialect |
SqlDialectKind |
Target SQL dialect for DDL generation (sqlite (default), postgres, mysql). |
relationships |
List<Relationship> |
Define HasMany, HasOne, or BelongsTo. |
timestamps |
bool |
Auto-manage createdAt / updatedAt (default true). |
useToJson |
bool |
Generate the toJson() mapper (default true). |
useFromJson |
bool |
Generate the fromJson() factory (default true). |
useCopyWith |
bool |
Generate the copyWith() helper (default true). |
useToString |
bool |
Generate the toString() helper (default true). |
useValidator |
bool |
Generate the validate() method from column CHECK constraints (default true). |
generateFullService |
bool |
Generate the pluralized static service class (e.g. Users) exposing the full CRUD/query API and column constants. Set false to emit only the lightweight schema/table/mappers and skip the large service (default true). |
@Column #
Standard column definition. PHORM automatically infers the SQLite type from the Dart field type.
@Column(unique: true, defaultValue: 'active')
final String status;
| Property | Type | Description |
|---|---|---|
columnName |
String? |
Override column name. |
sqlType |
String? |
Explicit SQL type override as a raw string (e.g. 'VARCHAR(255)'). |
type |
SqlType? |
Explicit SQL type as a typed object (e.g. VARCHAR(255), DECIMAL(10, 2), JSONB()). |
unique |
bool |
Enforce UNIQUE constraint. |
nullable |
bool |
Mark column as NULL or NOT NULL. |
defaultValue |
dynamic |
SQL DEFAULT value. |
validators |
List<IValidator>? |
List of validators (triggers SQL CHECK and Dart validation). |
converter |
ValueConverter? |
Custom Dart ↔ SQL value transformer. |
collate |
String? |
Specify string collation (e.g. Collate.noCase). |
SqlType objects are organised by dialect: common_types (VARCHAR, TEXT, INTEGER, BIGINT, BOOLEAN, REAL, DOUBLE, DECIMAL, DATE, TIME, TIMESTAMP, BLOB, JSON), sqlite_types (NUMERIC, Collate), postgres_types (JSONB), mysql_types (scaffolded). Resolution precedence: sqlType → type → converter → inferred Dart type.
Automatic Type Mapping #
PHORM maps Dart types to SQLite types automatically:
| Dart Type | SQLite Type | Notes |
|---|---|---|
String |
TEXT |
Default for strings, UUIDs |
int |
INTEGER |
Standard integer |
bool |
INTEGER |
Stored as 1 (true) / 0 (false) |
double |
REAL |
Floating point numbers |
num |
NUMERIC |
Supports both int and double |
DateTime |
TEXT |
Stored as ISO-8601 strings |
Uint8List |
BLOB |
Binary data |
Standard SQL Types #
For manual overrides in @Column(sqlType: ...) or when adding columns in migrations, use the SqlTypes class to avoid hardcoding strings:
// In a migration
table.addColumn(name: 'age', type: SqlTypes.integer, version: 2);
// In a model
@Column(collate: Collate.noCase)
final String username;
Available types: SqlTypes.text, SqlTypes.integer, SqlTypes.real, SqlTypes.blob, SqlTypes.numeric.
Referential Actions #
When defining relationships, use ReferentialAction for onDelete and onUpdate parameters:
HasMany(
model: Order,
foreignKey: 'user_id',
onDelete: ReferentialAction.cascade,
);
Available actions: cascade, setNull, setDefault, restrict, noAction.
🔗 Relationships #
Relationships define how models connect. They are used by phorm for automatic Eager Loading.
HasMany: One-to-Many (e.g., User has many Posts).HasOne: One-to-One (e.g., User has one Profile).BelongsTo: Many-to-One (e.g., Post belongs to User).
@Schema(
tableName: 'users',
relationships: [
HasMany(model: Post, foreignKey: 'user_id'),
ManyToMany(
model: Role,
pivotTable: 'user_roles',
foreignKey: 'user_id',
relatedKey: 'role_id',
),
],
)
class User extends Model with _$PhormUserMixin { ... }
Query Results #
phorm provides two read methods with explicit return types:
| Method | Returns | Use when |
|---|---|---|
readAll(...) |
Result<T> |
You only need the list of records |
readAllWithCount(...) |
ResultWithCount<T> |
You need the list + total count (pagination) |
// Simple read — no cast needed
final result = await userService.readAll(limit: 20);
for (final user in result.data) { ... }
// With count — no cast needed
final paged = await userService.readAllWithCount(limit: 20, offset: 40);
print('Page 3: ${paged.data.length} of ${paged.count} total');
Factories #
Use the Factory<T> interface to generate model instances for testing or seeding:
class UserFactory extends Factory<User> {
int _i = 0;
@override
User create() {
_i++;
return User(id: 'user_$_i', name: 'User $_i', email: 'user$_i@example.com');
}
}
final users = UserFactory().createMany(50); // List<User>
Status #
- Dart SDK:
>=3.0.0 <4.0.0 - License: MIT License
