PDA

View Full Version : Is possible to create a domain and its related domain objects in one line?



Saioa
Nov 18th, 2010, 07:55 AM
Hello,

I'm part of a team of developers and we're working in our second grails project. We're still learning some great features of grails and one question has come to our mind.

We have some domain classes with relationships to others. My question is.. is there a way to hydrate (fill in the attributes of the domain with, for example, the params map) a domain object and its relationships?
For example, if I have these two domain classes:

User{
String name
String NIF
static belongsTo = [profile:Profile]
}

Profile {
String profileName
}

and I have a form like this one:

<g:form name="userForm" action="save">
<g:textField name="name" value="${userInstance?.name}"/>
<g:textField name="NIF" value="${userInstance?.NIF}"/>
<g:textField name="profile.profileName" value="${userInstance?.profile?.profileName}"/>
<g:submitButton name="create" value="Create"/>
</g:form>

could we in the controller just write this to save the user and the profile?

def save = {
def userInstance = new User(params)
user.save()
...
}

Many thanks.

pledbrook
Nov 18th, 2010, 02:29 PM
Yes, that should work. Is it not?

Saioa
Nov 19th, 2010, 02:16 AM
I have tried with the following code and it doesn't work.

Gives this error:
org.hibernate.PropertyValueException: not-null property references a null or transient value: pruebadomain.User.profile
at $Proxy11.saveOrUpdate(Unknown Source)
at pruebadomain.UserController$_closure4.doCall(prueb adomain.UserController:27)
at pruebadomain.UserController$_closure4.doCall(prueb adomain.UserController)
at java.lang.Thread.run(Thread.java:619)


def create = {
def userInstance = new User()
userInstance.properties = params
def profileInstance = new Profile()
userInstance.profile = profileInstance
return [userInstance: userInstance]
}

def save = {
def userInstance = new User(params)
if (userInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), userInstance.id])}"
redirect(action: "show", id: userInstance.id)
}
else {
render(view: "create", model: [userInstance: userInstance])
}
}


and in the view

<g:form action="save" >
<div class="dialog">
<table>
<tbody>

<tr class="prop">
<td valign="top" class="name">
<label for="NIF"><g:message code="user.NIF.label" default="NIF" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: userInstance, field: 'NIF', 'errors')}">
<g:textField name="NIF" value="${userInstance?.NIF}" />
</td>
</tr>

<tr class="prop">
<td valign="top" class="name">
<label for="name"><g:message code="user.name.label" default="Name" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: userInstance, field: 'name', 'errors')}">
<g:textField name="name" value="${userInstance?.name}" />
</td>
</tr>

<tr class="prop">
<td valign="top" class="name">
<label for="profile"><g:message code="user.profile.label" default="Profile" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: userInstance, field: 'profile', 'errors')}">
<g:textField name="profile.profileName" value="${userInstance?.profile?.profileName}"/>
</td>
</tr>

</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>

pledbrook
Nov 19th, 2010, 02:52 AM
OK, I see what the problem is. In order to do this, you need the save to cascade from the user to the profile. You enable cascading saves via the belongsTo property, which you have but on the wrong class. If you're saving User, then Profile should have a belongsTo to User.

With the current relationships, you need to bind data to Profile and save that.

Hope that helps.

Saioa
Nov 19th, 2010, 03:27 AM
You're right, thanks.

I have changed my domain classes to the following:

class Profile {
String profileName

static belongsTo = [user:User]
static constraints = {
}
}

class User {
String name
String NIF
Profile profile

static constraints = {
}
}

This works, but I had to add the profile field in the user domain to be able to create the user and the profile as I said in my last post. Is this ok?

pledbrook
Nov 19th, 2010, 03:54 AM
Yes, that's fine. I actually recommend using the hasOne property for one-to-one relationships:


class User {
...
static hasOne = [ profile: Profile ]
...
}

Saioa
Nov 19th, 2010, 03:57 AM
ok, I'll follow your recomendation for one-to-one relationships.

Thanks for the help :)