This example here is from the grails docs:
def emeaCriteria = {
eq "region", "EMEA"
}
def results = Airport.withCriteria {
emeaCriteria.delegate = delegate
emeaCriteria()
flights {
like "number", "BA%"
}
}
My webpage is passing back a checkbox group of ethnicities, returning the row ids. So what the server gets is:
ethnicity:[1, 4]
or if the user only picks one ethnicity:
ethnicity:4
def criteria = { params ->
//handle case where only one ethnicity is returned as just a string, not a list of strings
def list = params.ethnicty instanceof String ? [params.ethnicty] : params.ethnicity
if (list) {
inList('ethnicity', list)
}
}
I'm getting an error: java.lang.String cannot be cast to java.lang.Enum.
If I didn't have a list I think I could figure it out. The params are sending back string values, and they need to be converted to the enum class. But within the closure, how do you convert each entry into a list to the enum?
I figured it out through a combination of multiple website posts and with help from dmahapatro above.
def genderCriteria = {
if (params.gender) {
inList('gender', params.list('gender').collect { Gender.valueOf(it)} )
}
}
If a webpage passes back one or more enums (a single string or a list of strings) and you want criteria to check values from the list passed back, you have to provide a list of enum types (not strings or ints).
Here is my enum class for reference:
public enum Gender {
M('Male'),
F('Female'),
U('Unknown')
final String value
Gender(String value) {
this.value = value
}
public String toString() {
value
}
public String getKey() {
name()
}
public String getValue() {
value
}
}
And my criteria builder:
def c = MyDomain.createCriteria()
results = c.list {
genderCriteria.delegate = delegate
genderCriteria(params)
}
Even if no values are passed back for the gender field, it still works (because of the if statement in genderCriteria.
It may not be the best or cleanest solution, but it does work.
Related
Is there a built-in / easy way to set mappings between domain class properties and JSON strings that don't have exact matches for the property names?
For example, when I have a domain class:
class Person {
String jobTitle
String favoriteColor
static constraints = {
jobTitle(blank: false)
favoriteColor(blank: false)
}
}
And someone's giving me the following JSON:
{ "currentJob" : "secret agent", "the-color" : "red" }
I'd like to be able to still do this:
new Person(request.JSON).save()
Is there a way in groovy/grails for me to map currentJob -> jobTitle and the-color -> favorite color?
EDIT:
I've done a little experimenting, but I still haven't gotten it working. But I have found out a couple interesting things...
At first I tried overwriting the setProperty method:
#Override
setProperty(String name, Object value) {
if(this.hasProperty(name)) this[name] = value
else {
switch(name) {
'currentJob': this.jobTitle = value; break;
'the-color': this.favoriteColor = value; break;
}
}
}
But this doesn't work for two reasons: 1) setProperty is only called if there is a property that matches name and 2) "this[name] = value" calls setProperty, leading to an infinite recursive loop.
So then I thought, well screw it, I know what the incoming json string looks like (If only I could control it), I'll just get rid of the line that handles the scenario where the names match and I'll override hasProperty, maybe that will work:
#Override
void setProperty(String name, Object value) {
switch(name) {
'currentJob': this.jobTitle = value; break;
'the-color': this.favoriteColor = value; break;
}
}
#Override
boolean hasProperty(String name) {
if(name == "currentJob" || name == "the-color") return true
return false
}
But no, that didn't work either. By a random stroke of luck I discovered, that not only did I have to overwrite hasProperty(), but I also had to have an empty setter for the property.
void setCurrentJob(){ }
That hack worked for currentJob - I guess setProperty only gets called if hasProperty returns true and there is a setter for the property (Even if that setter is auto generated under the covers in grails). Unfortunately I can't make a function "setThe-Color" because of the dash, so this solution doesn't work for me.
Still stuck on this, any help would definitely be appreciated.
EDIT:
Overriding the void propertyMissing(String name, Object value){} method is called by this:
Person person = new Person()
person["currentJob"] = "programmer"
person["the-color"] = "red"
But not by this:
Person person = new Person(["currentJob":"programmer", "the-color":"red"])
I want to get the values of the enum property.
In activiti 5.13 userguide,
the enumeration values are accessible with formProperty.getType().getInformation("values")
In activiti docs,return type of getType() is FormType. But in my code getType() return type is String. So i couldn't call FormType's getInformation() method.
When i am using formProperty.getType().getInformation("values"), i got the following error.
Cannot cast object 'enum' with class 'java.lang.String' to class 'org.activiti.engine.form.FormType'.
How could i get the values of enum?
<userTask id="handleRequest" name="Handle vacation request" activiti:candidateGroups="management">
<documentation>${employeeName} would like to take ${numberOfDays} day(s) of vacation (Motivation: ${vacationMotivation}).</documentation>
<extensionElements>
<activiti:formProperty id="vacationApproved" name="Do you approve this vacation" type="enum" required="true">
<activiti:value id="true" name="Approve"></activiti:value>
<activiti:value id="false" name="Reject"></activiti:value>
</activiti:formProperty>
<activiti:formProperty id="managerMotivation" name="Motivation" type="string"></activiti:formProperty>
</extensionElements>
</userTask>
Consider above user Task and You can do like this
//passing Task Id and Process definition Id
def getFormForTask(taskId,pdId) {
RepositoryService repositoryService =processEngine.getRepositoryService()
// getting model
BpmnModel model = repositoryService.getBpmnModel(pdId);
// getting list process from model including tasks
List<Process> processes = model.getProcesses()
List<FormProperty> taskFormPropertyList =[]
for(Process proc : processes) {
for(Object obj : proc.getFlowElements()) {
// need form Property only for User Task
if(obj instanceof UserTask) {
UserTask userTask = (UserTask)obj
if(userTask.getId() == taskId){
// getting list of Form Property from task that matches taskId
taskFormPropertyList = userTask.getFormProperties()
}
}
}
}
// loop through from Property
taskFormPropertyList.each {
// getFormValues() returns Property values for the form Property (like enum)
def fvlist = it.getFormValues()
fvlist.each {
// prints id, in this case true and false
println it.getId()
// prints name, in this case Approve and Reject
println it.getName()
}
}
}
I have a request to create a form filter that has two fields, one a freeform text
and the other a select. The value in the select will determine how to handle the value of the text is turned into a criteria.
I can create a custom addXXXColumnCriteria for either field, but how can I access the other field from within this function?
I suggest you not to use de addXXXColumnCriteria, but overwrite the FormFilter doBuildCriteria (Propel) or doBuildQuery(Doctrine) methods.
I have never used Propel, but I guess that works as good as for Doctrine.
For example:
class yourPropelFormFilter extends anyKindOfSfFormFilterPropel {
public function doBuildCriteria(array $values) {
$criteria = parent::doBuildCriteria($values);
// ... change the criteria behaviour with the $values array (do some print_r to $values to see how the data array is formatted)
return $criteria;
}
}
For Doctrine (remember to use the getRootAlias query method):
class yourDoctrineFormFilter extends anyKindOfSfFormFilterDoctrine {
public function doBuildQuery(array $values) {
$q = parent::doBuildQuery($values);
$rootAlias = $q->getRootAlias();
if(...) {
$q->innerJoin($rootAlias.'.RelationX rx')
->addWhere('rx.value = ?',$values['...']);
}
return $q;
}
}
Please, remember to return the criteria/query modified object!
I have a custom toString method in my enum:
enum TaxRate implements Serializable {
RATE23(23.0),
...
private String s
private BigDecimal rate
private TaxRate(BigDecimal s) {
this.s = s + "%"
this.rate = s * 0.01
}
public String toString() {
return s
}
Now when I display the rates in HTML I get nice output like TAX: 23.0%.
But what happens when a user selects the tax from a <select> and the sent value is i.e. 23.0% is that Grails can't create/get the TaxRate instance...
What should I override to support this custom mapping? Trying to override valueOf(String) ended with a error..
Have you seen the entry at the bottom of this page?
If you want to use a Enum with a "value" String attribute (a pretty common idiom) in a element, try this:
enum Rating {
G("G"),PG("PG"),PG13("PG-13"),R("R"),NC17("NC-17"),NR("Not Rated")
final String value
Rating(String value) { this.value = value }
String toString() { value }
String getKey() { name() }
}
Then add optionKey="key" to your tag.
Assume a domain class called User. User class looks like this:
class User {
List<String> values
}
The collection values contains strings like "http://example.com/x", "http://google.com/y", "http://google.com/z" and so on...
Let's say we want to build a query which gets all the users that have specific string in the collection values (e.g. "google.com"). Something like this:
def criteria = User.createCriteria()
def results = criteria.listDistinct () {
and {
user.values.each { like('someField', '%${it}%') }
}
}
Any ideas?
I have found the answer by experimentation. The solution is:
def criteria = User.createCriteria()
def results = criteria.listDistinct () {
and {
user.values.each { like('someField', '%'+it+'%') }
}
}
I am not sure what you are doing with your suggested answer.
I have never seen that usage of each in the criteria query before.
This question has been asked many times before but never given an answer.
The problem is that you are queriyng a String association, which is not a domain class. If you would make your own String domain class for example ondrej.String { String strValue } then you would be able to do :
User.withCriteria {
values { ilike("strValue", "...") }
}
The problem is not having access to the value of the String object. The value of the String class is called value, but it is a char array, so I do not believe the following will work:
User.withCriteria {
values { ilike("value", "...") }
}
You could try using :
User.withCriteria {
values { ilike("toString", "...") }
}
or something else instead of toString ... I do not have the possibility to test this right now.
Hope that helps
After a lot of trying and researching, I found this will work with Grails 2.4.0, I don't know about older versions.
Cat.withCriteria {
createAlias('nicknames', 'n')
ilike 'n.elements', '%kitty%'
}
The trick is to use 'n.elements'