Is there a way to group domain object constraints? Something like this:
static constraints = {
personalDetails {
firstName(nullable: false)
}
address {
street(nullable: false)
}
}
Rich domain plugin does this for NON-domain objects... I want to do this FOR domain objects.
As i know this format does not supported by Grails. May be shared constraints will help you.
Related
I've created a custom constraint (most code copied from NullableConstraint) that I'd like to use in my project.
How do I register this new constraint so I can use it in my domain classes and command objects? I've tried following this answer but that didn't work. I also tried copying the code from here but that didn't work either.
In your Config.groovy add the follow:
grails.gorm.default.constraints = {
validMobilePhone(validator: { value, obj, errors ->
if (!value) return
value = PhoneNumberUtils.sanitizeNumber(value)
if (!PhoneNumberUtils.validateMobilePhone(value)) {
errors.rejectValue('mobilePhone', "invalid")
}
})
nonNegative(min: new BigDecimal(0))
}
And then use this constraint in your domain:
static constraints = {
value shared: 'nonNegative'
}
I set up a few custom Constraint classes in a Grails 3 project and carried them forward to Grails 4.
In my Application class, I register the custom constraints to 2 constraint registries. There are a few methods on the Application class you can choose from to implement this. I don't know if my selection was the best.
void doWithDynamicMethods(){
//for domains
applicationContext.getBean(
org.grails.datastore.mapping.validation.ValidatorRegistry
).addConstraintFactory( someConstraintFactory )
//for command objects
(
(DefaultConstraintEvaluator)
applicationContext.getBean(
org.grails.datastore.gorm.validation.constraints.eval.ConstraintsEvaluator
)
).constraintRegistry.addConstraintFactory( someConstraintFactory )
}
I am developing my iOs App and I am using Realm database. As I am totally new to ios developing (also swift and xcode) I have question about structuring data (I've already read some general project structure guidelines but couldn't find the answer). My thinking is connected with Java structures
For Realm databases (RealmSiwft) I created a model like this:
#objcMembers class Patient: Object {
dynamic var patientId:Int = 0
dynamic var refNumber:String = ""
convenience init(id:Int, refNumber:String){
self.init()
self.patinetID = id
self.refNumber = refNumber
}
}
Now, it looks just like a POJO class in Java. But as I learned, this model structure is made that way so it can be able to use Realm.
So the question is, if I need somewhere else in my project to use Patient objects, is this Realm-POJO-model good to use? I mean, should I use it just like a normal Model even when I dont need to make database operations on it? Or should I make this Realm model alike DAO class for databases operations and make another model class like Patient.swift for whenever I want to play with Patient without using databases (I hope not, cause it's so much code duplicating)
And what if I need variables in that Patient Model that won't be stored in database? Can I make it without dynamic? What about init than? That blows my mind, as far as I learn swift it seems so ugly and unstructured, or I just can't switch to it yet...
if I need somewhere else in my project to use Patient objects, is this
Realm-POJO-model good to use?
even when I dont need to make database operations on it?
You can use your Patient object without savings to the DB, move them to different controllers and so on.
what if I need variables in that Patient Model that won't be stored
in database?
Look to ignoredProperties() method.
Can I make it without dynamic?
No you can't because of Realm based on Objective-C object, so this is necessary type.
What about init than?
You can create different Constructors methods, look to the Initialization doc. In case with Realm you should setup values to noticed variables (if you don't give them Default Property Values)
Your class should look like this:
class Patient: Object {
// MARK: - Properties
#objc dynamic var patientId: Int = 0
#objc dynamic var refNumber: String = ""
// MARK: - Meta
// to set the model’s primary key
override class func primaryKey() -> String? {
return "patientId"
}
//Ignoring properties
override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
//It's ok
convenience init(id:Int, refNumber:String){
self.init()
self.patientId = id
self.refNumber = refNumber
}
}
All other detail information you can find in: realm docs
Also you can extend you base code with swift extension:
extension Patient {
var info: String {
return "\(patientId) " + refNumber
}
func isAvailableRefNumber() -> Bool {
return refNumber.length > 6
}
}
In order to support UTFMB8 encoding I added the following default constraint in Config.groovy
grails.gorm.default.constraints = {
'*'(maxSize: 191)
unlimitedSize(maxSize: Integer.MAX_VALUE)
}
I also added the unlimitedSize shared constraint which I use in some of my domain classes to override this default, e.g.
class BlogPost {
String body
static constraints = {
body shared: 'unlimitedSize'
}
}
However, there are a couple of classes in plugins that also need to override the default maxSize of 191. In these cases, I can't use the shared constraint, because I can't edit the source code. One option is to copy the classes into my application, and edit the copies (because artifacts in an application override those in plugins), but this is not very appealing because I've then effectively forked those classes.
Is there a better way? For example, is it possible for me to add constraints to these domain classes in Bootstrap.groovy?
Constraints can be added during boostrap something like:
class BootStrap {
def grailsApplication
def init = { servletContext ->
grailsApplication.domainClasses.each { GrailsDomainClass gdc ->
Class domainClass = gdc.clazz
if (domainClass.simpleName == 'BookFromPlugin') {
def field = domainClass.declaredFields.find {
it.name == 'body' && it.type == String
}
if (field) {
domainClass.constraints.body.maxSize = Integer.MAX_VALUE
}
}
}
}
}
where BookFromPlugin is a domain class from plugin and body is a property from the domain class. This can be optimized and made applicable to more than one domain class.
Since bootstrap is the last thing that is taken care of, it should eventually override the constraint previously defined in domain class.
UPDATE:
I guess you meant domainClass.constraints is accessing the private variable from domain class, but that is not true. domain.constraints gives a map where propery name is mapped to all its constraints. This is map is taken from ConstrainedProperty which is comprised of 3 elements: class owning the constraint, property name where constraint will be applied and the type of the property.
So when we use domain.constraints.body it actually gives a list of constraints applied to body as the value for key body. Each element in the list is a ConstrainedProperty.
By calling setMaxSize() we are just adding another ConstrainedProperty to the list of constraints.
I would like to create a custom simple constraint (like display and editable) which I can use within my Domain class. Is it possible to extend ConstrainedProperty class?
class City {
String title
BigDecimal latitude
BigDecimal longitude
Country country
static constraints = {
title ( blank: false, customConstraint: true ) // filter can be also be applied as attributes: [customConstraint: true]
}
}
Somebody familiar with this case?
To create your own constraint:
extend org.codehaus.groovy.grails.validation.AbstractConstraint
register the class as a constraint by calling org.codehaus.groovy.grails.validation.ConstrainedProperty.registerNewConstraint
Take a look at the grails implementation of the size constraint for an example.
Im building an example where there exists Video and Image domain models. Each have a one-to-many relationship with the Comment model as follows:
package commentstest
class Video {
static constraints = {
embeddUrl(blank:false,nullable:false,url:true)
}
String embeddUrl
static hasMany = [comments:Comment]
}
and
package commentstest
class Image {
static constraints = {
fileName(blank:false,nullable:false)
}
String fileName
static hasMany = [comments: Comment]
}
and finally the Comment class
package commentstest
class Comment {
static constraints = {
body(blank:false,nullable:false)
}
String body
static belongsTo = [image:Image, video:Video]
}
Now the problem i'm having is that when i create a 'comment' it must be able to be added to either a video OR an image. Currently, the scaffolding populates both the image and video option, and doesnt give the option to leave empty for one of those fields.
Anyone have an idea how to do this? Im sure my problems are spawning from the line:
static belongsTo = [image:Image,video:Video]
But i dont know how to specify that it must belong to one OR the other. not both.
From what I could see and understand, it's like you saystatic belongsTo = [image:Image,video:Video] is the root cause.
What you should do is add something like this to your constraints in Comment
static constraints = {
fileName(blank:false,nullable:false)
image(nullable:true)
video(nullable:true)
}
That enables you to set it to either a video or image.
Hope it hepls!