Update HasMany Relation in Grails Controller - grails

I have the following grails domain classes:
A {
belongsTo = [b: B]
}
B {
hasMany = [aClasses: A]
}
In a GSP view I have a form where the user can select many A instances to connect them with a B instance. When the user submits the form the Controller which handles the form receives something like this:
params: [aClasses: [123,124]]
The user may have removed previously added aClasses from B so I tried to clear the aClasses of B first and then perform addTo but I get a java.util.ConcurrentModificationException.
Here is what I do in my Controller Action:
def update() {
B b = B.get(params.id)
b.properties = params
b.aClasses.clear()
def newAs = params.aClassIds ? [] + params.aClasses : []
newAs.each {
A a = A.get(it)
b.addToAClasses(a)
}
b.save(flush: true)
}
Is there a way to make this work?

Is this running in a service or controller? I've done something similar before but the logic was in a service and it worked. This kind of database access should run in a service because they are transactional.
The example that worked is :
if(!clone.hasErrors())
{
def projectTasks = Task.withCriteria {
project {
eq('id', project_id.toInteger())
}
}
projectTasks.each{ task ->
clone.addToTasks(task)
}
}
clone.save(flush : true)

Related

grails unknown number of model objects in the view

Working in Grails 2.2
I have a situation where I need to be able to handle an unknown number of CommitteeMembers in the view. These need to be both created and displayed.
Each one has the usual attributes - name, address, contact information, userid.
I understand that if I name form fields the same name, Grails will return a collection for me to iterate over. In this case, however, I am faced with this situation:
cm_firstname
cm_lastname
cm_address
cm_email
cm_userid
So does this mean I will be given collections of each of these fields? That is not as useful as there is no way to corelate the various firstnames with the correct lastnames, etc.
I am enjoying Grails and am looking forward to your feedback.
You can use Grails Command objects to do this work for you. Here's an example in a SO question. Basically you will have a single collection of CommitteeMembers that will be populated in your controller thorugh data binding.
As #Gregg says, in the view you need the fields to have an index.
class MyDomain {
String name
}
class MyDomainCommand {
List<MyDomain> instances = ListUtils.lazyList([], FactoryUtils.instantiateFactory(MyDomain))
}
class MyController {
def save() {
MyDomainCommand command = new MyDomainCommand()
bindData(command, params, [include: 'instances'])
}
}
I'll tell you what I do, which may or may not be the best option. I do this mainly because I don't like data binding.
For your case as an example, I would name my fields: "cm.firstName, cm.lastName, cm.address, cm.email, cm.userId".
If you are in a service:
GrailsWebRequest webUtils = WebUtils.retrieveGrailsWebRequest()
List committeeMembers = [].withDefault {new GrailsParameterMap([:], webUtils.getCurrentRequest())}
In a controller:
List committeeMembers = [].withDefault {new GrailsParameterMap([:], request)}
Then
params.cm.each { k, v ->
if (v instanceof String[]) {
v.eachWithIndex { val, idx ->
committeeMembers[idx]."$k" = val
}
}
else {
committeeMembers[0]."$k" = v
}
}
Then you can do:
committeeMembers.each {
<Create from it.firstName, it.lastName, etc>
}

Grails: querying hasMany association

