Reputation: 14184
Coming from a Rails background I was happy to see that Grails 2.0.x supports the dynamic finders of the form Model.findOrSaveBy*
and Model.findOrCreateBy*
. However, the functionality is extremely artificially limited -- according to the documentation, the method accepts n parameters, where n is the exact number of attributes listed in the method call. So for example:
class Car {
String color
String license
static constraints = {
license blank: false, unique: true
color blank: false, inList: ["red", "black"]
}
}
/* If there exists a Car(color: red, license: "ABCDEF") */
// WORKS
Car.findOrSaveByColor("red") // will find the car
Car.findOrSaveByLicense("ABCDEF") // will find the car
Car.findOrSaveByLicenseAndColor("UVWXYZ", "black") // will persist a new car
// DOES NOT WORK
Car.findOrSaveByLicense("UVWXYZ") // will fail because the color is not provided
Car.findOrSaveByLicense("UVWXYZ", color: "black") // will fail due to an extra parameter
Car.findOrSaveByLicenseAndColor("ABCDEF", "black") // will fail due to persisting a new instance because the color does not match, which then has a unique collision
I only care about locating via the unique license
value, but if the object does not exist, I need to populate it with all required attributes. Rails allows you to do this via a Hash
parameter, as follows:
// Using findOrSaveBy* because Grails:"save"::Rails:"create" and "create" does not persist in Grails
Car.findOrSaveByLicense("ABCDEF", color: "red") // will find a car with that license plate regardless of color or persist a new entry with the license and color "red"
Is there a reason this functionality was not implemented in Grails? It seems to me this greatly limits the usefulness of these dynamic finders. I guess I could add a methodMissing
to the domain classes which intercepts the call and delegates to something like:
def car = Car.findByLicense("UVWXYZ") ?: new Car(license: "UVWXYZ", color: "red").save(flush: true)
but it just seems very repetitive. Any suggestions? Thanks.
Upvotes: 2
Views: 319
Reputation: 3070
Based on your example, an easy solution would be giving a default value to color in your domain class:
class Car {
String color = "red"
String license
static constraints = {
license blank: false, unique: true
color blank: false, inList: ["red", "black"]
}
}
Then the following should work:
Car.findOrSaveByLicense("UVWXYZ")
Upvotes: 1