How to access properties of a command object from another command object? - grails

I am currently working on a grails application. I have two command objects (AccountInfoCommand and EducInfoCommand) in a controller. On my EducInfoCommand, I wanted to check if the yearGraduated property is earlier than the set birthDate(a property of AccountInfoCommand) on its validator constraints. How will I do that?
This is my code for my AccountInfoCommand:
class AccountDetailsCommand implements java.io.Serializable {
String username
String password
String confirmPassword
String emailAddress
Date birthDate
}
This is my code for EducInfoCommand:
class EducInfoCommand implements java.io.Serializable {
Integer graduated
EducationLevel educationLevel
String schoolName
String yearGraduated
String honorsReceived
}
static constraints = {
yearGraduated nullable: false, maxSize:4, blank: false, matches: /([0-9]*)/,
validator: {
Date.parse('yyyy',it) <= new Date() ? true : false
}
}
Please help!
Thanks!

You need some reference to which AccountDetails the EducInfo is for. For example, if you added a username field to the EducInfoCommand you could look up the account details from that (assuming there is an AccountDetails gorm object which is similar to the command object):
class EducInfoCommand implements java.io.Serializable {
String yearGraduated
String username
// ...
}
static constraints = {
yearGraduated nullable: false, maxSize:4, blank: false, matches: /([0-9]*)/,
validator: { val, obj ->
Date.parse('yyyy',val) > AccountDetails.findByUsername(obj.username).birthDate
}
}

Related

Convert Sql Query to Grails/Gorm query

