Grails - Preventing a recursive one-to-many relationship - grails

I have the following domain class in Grails:
class TreeNode {
String name
String description
static hasMany = [childNodes: TreeNode]
}
What is the most idiomatic Grails way of ensuring that an instance of TreeNode cannot have itself as a child? Can I do this as a constraint in the domain class, or should I be writing custom code in the Save action on the TreeNodeController?

Do it as a custom constraint.
static constraints = {
childNodes(validator: {value, obj, errors->
if(value.contains(obj) {
errors.rejectValue('childNodes', 'Cannot contain self')
}
}
}

The answer depends on how deep you want to check within the children. If you are only worried about the immediate children, then the code from #Tiggerizzy should work just fine.
If on the other hand you want to verify that the node isn't an immediate or deep child in your tree, then the logic should be pulled out of validation and placed within a Grails Service class. This would offer at least two benefits:
If other properties in the node change, but not the structure of the tree, you can skip the children check validation and save yourself the extra processing time when validating.
If you are validating from near or at the root of the tree, then verifying all of the sub-children would be a longer process for a large tree, involving a lot of database work. By doing this work in a Service class you gain the Service's transactional nature, which will roll back any database changes on an unhandled Exception such as in an optimistic lock situation with another thread.

Related

Grails dynamic "inList"

This is almost identical to this old question: Dynamic define the inList constraint using database query which was essentially unaddressed, perhaps there have been advances in the years since that was asked.
I'd like to populate the inList parameter of a domain attribute with values from another domain. Due to auto-generated views (scaffolding, filterpane), this needs to come from inList rather than a custom validator.
class MyDomain {
String someValue
static constraints = {
someValue(nullable: true, maxSize: 50, inList: SomeOtherDomain.list()*.name)
}
}
This gives the following error on startup:
Caused by: java.lang.IllegalStateException: Either class [thepackage.SomeOtherDomain] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.
I know, the "correct" way to handle this is to make someValue an instance of SomeOtherDomain instead of just storing the name, but that doesn't quite fit. We want to be able to delete an instance of SomeOtherDomain without breaking the saved value of the owning domain... the domain with the deleted value will be invalid going forward, and would have to be updated before saving, but archived/locked records will still exist and can be displayed.
You can specify value lists for filterpane like this:
<filterpane:filterPane domain="MyObject" filterPropertyValues="${['someValue':[values: SomeOtherDomain.list().collect{it.name}]]}" />
And then just use a custom validator to actually validate. I'm not sure what scaffolding might use the inList but easy enough to get around that if you're OK with replacing a few scaffolded pages with static ones.

breeze: Tracking errors with many-to-many associations

A few weeks ago, I've asked how to save Many-To-Many associations with breeze.
Ward Bell came up with this nice solution: breeze: many-to-many issues when saving
I've implemented his solution and it works really well. One issue I've come up with recently though, is how to track errors ?
Taking Ward's example, we manipulate UserRoleVm instances. Therefore validationErrorsChanged will not be triggered for this property.
How could I use breeze to raise an error if say, the parent entity does not have at least one UserRoleVm entity in its collection ?
The UserRoleVm is a regular JavaScript object. It is not a Breeze entity and so does not participate in the Breeze validation support. There is no obvious way to make it do so (at least not obvious to me). Almost anything I can dream up would be more complicated than writing traditional, view-based validation.
What kind of validation do you need? In the example that I put together, the user can only add and remove roles (the equivalent of super powers). There is no way the user can touch any value of the corresponding mapping entity (which may not even exist yet).
When I turn my imagination loose, I speculate about the rules governing how many roles the user can have or whether certain combinations of rule are allowed ... or disallowed. Is that what you mean?
If I had such rules, I'd build validation logic into the outer ViewVM (not the UserRoleVMs) ... the VM that supervises the user's actions. This logic would be quite apart from the Breeze validation logic that you register in metadata ... the validation rules implemented by Breeze inside each entity's EntityAspect.
Ultimately, I would have Breeze validations too ... probably entity validations on the parent User entity type ... so that I could guard against an actual attempt to save an invalid UserRole combination.
But such Breeze validation rules wouldn't kick in until you tried to save. While the user is working with "item VMs" (the UserRoleVms), the validation rules would be defined and implemented separately by the ViewVM in good old vanilla JavaScript.
Such is my thinking at the moment.
Following Ward's advice, I have:
-added the following code for forcing User entity's state to be modified whenever a UserRoleVM is added or removed:
$scope.user.entityAspect.setModified();
-added a custom validator for validating the UserRoles collection on the User entity:
function notEmptyCollectionValidator() {
var name = "notEmptyCollectionValidator";
var validator = new breeze.Validator(name, function (value) {
if (!value || value.length === 0) {
return false;
} else {
return true;
}
});
return validator;
}
breeze.Validator.registerFactory(notEmptyCollectionValidator, 'notEmptyCollectionValidator');
var entityType = metadataStore.getEntityType('User');
entityType.getProperty('userRoles').validators.push(breeze.config.functionRegistry['Validator.notEmptyCollectionValidator']());
Now when I hit the save button, the validation occurs on the userRoles collection. If no userRole was selected, I get a validation error and I show a * next to the control in th UI.
Obviously, that does not work for OnChange validation. I don't know yet how I'm going to achieve that.

Should removeFrom* method remove child item from a DB or it should just remove the parent-child relation?

The question is in the subj.
Currently, the behavior I observe is that when I call Parent.removeFromChilds(child), child is not removed from my DB (i.e. I can find it with Child.findBy...(args)).
I'm curious if it's the correct behavior. If yes - what's the best way to remove both relation and child within one transaction?
If no - what may I do wrong?
By default, deletes cascade if you delete the owning side of a one to many. If you just remove the child, it will not delete. You can change that by specifying the custom cascading behavior of 'all-delete-orphan' on the owning side of the relationship. From the Grails doc:
class Person {
String firstName
static hasMany = [addresses: Address]
static mapping = { addresses cascade: "all-delete-orphan" } }
Update
Part 2 of GORM Gotchas has a really good breakdown of the not-so-obvious addTo and removeFrom behavior. I think it contains the exact information you're looking for.

How can I use more than one set of constraints on a single domain class in Grails?

I need to choose a set of validation rules at save time. Ideally I would like to be able to define more than one set of constraints in the domain class:
class Account {
...
static constraints = { ... }
static constraints2 = { ... }
}
I know I can do my own validation in code (account.errors.rejectValue(...) etc) but I am looking to save some time by using the built in Grails validation if possible.
This is what Command Objects are for.
The reason you can't just swap out validation is that the validation also helps define the database structure, like setting a field to be non-null, or non-blank, or having a specific length. Turning off "non-null", for example, could break the database structure.
Command objects, on the other hand, are designed to have their own unique set of validation rules. It's not the cleanest method (now you are tracking the same structure in more than one situation), but it's better in a lot of ways.
It allows you to securely accept input parameters without worrying that something that shouldn't be processed gets set.
It creates cleaner controllers, since the validation logic can all be handled within the Command Object, and not sprinkled through the controller method.
They allow you to validate properties that don't actually exist on the object (such as password verification fields).
If you think you really need multiple sets of constraints, you are most likely either a) over-complicating the domain object to represent more information than it really should (maybe break it up into several one-to-one related objects), or b) incorrectly setting the constraints in the first place.†
In my experience, usually it's a that happens when you feel the need to swap out constraints.
† That being said, it can (rarely) make sense. But Command objects are the best solution in this situation.
you can use the validator constraint, see: http://www.grails.org/doc/latest/ref/Constraints/validator.html
within one validator constraint you can perform more checks than one and return diffrent error codes, e.g.
static constaints = {
name: validator { value, obj ->
if (value.size() > 10)
return [invalid.length]
else if (value.contains("test"))
return [invalid.content]
return true
}
in message.properties
domainClass.propName.invalid.content = ...
}

