If I want to use a domain class, e.g. MoneyTransaction, for two entirely different purposes, i.e.:
1) when a customer places an order
2) when a member gets paid
such that I have something like:
class Order {
static hasMany = [transactions: MoneyTransaction]
}
class Member {
static hasMany = [payments: MoneyTransaction]
}
and
class MoneyTransaction {
static belongsTo = [order: Order, member: Member]
static constraints = {
order(nullable: true)
member(nullable: true)
}
}
and then in essence only use one belongsTo/association at a time, is this pretty "standard" usage, or do I need to switch this modeling? Right now MoneyTransaction has both credit card and ACH payment capabilities, as both apply for orders. For payments, just the ACH portion will be used.
The domain class definitions that you have posted seem correct based on your requirements. One modification that I'd make here would be to add a custom validator to make sure that both order and member are not null at the same time.
static constraints = {
order(nullable: true, validator: {field, inst -> inst.member || field})
member(nullable: true)
}
Related
In my Grails project I need to add a particular constraint to an entry of my domain class object.
Domain class is as follows:
class HealthServiceType {
String healthService;
static belongsTo = [doctor:Doctor]
static constraints = {
}
static mapping = {
}
}
I need that healthService is not empty and is unique for each Doctor; that is, I can have multiple "test" value for healthService, but each one need to have different Doctor value.
Is it possible to perform this constraint in domain class? Or do I need to implement some check in Controller?
That's quite simple to make a unique property. For example:
static constraints = {
healthService(unique: ['doctor'])
}
The above will ensure that the value of healthService is unique for each value of doctor within your domain class.
What I want: when I call validate (or save) on a parent domain during a unit test, it will check the constraints of all children domains AND their children AND their children etc.
What is happening: during a unit test for a service, validate keeps returning true despite constraints not being met for children domains. Please help...
I had two change two things about my code:
1) I had to make sure all domains were mocked for the unit test.
2) I had to make sure each child domain's back reference to the parent was populated. obj.addToSomeProperty(property) automatically populated back references for one-to-many objects, but for one-to-one I had to set it manually.
Service Unit Test:
#TestFor(GardenerService)
#Mock([Orchard, Plot, Tree, Fruit])
class GardenerServiceSpec extends Specification {
void "test save and fetch"() {
setup:
Fruit fruit = new Fruit(name:"Peach")
Tree tree = new Tree(height:6)
tree.addToFruits(fruit)
Plot plot = new Plot(litersOfWaterAdded:10)
plot.tree = tree // Must be a better way... why didn't setTree(tree)
tree.plot = plot // set the back reference
Orchard orchard = new Orchard(orchardName:"Eden")
orchard.addToPlots(plot)
when:
orchard.save(flush: true)
then:
orchard.validate()
Fruit.findByName("Peach").tree.plot.orchard.name == "Eden"
Tree.findByHeight(6).plot.litersOfWaterAdded == 10
}
}
Domains:
class Orchard {
String orchardName
static hasMany = [plots: Plot]
}
class Plot{
static belongsTo = [orchard: Orchard]
int litersOfWaterAdded
static hasOne = [tree: Tree]
}
class Tree {
static belongsTo = [plot: Plot]
int height
static hasMany = [fruits: Fruit]
}
class Fruit {
static belongsTo = [tree: Tree]
String name
String description
static constraints = { description nullable: true }
}
I want to add a child to two different parent class, like that:
First:
class Member {
Profile profile
Member() {
this.profile = new Profile()
}
static mapping = {
profile cascade: 'all-delete-orphan'
}
}
Second:
class Team {
Profile profile
Team() {
this.profile = new Profile()
}
static mapping = {
profile cascade: 'all-delete-orphan'
}
}
Thw child is simply define like that
class Profile() {
}
The probleme is when I save the parent, it dosent save the child:
Member member = new Member().save(flush: true, failOnError: true)
assert !member.hasErrors()
assert member.profile
assert !member.profile.hasErrors()
assert member.profile.id //FAIL
What do I do wrong? Is there a better way to do it?
UPDATE:
I found this
Saving associated domain classes in Grails
It seem that 'belong to' is needed for that kind of behavior. But Why the 'cascade: 'all-delete-orphan' doesn't force this ? Because I can't use 'belong to' in that specific case
I copied your example (with slight modification to change group table name to a non-reserved word) and the cascades are working properly using grails 2.2.1. Both Member and Group cascaded their saves to the newly created Profiles.
Assuming your classes are more complicated than this, you might have an error elsewhere in your class (eg cascade behavior described in constraints instead of mapping, etc).
I found a nice solution. The best was to define both parent in the child but nullable. Like that:
class Profile() {
static belongsTo = [member: Member, team: Team]
static constraints = {
member nullable: true
team nullable: true
}
}
This way, the cascade behavior work just fine !
I read that a m:m relationship often means there is a third class that isn't yet required. So I have m:m on User and Project, and I created a third domain class, ProjectMembership
The three domains are as follows (minimized for illustration purposes):
User
class User {
String name
static hasMany = [projectMemberships : ProjectMembership]
}
Project Membership
class ProjectMembership {
static constraints = {
}
static belongsTo = [user:User, project:Project]
}
Project:
class Project {
String name
static hasMany = [projectMemberships : ProjectMembership]
static constraints = {
}
}
If I have the ID of the user, how can I get a list of Project objects that they are assigned to?
There are a handful of ways - here are a couple:
def user = User.get(userId)
ProjectMembership.findAllByUser(user).collect { it.project }
or to avoid the query for the User:
ProjectMembership.withCriteria {
user {
eq('id', userId)
}
}.collect { it.project }
Be wary of queries that'll return large result sets - you'll end up with a huge in-memory list of project objects.
I have a User domain class, and a List one.
Each list must have an author (a user) and each user must have a "primary list". Only some of the lists will have the "primaryList" statute.
So.. somthing like
User:
List primaryList
List:
User author
static belongsTo = User
Of course this does not work as intended because the two relations are mistakenly taken as only one. I should also add a hasMany on the User and other belongsTo to the List..., but I don't want to complicate the example because I want to get the right answer from you.
You may need to use mappedBy to explain how the fields in User and List line up. Here are a couple domains that I wrote that allow a User to author many Lists but only set one to be "primary". There are a couple extra nullable constraints so you can use the scaffolded UI without getting into a chicken-and-egg scenario.
class User {
String name
FooList primaryList
static hasMany = [authoredLists: FooList]
static mappedBy = [primaryList: 'primaryOwner', authoredLists: 'author']
static constraints = {
primaryList nullable: true, unique: true
authoredLists nullable: true
}
String toString() { name }
}
I named this class "FooList" just to avoid confusion with the standard List class:
class FooList {
static belongsTo = [author: User, primaryOwner: User]
static constraints = {
primaryOwner nullable: true, display: false
}
}
Try using the map belongsTo approach:
static belongsTo = [user:User]
That way Grails should see the 2 properties as separate.