I know there are several questions on this subject but none of them seem to work for me. I have a Grails app with the following Domain objects:
class Tag {
String name
}
class SystemTag extends Tag {
// Will have additional properties here...just placeholder for now
}
class Location {
String name
Set<Tag> tags = []
static hasMany = [tags: Tag]
}
I am trying to query for all Location objects that have been tagged by 1 or more tags:
class LocationQueryTests {
#Test
public void testTagsQuery() {
def tag = new SystemTag(name: "My Locations").save(failOnError: true)
def locationNames = ["L1","L2","L3","L4","L5"]
def locations = []
locationNames.each {
locations << new Location(name: it).save(failOnError: true)
}
(2..4).each {
locations[it].tags << tag
locations[it].save(failOnError: true)
}
def results = Location.withCriteria {
tags {
'in'('name', [tag.name])
}
}
assertEquals(3, results.size()) // Returning 0 results
}
}
I have validated that the data is being created/setup correctly...5 Location objects created and the last 3 of them are tagged.
I don't see what's wrong with the above query. I would really like to stay away from HQL and I believe that should be possible here.
Welcome to hibernate.
The save method informs the persistence context that an instance should be saved or updated. The object will not be persisted immediately unless the flush argument is used
if you do not use flush it does the saves in batches so when you setup your query right after the save it appears that the data is not there.
you need to add
locations[it].save(failOnError: true, flush:true)
You should use addTo* for adds a domain class relationship for one-to-many or many-to-many relationship.
(2..4).each {
locations[it].addToTags(tag)
}

Grails update instead of delete

Is there an easy way in Grails to not allow deleting for any Domain Class? And rather have a delete flag in each domain which gets updated whenever something is deleted.
Also, in effect all the list/show methods should not show objects where delete flag is true.
I know I can do that by manually editing all my CRUD methods in all the controllers but that seems a little bit too much work when working with Grails where everything can be done by changing some flag somewhere!!
My usual list method looks like following, almost all the list methods in my project lets user access things which only belongs to users' company.
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
def documentsList = Documents.createCriteria().list(params){
eq("company.id",session.companyId)
maxResults(params.max)
order("dateCreated","desc")
//firstResult(params.offset)
}
[documentsInstanceList: documentsList , documentsInstanceTotal: documentsList.getTotalCount() ]
}
You'll have to ovveride the delete and list methods of all your domain classes. Add code like this to your Bootstrap
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (dc in grailsApplication.domainClasses) {
dc.clazz.exists(-1); //to register meta class
def gormSave = dc.clazz.metaClass.getMetaMethod('save');
dc.clazz.metaClass.delete = { ->
delegate.deleted = true
gormSave.invoke delegate
}
dc.clazz.metaClass.delete = { Map args ->
delegate.deleted = true
gormSave.invoke(delegate, args)
}
dc.clazz.metaClass.static.list = { ->
def crit = delegate.createCriteria();
def list = crit.list{
eq('deleted', false)
}
return list;
}
}
}
def destroy = {}
}

Better way to discover relationship dynamically when saving a new record? (otherSide fails)

Given this relationship:
class A {
String name
static hasMany = [b:B]
}
class B {
String name
static belongsTo = [a:A]
}
I have an record b that I want to save. I've already discovered via working Grails reflection (omitted in the code example below) that it needs to be an instance of class B. Beyond that, record b only knows:
it has a relation "a"
relation "a"'s key
Since it's a dynamic case, we do not know and must discover:
relation "a" is to an instance of class A (so we can call A.find(a's key))
the "other side" of the relation - class A's perspective - is relation "b" (so we can call .addToB(b))
So how do I save b to the database? Here's how I'm doing it:
class AssocTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
// I don't know this part, but it's in the DB
def a = new A(name:"al")
a.save()
}
void testAssociation() {
// I want to create a new B such that name="bob"
// I also had to discover "class B" using (working) Grails reflection
// but omitted it for this example.
def b = new B(name:"bob")
// ... and the relation is as "given" below
def given = [a:[name:"al"]]
// So I need to call A.find([name:"al"]).addToB(b). But "A" and
// "addToB" are unknown so need to be found via reflection
def gdc = new DefaultGrailsDomainClass(B)
given.each { give ->
def prop = gdc.getPropertyByName(give.key)
if (prop.isAssociation() && !prop.isOwningSide()) {
println "I want to use otherSide, but it's ${prop.otherSide}"
def os = reallyGetOtherSide(B, give)
def object = os.parent.find(
os.parent.newInstance(give.value))
object."${os.method}"(b)
}
}
def bFound = B.findByName("bob")
assertEquals "al", bFound.a.name
}
def reallyGetOtherSide(clazz, relation) {
def parent=clazz.belongsTo[relation.key]
def addTo=parent.hasMany.find { (clazz == it.value) }.key
[parent:parent, method:"addTo${addTo.capitalize()}"]
}
}
...with otherSide returning null, unfortunately. This can't be the best way to do this, can it?
If I understood you correctly, You can refer to these docs here. You can try the following:
`new A(name:"Gatwick")
.addToB(new B(name:"BA3430"))
.addToB(new B(name:"EZ0938"))
.save()`