How to prevent cascade delete for bidirectional associations?

I'd like to know if it's possible to delete parent objects in a bidirectional 1:N association without a cascade delete. According to
http://grails.org/doc/1.0.x/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html
...in a unidirectional relationship the parent end gets mapped with a 'save-update' and the child end with a 'none'. I've tried these settings for the bidirectional relationship, but not very surprisingly it didn't work. Specifically:
class Personnel {
...
}
static hasMany = [projectlead:Project, projectmanager:Project]
static mappedBy = [projectlead:'leaddeveloper', projectmanager:'projectmanager']
static mapping = {
projectlead cascade:'save-update'
projectmanager cascade:'save-update'
}
And here's the project class:
class Project {
...
}
static belongsTo = [leaddeveloper:Personnel, projectmanager:Personnel]
static mapping = {
leaddeveloper cascade:'none'
projectmanager cascade:'none'
}
I'd rather not redesign my application (by removing the "belongsTo" from the Project class) if there's a solution to this: the navigational access from both ends is very convenient and I don't have too much instances on the project end.
Also, if there's a way to implement this, it would be interesting to know, what happens with those active mappings afterwards, can I set them (or will they be set) to "null" for instance?
Appreciate any inputs on this matter.
By reading your question, I think that you want to delete "Personnel" object but leaving his "projects" untouched. Sadly, this can't be done with your current implementation. A "belongsTo" relationship means that every child object must has a Parent object. If you delete the Parent, the orphan will be deleted as well.
Another option that you could try without modifying too much the design is "marking as deleted". Add a "deleted" field in Personal, and in case you want to delete someone, just mark him as deleted. This will help you keep the historical data of projects, even after the project manager left.

Resources