at the moment I try to deal with scaffolding in grails. There I have a little problem with the generation-templates where i don't find a solution. I want to configure the generating-algorithm in the domainmodel.
My thought was to define static variables in the model which aren't created in the database and only important for the generation-process.
For example I want to display only some special fields in the show.gsp but i want to display every field in the _form.gsp Or i want to do some gsp-imports but only in seperate gsp.
So i thought that i could define some static variables where the value contains some configuration-parameter i can interpretate in the generation template.
I hope everybody understand what I mean ?
Here is an example:
class Awesome {
Long awesome_level
Long person_name
Boolean itsMe
static String showFields
static transients = ['showFields']
static constraints = {
einrichtungs_type()
type_of_conzept()
anzahl_gruppen()
anzahl_kinder_pro_Gruppe()
offnungszeiten()
showFields(["person_name", "itsMe"])
}
In the Show-View i only want to display the fields in the array "showFields"
...
for (p in props) {
if (p.embedded && p.name in domainClass.showFields) {
def embeddedPropNames = p.component.persistentProperties*.name
def embeddedProps = p.component.properties.findAll { embeddedPropNames.contains(it.name) && !excludedProps.contains(it.name) }
Collections.sort(embeddedProps, comparator.constructors[0].newInstance([p.component] as Object[]))
%><fieldset class="embedded"><legend><g:message code="${domainClass.propertyName}.${p.name}.label" default="${p.naturalName}" /></legend><%
for (ep in p.component.properties) {
renderFieldForProperty(ep, p.component, "${p.name}.")
}
%></fieldset><%
} else {
renderFieldForProperty(p, domainClass)
}
...
I know that the if clause don't work. My problem is, that i am not able to get the value of the field "showFields".
Know my questions:
Is it able to receive the values of a field of a domainclass?
Is it able to execute a methode of a domainclass?
Is there an other way do define configuration-parameters which i can access in the generation-templates?
I hope i was able to display my problem and thank you for some help!
Greetz
V
I found a solution for that problem. First of all I thought it could be possible with the creation of a custom-Constraint. I am still thinking that this is also possible but i found a better /easyer way to add "configurations".
I use the tag attributes, which already exsits. If i understand it right, the attributes parameter i used to add attributes in select-Html-tags. Now i use it for adding some configuration-parameters. Here my solution:
You have to change the "renderEditor.template" that not all the configuration-parameter will add in a HTMl-tag as a attribute. cp.attributes.each { k, v ->
sb << "${k}=\"${v}\" "
} change to cp.attributes?.realAttributes.each { k, v ->
sb << "${k}=\"${v}\" "
}
When you realy want to this attributes-function add into the attributes-parameter the value "realAttributes" (or how you would like to call it)
Change the constraints of the model: (in my example:)
static constraints = { einrichtungs_type(attributes:
[showField:true])
}
Finaly change the generation-template
if(cp.attributes?.showField){
...
I hope these 4 steps help you, if you have nearly the same problems.
Greetz
V
Related
I have a domain class
class Url {
UUID id
String url
static hasMany = [
indications:UrlIndication
]
...
}
And
class UrlIndication {
UUID id
String name
static belongsTo = Url
...
}
I want to choose urls so that it has all the necessary UrlIndication elements in a given list indicationsId.
For that I use an association and criteria like this one:
indications {
and {
indicationsId.each{
indication->
eq ('id',UUID.fromString(indication as String))
}
}
}
However, all I got is an empty result. Can you suggest any modifications/ other methods so that I can do this? Thanks in advance
Your query returned an empty list because it's the equivalent of the expression (pseudo-code): if 1 = 1 and 1 = 2 and 1 = 3
Such an expression would always be false. in or inList would not work for the reason #innovatism described.
In theory, Criteria's eqAll() or HQL's = ALL would work. But, I don't know for sure because I could not get either one to work.
What will work is to use inList to return a subset of Urls: those which contain at least one of the UrlIndication IDs. Then use Groovy's containsAll() to finish the job.
def ids = indicationsId.collect { UUID.fromString(it as String) }
Url.createCriteria()
.buildCriteria {
indications {
inList 'id', ids
}
}
.setResultTransformer(org.hibernate.Criteria.DISTINCT_ROOT_ENTITY)
.list()
.findAll {
it.indications.id.containsAll(ids)
}
Since the query has the potential to return duplicate Url instances, the ResultTransformer is set to return a unique list.
Finally, findAll() is used along with containsAll() to filter the list further.
Using eqAll (maybe)
Something like the following might work. Something funky is going on with Grails' HibernateCriteriaBuilder that causes the eqAll method to look up properties in the root entity; completely ignoring the sub criteria. So the following uses Hibernate directly. It didn't work for me, but it's as close as I could get. And it gave me a head-ache!
Url.createCriteria().buildCriteria {}
.createCriteria('indications', 'i')
.add(org.hibernate.criterion.Property.forName('i.id').eqAll(org.hibernate.criterion.DetachedCriteria.forClass(UrlIndication)
.add(org.hibernate.criterion.Restrictions.in('id', ids))
.setProjection(org.hibernate.criterion.Property.forName('id'))
))
.setResultTransformer(org.hibernate.Criteria.DISTINCT_ROOT_ENTITY)
.list()
The problem I had is I could not get Restrictions.in to work. Restrictions.eq works fine.
the in clause should do:
indications {
'in' 'id', indicationsId.collect{ UUID.fromString indication.toString() }
}
For example I need to retrieve several registers in a table, and edit a field, but it takes too long to save all with a loop, does exist a better way to save?
This how I do it....
class Table
static mapping = {
table "TABLEEX"
id generator:'sequence', params:[sequence:'TABLEEX_SEQ']
}
// identificacion
String data1
String data2
}
And searching the data:
def stuff = Table.createCriteria().list{
eq("data1","1")
}
And editing and saving
stuff.each {
it.data2 = "aaa"
it.save()
}
It isn't clear why you are retrieving the objects to begin with. Is something like this what you are looking for?
Table.executeUpdate("update Table t set t.data2=:newData where t.data1=:oldData", [newData: 'BAR', oldData: 'FOO'])
EDIT
You could also do something like this...
def query = Table.where {
data1 == 'FOO'
}
int total = query.updateAll(data2:'BAR')
Hibernate (the underlying mechanism of gorm, the grails orm) does not support that.
You'll have to iterate over every element and save or implement it yourself (and that will not make it faster).
The next code works:
self query1 = DomainClassExample.findAllByPropertyInList("hi","bye")
But if I add Not, the dynamic finder does not exist (it DOES exists: check the answer):
self query2 = DomainClassExample.findAllByPropertyNotInList("hi","bye")
I want to get all the DomainClassExample which don't have the property "hi" or "bye". Is there any dynamic finder for that purpose? Should I use the Where Query. How?
First, a minor correction. It's not a method expression. It's a dynamic finder. Now, as to your actual problem, you'll need to use the criteria API for this...
def c = DomainClassExample.createCriteria()
def matchingProperties = c.list {
property {
not { 'in'(["hi","bye"]) }
}
}
You might run into an issue with the word 'property' and I haven't actually tested the code I just wrote. But that is the gist of it.
In Grails 1.3.9, given the following domain:
class User {
String username
....
}
this works in our application without any issues:
def userList = User.findAllByUsernameNotInList(["user1#testdomain.com","user2#anotherdomain.com"])
I have a simple Tag class with only two fields, name and value,
class Tag {
String name
String value
}
and I'm trying to render an XML where I want to search for parts of both parameters via findBy...Ilike().
def getXml = {
render Tag.findAllByNameAndValueIlike("%${params.name}%", "%${params.value}%") as XML
}
But this doesn't give my any results. If I use only one parameter, it works as I expect:
def getXml = {
render Tag.findAllByNameIlike("%${params.name}%") as XML
}
My next question is probably going to be about filtering the results, and adding other "similar" tags to the returns list, so is there a way to solve the above with something like:
def getXml = {
list = Tag.findAllByNameIlike("%${params.name}%")
list.add(Some other stuff)
list.sortBy(Some thing, maby name length)
}
For your multiple-field ilike query you can use withCriteria:
def result = Tag.withCriteria {
ilike('name', "%${params.name}%")
ilike('value', "%${params.value}%")
}
This will return a list of Tag domains whose name matches the provided name and value matches the provided value.
The Criteria DSL will probably let you do most of the filtering you need, but you can also consider using some of the Groovy collection examples here.
You have to put the restrictions(InList, NotNull, etc) on each field of a dynamic finder. If you do not, it assumes equals. Here is what you were looking for:
Tag.findAllByNameIlikeAndValueIlike("%${params.name}%", "%${params.value}%")
Both answer are good. I tried both, but I have to say I like the withcCritia the best. It seems very flexibly.
def result = Tag.withCriteria {
if(params.name != null)
ilike('name', "%${params.name}%")
if(params.value != null)
ilike('value', "%${params.value}%")
}
result.add(new Tag('name': "something"))
render result as XML
My problem is simple but I could not find any GORM syntax for this.
Consider the following class:
class Article {
String text
static hasMany = [tags: String]
static constraints= {
tags(unique: true) //NOT WORKING
}
}
I want to have one unique tag name per article defined in my constraints but I cannot make it with the above syntax.
Clearly I need in DB schema something like:
create table article_tags (article_id bigint, tags_string varchar(255), unique (article_id , tags_string))
How can I do that?
PS: I am also stuck for setting constraints on tag minimum and maximum size
FYI, you can also use a custom validator in domain classes:
static constraints = {
tags(validator: {
def valid = tags == tags.unique()
if (!valid) errors.rejectValue(
"tags", "i18n.message.code", "default message")
return valid
})
At the database level, you can customize DDL generation by having the following code in grails-app/conf/hibernate/hibernate.cfg.xml:
<hibernate-mapping>
<database-object>
<create>
ALTER TABLE article_tags
ADD CONSTRAINT article_tags_unique_constraint
UNIQUE(article_id, tags_string);
</create>
<drop>
ALTER TABLE article_tags
DROP CONSTRAINT article_tags_unique_constraint;
</drop>
</database-object>
</hibernate-mapping>
Initially I looked at the joinTable mapping to see if it would support a unique key, but it won't.
The best solution I can think of is the following combination:
Manually run the SQL statement to add the unique constraint. If you have some sort of database management tool (e.g. Liquibase), that would be the ideal place.
Explicitly declare the association as a Set. This should avoid Hibernate ever running into the unique constraint, anyway.
class Article {
static hasMany = [tags: String]
Set<String> tags = new HashSet<String>()
}
An alternate solution would be to explicitly declare your child domain (Tag) and set up a many-to-many relationship, adding the unique key to the join table there using constraints. But that's not really a great solution, either. Here's a primitive example:
class Article {
static hasMany = [articleTags: ArticleTag]
}
class Tag {
static hasMany = [articleTags: ArticleTag]
}
class ArticleTag {
Article article
Tag tag
static constraints = {
tag(unique: article)
}
}
With this, though, you have to explicitly manage the many-to-many relationship in your code. It's a bit inconvenient, but it gives you full control over the relationship as a whole. You can find out the nitty gritty details here (the Membership class in the linked example is akin to the ArticleTag in mine).
Perhaps one of the gurus more familiar with GORM will chime in with a more elegant solution, but I can't find anything in the docs.
EDIT: Note that this approach does not consider a unique(article_id , tags_id) constraint. It also raises an issue with two Articles having the same tags. - Sorry.
While this is not officially documented (see the relevant parts of the Grails Reference Documentation here and here) constraints on one-to-many associations are simply ignored by GORM. This includes unique and nullable constraints, and probably any.
This can be proved by setting dbCreate="create" and next, by looking at the database schema definition. For your Article sample and the PostgreSQL database, this would be:
CREATE TABLE article_tags
(
article_id bigint NOT NULL,
tags_string character varying(255),
CONSTRAINT fkd626473e45ef9ffb FOREIGN KEY (article_id)
REFERENCES article (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT article0_tags_article0_id_key UNIQUE (article_id)
)
WITH (
OIDS=FALSE
);
As can be seen above, there are no constraints for the tags_string column.
In contrast to constraints on association fields, constraints on "normal" instance fields of domain classes do work as expected.
Thus, we'll want to have some kind of Tag, or TagHolder, domain class, and we'd need to find a pattern that still provides the Article with a clean public API.
First, we're introducing the TagHolder domain class:
class TagHolder {
String tag
static constraints = {
tag(unique:true, nullable:false,
blank:false, size:2..255)
}
}
and associate it with the Article class:
class Article {
String text
static hasMany = [tagHolders: TagHolder]
}
In order to provide a clean public API, we're adding the methods String[] getTags(), void setTags(String[]. That way, we can also call the constructor with named parameters, like, new Article(text: "text", tags: ["foo", "bar"]). We're also adding the addToTags(String) closure, which mimicks GORM's corresponding "magic method".
class Article {
String text
static hasMany = [tagHolders: TagHolder]
String[] getTags() {
tagHolders*.tag
}
void setTags(String[] tags) {
tagHolders = tags.collect { new TagHolder(tag: it) }
}
{
this.metaClass.addToTags = { String tag ->
tagHolders = tagHolders ?: []
tagHolders << new TagHolder(tag: tag)
}
}
}
It's a workaround, but there's not too much coding involved.
A drawback, we're getting an additional JOIN table. Nevertheless, this pattern allows for applying any available constraints.
Finally, a test case could look like this one:
class ArticleTests extends GroovyTestCase {
void testUniqueTags_ShouldFail() {
shouldFail {
def tags = ["foo", "foo"] // tags not unique
def article = new Article(text: "text", tags: tags)
assert ! article.validate()
article.save()
}
}
void testUniqueTags() {
def tags = ["foo", "bar"]
def article = new Article(text: "text", tags: tags)
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
void testTagSize_ShouldFail() {
shouldFail {
def tags = ["f", "b"] // tags too small
def article = new Article(text: "text", tags: tags)
assert ! article.validate()
article.save()
}
}
void testTagSize() {
def tags = ["foo", "bar"]
def article = new Article(text: "text", tags: tags)
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
void testAddTo() {
def article = new Article(text: "text")
article.addToTags("foo")
article.addToTags("bar")
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
}
Try this:
http://johnrellis.blogspot.com/2009/09/grails-constraints-across-relationships.html
The only way I've found to do this is to write a custom constraint and do a database check for the duplication. I don't think there is a built-in way to use a GORM constraint to accomplish this.