Reading out all actions in a Grails-Controller

I need to read out all available actions from any controller in my web-app. The reason for this is an authorization system where I need to give users a list of allowed actions.
E.g.:
User xyz has the authorization for executing the actions show, list, search.
User admin has the authorization for executing the actions edit, delete etc.
I need to read out all actions from a controller. Does anyone has an idea?
This will create a List of Maps (the 'data' variable) with controller information. Each element in the List is a Map with keys 'controller', corresponding to the URL name of the controller (e.g. BookController -> 'book'), controllerName corresponding to the class name ('BookController'), and 'actions' corresponding to a List of action names for that controller:
import org.springframework.beans.BeanWrapper
import org.springframework.beans.PropertyAccessorFactory
def data = []
for (controller in grailsApplication.controllerClasses) {
def controllerInfo = [:]
controllerInfo.controller = controller.logicalPropertyName
controllerInfo.controllerName = controller.fullName
List actions = []
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(controller.newInstance())
for (pd in beanWrapper.propertyDescriptors) {
String closureClassName = controller.getPropertyOrStaticPropertyOrFieldValue(pd.name, Closure)?.class?.name
if (closureClassName) actions << pd.name
}
controllerInfo.actions = actions.sort()
data << controllerInfo
}
Here's an example that works with Grails 2, i.e it will capture actions defined as either methods or closures
import org.codehaus.groovy.grails.commons.DefaultGrailsControllerClass
import java.lang.reflect.Method
import grails.web.Action
// keys are logical controller names, values are list of action names
// that belong to that controller
def controllerActionNames = [:]
grailsApplication.controllerClasses.each { DefaultGrailsControllerClass controller ->
Class controllerClass = controller.clazz
// skip controllers in plugins
if (controllerClass.name.startsWith('com.mycompany')) {
String logicalControllerName = controller.logicalPropertyName
// get the actions defined as methods (Grails 2)
controllerClass.methods.each { Method method ->
if (method.getAnnotation(Action)) {
def actions = controllerActionNames[logicalControllerName] ?: []
actions << method.name
controllerActionNames[logicalControllerName] = actions
}
}
}
}
Grails does not support a straightforward way to do this. However, I was able to put together a puzzle from available grails methods and have come to this solution:
def actions = new HashSet<String>()
def controllerClass = grailsApplication.getArtefactInfo(ControllerArtefactHandler.TYPE)
.getGrailsClassByLogicalPropertyName(controllerName)
for (String uri : controllerClass.uris ) {
actions.add(controllerClass.getMethodActionName(uri) )
}
Variables grailsApplication and controllerName are injected by grails.
As controller itself does not have necessary methods, this code retrieves its controllerClass (see GrailsControllerClass), which has what we need: property uris and method getMethodActionName
To print out a list of all the methods with action names:
grailsApplication.controllerClasses.each {
it.getURIs().each {uri ->
println "${it.logicalPropertyName}.${it.getMethodActionName(uri)}"
}
}
I had to pull a list of all controllers and their respective URI. This is what I did on a grails 3.1.6 application.
grailsApplication.controllerClasses.each { controllerArtefact ->
def controllerClass = controllerArtefact.getClazz()
def actions = controllerArtefact.getActions()
actions?.each{action->
def controllerArtefactString = controllerArtefact.toString()
def controllerOnly = controllerArtefactString.split('Artefact > ')[1]
println "$controllerOnly >>>> $controllerOnly/${action.toString()}"
}
}

Resources