PDA

View Full Version : Domain Modeling Question



ThomasBecker
Feb 4th, 2011, 10:14 AM
Hi all,

I'm evaluating to use grails for a project. I'm completely knew to grails and trying to model my domain.

I've the following simple domain just to try how domain modeling works in grails:



class Survey {
static hasMany = [answers : SurveyAnswer,responses: SurveyResponse]
...
}

class SurveyAnswer {
static belongsTo = [survey:Survey]
static hasMany = [responses:SurveyResponse]
String answer;

static constraints = {
survey(blank:false)
answer(blank:false)
}
...
}

class SurveyResponse {
static belongsTo = [answer:SurveyAnswer, survey:Survey]

static constraints = {
answer(blank:false)
dateCreated(editable:false)
lastUpdated(editable:false)
}
...
}

With scaffolding controllers I have all the CRUD views I need for the moment (very cool). However when I add a SurveyResponse I'm able to save all combinations of survey + answer. But apparently I should be able to store Responses for a specific survey only with answers which belong to that survey.
Is there any standard grails way to apply such constraints and I just don't find it in the documentation or do I have to add custom code in the controller layer?

Thanks a lot,
Thomas

ThomasBecker
Feb 4th, 2011, 11:48 AM
I thought about skipping the relation between Survey and SurveyResponse. There's still the connection Survey - SurveyAnswer - SurveyResponse.
But the question still stays how to tell grails that it should only allow SurveyResponses which have a direct or indirect relation to a specific survey.

deuseks
Feb 4th, 2011, 12:47 PM
Thomas
I just wanted to offer a quick response that may or may not be helpful but I still recommend it based on what you seem to be modeling.

Burt Beckwith - an excellent contributor to the Grails ecosystem - has a presentation at infoq : http://www.infoq.com/presentations/GORM-Performance where he talks a little about these one to many and many to many relations especially in the context of what may be large numbers of child objects.

Kindly watch it and in the process you may actually get some answers to the questions about best practices you mentioned.

Just a thought.

ThomasBecker
Feb 7th, 2011, 08:22 AM
Hi,

watched and enjoyed the presentation. Thanks for the link. Remodelt my domain a bit accordingly.

However my question is not 100% answered, but I'am trying to establish the constraints now on the view.

Cheers,
Thomas

deuseks
Feb 7th, 2011, 11:36 AM
Hi Thomas
I believe Grails Constraints blocks work hand in hand with validation. They are used mainly to ensure that before you save data, you can validate your object state by calling the validate() method on your domain instance to have it checked against the rules in the constraints block. see: Link to Validation documentation (http://grails.org/doc/latest/ref/Domain%20Classes/constraints.html)

For what you want to do, I noticed you have answer as a String and not a SurveyResponse. You should change that. Perhaps that is all that is wrong in your definitions.!

ThomasBecker
Feb 7th, 2011, 05:11 PM
HI deuseks,

thanks for your reply. I will look more into how form validation is done in grails. I can definetly work out something with custom code and or grails validation. I just thought there must be something builtin in grails.

Regarding the Answer as String. My domain model is a simplified part of the domain the real application will have and is not final at all. It's just enough to evaluate if grails is up to do the job:

I've a Survey object.
A Survey will have multiple SurveyAnswer Objects (each representing a single answer for this Survey).
A Survey will have multiple SurveyResponse Objects. These are the answers given by the user to a survey.

The constraint I'd like to have enforced are:

- A SurveyResponse for Survey 1 can only have a SurveyAnswer which belongs to Survey 1. Answers belonging to Survey 2 should not be allowed.

I will see if I can work around that and am having some ideas already. But atm. I'm stuck with another issue which should be a really simple thing to do in grails. See my other post.

Cheers,
Thomas

pledbrook
Feb 8th, 2011, 08:23 AM
A custom 'validator' constraint (http://grails.org/doc/latest/ref/Constraints/validator.html) should work for you.

ThomasBecker
Feb 8th, 2011, 11:10 AM
HI deuseks, Hi Peter,

thanks for the hints. I'm trying to do the following now:



class Response {
Survey survey
Answer answer
Store store
static belongsTo = [answer:Answer, survey:Survey, store:Store]

static constraints = {
survey(blank:false)
store(blank:false)
answer(blank:false),inList:survey.answers)
}

String toString(){
return answer.toString()
}
}


However it doesn't work and throws a MissingPropertyException:

Caused by: groovy.lang.MissingPropertyException: No such property: survey for class: com.bat.pop.server.Response
Possible solutions: survey

Intuitively I'd expected that this should do the job. However will now try the custom validator instead.

Had a couple of more issues with grails. Some have been caused by myself trying the wrong thing. But it always took me a while to find out what the root cause is. Maybe it's just me being not clever enough for grails. But I usually don't have that many issues with other technologies. Lets see how far I'll get. Maybe the risk to go for grails is too high considering that what I'm trying to do now is in no way complicated when writing a nice plain Java Domain model, putting some ORM on top of it and writing the CRUD views yourself.

Thanks for the help again.

Cheers,
Thomas

pledbrook
Feb 8th, 2011, 11:28 AM
Thomas,

The particular problem in this case is that you are referring to an instance property from the static constraints block. That won't work (for the same reasons it doesn't work in Java). However, the Groovy error message is a little mystic. We should definitely point out common pitfalls like this.

What you really want is a custom validator:


class Response {
Survey survey
Answer answer
Store store

static belongsTo = [answer:Answer, survey:Survey, store:Store]

static constraints = {
survey(blank:false)
store(blank:false)
answer(blank:false), validator: { val, obj -> val in obj.survey.answers} )
}

String toString(){
return answer.toString()
}
}


The second argument to the validator closure is the Response instance, which allows you to access its instance 'survey' property. 'val' is the value that is being saved for that property.

We definitely value such feedback because it helps us improve the feedback mechanisms in Grails. In fact, we should be able to improve the error message in the above example, so I'll raise an issue.

ThomasBecker
Feb 8th, 2011, 11:51 AM
Hi Peter,

great. Works. Thumbs up for the good support.

Thinking about the fact that the constraint block is static it should have been clear to me that I can't access instance variables in there. *facepalm* Won't do the same mistake again.

The custom validator looks quite nice and I even like the syntax. Will not try to add custom error message and then this one is sorted out as well.

Cheers,
Thomas