HasMany
Use HasMany when one row of this table has many rows in another table.
For example, a user has many posts.
The other table keeps the foreign key (post.user_id).
Define it
Add a field with @HasMany(...) to the model.
The field type is a list of the related model. Start it as an empty list.
import 'package:dox_orm/dox_orm.dart';
import '../post/post.model.dart';
part 'user.model.g.dart';
@DoxModel()
class User extends UserGenerator {
@Column()
String? name;
@HasMany(Post)
List<Post> posts = <Post>[];
}
import 'package:dox_orm/dox_orm.dart';
part 'post.model.g.dart';
@DoxModel()
class Post extends PostGenerator {
@Column()
int? userId; // column user_id
@Column()
String? title;
@Column()
String? status;
}
Run dart run build_runner build after you add a relationship.
Load it
The relation is not loaded by default. Load it in one of these ways.
Use the field name ('posts') as the relation name.
preload
Use it when you load the models with a query.
User? user = await User().preload('posts').find(1);
print(user?.posts.length);
List<User> users = await User().preload('posts').get();
preload works with find, get, all and getFirst.
For a list of users, it runs one query for all of them.
A user without posts gets an empty list.
$getRelation
Use it when you already have the model.
related
Use it when you need more conditions on the related rows.
User? user = await User().find(1);
List<Post> activePosts = await user
?.related<Post>('posts')
?.where('status', 'active')
.orderBy('id', 'desc')
.get() ??
<Post>[];
It does not set user.posts.
eager
Use it when every query of User should load the relation.
You do not call preload.
The load runs this SQL:
reload() and a preload set the relation fields again, also when there is no related row. See Relationships.
Filter by it
whereHas() keeps the users that have a post.
whereDoesntHave() keeps the users with no post.
It does not load the posts.
List<User> writers = await User().whereHas('posts', (QueryBuilder<dynamic> q) {
q.where('status', 'active');
}).get();
List<User> noPosts = await User().whereDoesntHave('posts').get();
List<User> busy = await User().has('posts', '>=', 10).get();
See Query by relations.
Options
| Option | Default | What it is |
|---|---|---|
foreignKey |
{this model}_id, like user_id |
The column in the related table that points to this model. |
localKey |
the primary key of this model | The column in this table that foreignKey points to. |
onQuery |
none | A static function that changes the query. |
eager |
false |
Load the relation on every query. |
The default foreignKey uses the class name in snake_case.
User gives user_id, also when the table is users.
foreignKey and localKey
onQuery
onQuery is a static function in the same model class.
It gets the query of the related model and returns it.
You can have two fields on the same model with different queries.
@DoxModel()
class User extends UserGenerator {
@HasMany(Post)
List<Post> posts = <Post>[];
@HasMany(Post, onQuery: onlyActive)
List<Post> activePosts = <Post>[];
static QueryBuilder<Post> onlyActive(Post q) {
return q.where('status', 'active').orderBy('id', 'desc');
}
}
In JSON
- A relation with
eager: trueis always intoJson(). - A relation loaded with
preloadis intoJson()of that query. - The JSON key is the field name in snake_case.