How do I convert the following SQL query to Grails/Gorm? Can this be done with a basic query? I would like to avoid using Criteria, Projections and HQL to keep it consistent with the structure of the other queries in my code base (which are basic queries).
SELECT dealer_group_user_dealer_users_id, COUNT(user_id)
FROM db.dealer_group_user_user
GROUP BY dealer_group_user_dealer_users_id;
And is it possible to perform the query in the gsp page to display the result for a specific user_id as opposed to running the query in the controller?
To update from a comment made, below is my domain classes.
class DealerGroupUser extends User{
static hasMany = [dealerUsers: User]
static constraints = {
}
}
class User {
transient authService
Boolean active = true
String firstName
String lastName
String title
String username
String emailAddress
String passwordHash
Date lastLoginTime
Date dateCreated
Date lastUpdated
Retailer dealer
Client client
Date passwordUpdated
Long dealerUser
Boolean isReadOnlyClientManager = false
String regionClientManager
// Transient properties
String fullName
String password
static transients = ['fullName', 'password']
static mapping = {
//permissions fetch: 'join'
sort firstName: 'asc' // TODO: Sort on fullName
}
static hasMany = [roles: Role, permissions: String]
static constraints = {
// NOTE: If a username is not provided, the user's email address will be used
firstName maxSize: 30, blank: false
lastName maxSize: 30, blank: false
title maxSize: 50, blank: false, nullable: true
username blank: false, unique: true
emailAddress email: true, unique: false, blank: false
passwordHash blank: false
lastLoginTime nullable: true
active nullable: true
dealer nullable: true
client nullable: true
passwordUpdated nullable: true
dealerUser nullable: true
regionClientManager nullable: true
}
void setEmailAddress(String emailAddress) {
if (EmailValidator.instance.isValid(emailAddress)) {
this.emailAddress = emailAddress
if (!username) {
username = emailAddress
}
}
}
static namedQueries = {
dealerGroupUsers {
eq 'class', 'com.db.torque.DealerGroupUser'
}
}
Integer setPassword(String plainTextPassword) {
plainTextPassword = plainTextPassword?.trim()
if (plainTextPassword) {
if (!plainTextPassword.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=\\S+\$).{8,}\$")){
return -1
}
String previousPassword = this.passwordHash
String newPassword = authService.encrypt(plainTextPassword)
if (previousPassword != newPassword) {
this.passwordHash = newPassword
return 1
}
else {
return -2
}
}
return -1
}
#Transient
public static List<User> findAllByRolesContains(Role role) {
return User.executeQuery("""
SELECT u
FROM User as u
WHERE :role IN elements(u.roles)
""", [role: role])
}
String fullName() {
return "${firstName} ${lastName}"
}
String toString() {
return fullName()
}
}
SELECT dealer_group_user_dealer_users_id, COUNT(user_id)
FROM db.dealer_group_user_user
GROUP BY dealer_group_user_dealer_users_id;
In your user class you have :
Retailer dealer
So lets start from the beginning what is query doing, listing users from user then getting a count of how many times user appears on Retailer domain class ?
The best way to do this would
be
String query = """ select
new map(
u.id as userId,
(select count(d) from DealerGroupUser d left join d.dealerUsers du where du=u) as userCount
)
From user u order by u.id
"""
def results = User.executeQuery(query,[:],[readOnly:true]
This should do what you are doing I think.
And is it possible to perform the query in the gsp page to display the
result for a specific user_id as opposed to running the query in the
controller?
Views are presentation layer and hardcore work should be kept out of it - if needed to use a TagLib call.
Controllers although used in grails examples and rolled out as part of defaults to make things easier also not the best place. You should be doing that in a service which is injected in a controller and presented in view as the actual model of what is needed.
That is the proper way - GSP have a runtime size - keep it short, keep it sweet

Grails domain hasMany validation

I have a case that validation is done on domain properties but not on the an associated (hasMany) properties.
Is there any configuration I can add to enable the validation on both properties (domain and hasMany).
grails version : 3.1.14
Example:
class Person {
String name;
static hasMany = [location: Location]
static constraints = {
name nullable: true
}
}
class Location {
String address
String city
State state
String zip
static constraints = {
address nullable: true
}
}
According to the documentation the validation should work for has-many associations as you wish: http://docs.grails.org/3.1.14/ref/Domain%20Classes/validate.html
But in my test's it does not work eather.
An other solution is to work with the constraints:
static constraints = {
name nullable: true
location validator: {val, obj ->
val.every { it.validate() } ?: 'invalid'
}
}

How to have multiple One-to-One relationships between three domain classes

As a follow up question to this, I want to have a User domain class which has an optional one-to-one relationship with BasicProfile domain class, User being the owner which may or may not have a profile. I have this part figured out. What I also want is an optional one-to-one relationship between the AcademicProfile domain class and the User, AcademicProfile being the owner, such that an AcademicProfile may or may not have a User. When I try to replicate this the way I did the first one-to-one relationship, it does not work. Here are my classes.
class User {
String username
String password
String email
AcademicProfile academicProfile
Date dateCreated
Date lastUpdated
static hasOne = [basicProfile: BasicProfile]
static constraints = {
username size: 3..20, unique: true, nullable: false, validator: { _username ->
_username.toLowerCase() == _username
}
password size: 6..100, nullable: false, validator: { _password, user ->
_password != user.username
}
email email: true, blank: false
basicProfile nullable: true
}
}
class BasicProfile extends Profile {
User user
Date dateCreated
Date lastUpdated
}
class AcademicProfile extends Profile {
String dblpId
String scholarId
String website
Date dateCreated
Date lastUpdated
static hasOne = [user: User]
static hasMany = [publications: Publication]
static constraints = {
dblpId nullable: true
scholarId nullable: true
website nullable: true, url: true
publications nullable: true
user nullable: true
}
}
class Profile {
String firstName
String middleName
String lastName
byte[] photo
String bio
static constraints = {
firstName blank: false
middleName nullable: true
lastName blank: false
photo nullable: true, maxSize: 2 * 1024**2
bio nullable: true, maxSize: 500
}
static mapping = {
tablePerSubclass true
}
}
When I run it, I get the error: Field error in object 'org.academic.User' on field 'academicProfile': rejected value [null];. I don't understand what I am doing wrong.
You'll have to add nullable:true constraint for AcademicProfile academicProfile in User class as you mentioned that you need an 'optional' relationship between AcademicProfile & User.
The error itself is self explanatory though, that you can't create a User class's instance, without providing the academicProfile property a non-null value.

Relationships between Grails domain classes with inheritance

I have the following class. In src/groovy,
class Profile {
String firstName
String middleName
String lastName
byte[] photo
String bio
}
The domain classes BasicProfile and AcademicProfile extend Profile.
class BasicProfile extends Profile {
User user
Date dateCreated
Date lastUpdated
static constraints = {
firstName blank: false
middleName nullable: true
lastName blank: false
photo nullable: true, maxSize: 2 * 1024**2
bio nullable: true, maxSize: 500
}
static mapping = {
tablePerSubclass true
}
}
class AcademicProfile extends Profile {
User user
String dblpId
String scholarId
String website
Date dateCreated
Date lastUpdated
static hasMany = [publications: Publication]
static constraints = {
importFrom BasicProfile
dblpId nullable: true
scholarId nullable: true
website nullable: true, url: true
publications nullable: true
}
static mapping = {
tablePerSubclass true
}
}
Then there is a Publication class.
class Publication {
String dblpId
String scholarId
String title
String description
Date publicationDate
int citations
Date dateCreated
Date lastUpdated
static belongsTo = [AcademicProfile]
static hasOne = [publisher: Publisher]
static hasMany = [academicProfiles: AcademicProfile]
static constraints = {
dblpId nullable: true
scholarId nullable: true
title blank: false, maxSize: 100
description nullable: true, maxSize: 500
publicationDate: nullable: true
academicProfiles nullable: false
}
}
Finally, I have a User class.
class User {
String username
String password
String email
Date dateCreated
Date lastUpdated
static hasOne = [basicProfile: BasicProfile, academicProfile: AcademicProfile]
static constraints = {
username size: 3..20, unique: true, nullable: false, validator: { _username ->
_username.toLowerCase() == _username
}
password size: 6..100, nullable: false, validator: { _password, user ->
_password != user.username
}
email email: true, blank: false
basicProfile nullable: true
academicProfile nullable: true
}
}
My questions are as follows.
I want a relationship where each User may optionally have a Profile (either BasicProfile or AcademicProfile). I tried static hasOne = [profile: Profile] but I got errors saying Profile does not agree to the hasOne relationship. So the current setup I have is a workaround. Is there no way a user can have one Profile be it BasicProfile or AcademicProfile?
Secondly, in the current setup, I get the error: Invocation of init method failed; nested exception is org.hibernate.MappingException: An association from the table academic_profile_publications refers to an unmapped class: org.academic.AcademicProfile when I try to run it. A Google search tells me that this is a problem with classes which are inheriting from other classes. So technically, if I don't have a hasMany relationship in Publication with AcademicProfile, it should work without any issues. But I don't want that. Because a publication has many authors (AcademicProfiles in my case) and an author may have many publications. So is there a way to fix this?
You're not using Hibernate inheritance - that requires that all of the classes be mapped. You're just using regular Java/Groovy inheritance where you inherit properties and methods from base classes. But Hibernate isn't aware of that, so it can't do queries on the unmapped base class.
I'm not sure why it's complaining about AcademicProfile, but it could be a secondary bug caused by the core issue.
I find Hibernate inheritance to be way too frustrating to use in most cases, so I use this approach when there is shared code.
It should work if you move Profile to grails-app/domain. Once you do that you should move the tablePerSubclass mapping config to the base class and only specify it once.

Grails searchable plugin with hasMany

I am using grails searchable plugin to search my domain classes. However, I cannot yet search by my hasMany (skills and interests) fields even though they are of the simple type String. This is my domain class:
class EmpactUser {
static searchable = [except: ['dateCreated','password','enabled','accountExpired','accountLocked','passwordExpired']]
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
String email
String firstName
String lastName
String address
String phoneNumber
String description
byte[] avatar
byte[] resume
Date dateCreated
static hasMany = [
skills : String,
interests : String, // each user has the ability to list many skills and interests so that they can be matched with a project.
]
static constraints = {
username blank: false, unique: true
password blank: false
email email: true, blank: false
firstName blank: false
lastName blank: false
description nullable: true
address nullable: true
avatar nullable: true, maxSize: 1024 * 1024 * 10
resume nullable: true, maxSize: 1024 * 1024 * 10
phoneNumber nullable: true, matches: "/[(][+]d{3}[)]d+/", maxSize: 30
}
}
This is the code I am using to search:
def empactUserList = EmpactUser.search(
searchQuery,
[reload: false, result: "every", defaultOperator: "or"])
Am I missing something?
Thanks,
Alan.
Searchable has trouble recognising hasMany relations with Strings. A workaround is to create a new domain object which "belongs to" the parent object, and make a transient variable in the parent class. The following code works as an alternate to the example given in documentation.
class Article {
static searchable = {
root true
keywords (component:true)
}
static transients = ['keywords']
Set<ArticleKeyword> getKeywords() {
ArticleKeyword.findAllByArticle(this) as Set
}
}
class ArticleKeyword {
static searchable = { root false}
static constraints = {
}
String text
static belongsTo = [article:Article]
static mapping = {
text type: 'text'
}
}

Resources