Search code examples
validationgrailsgroovy

Grails, Custom Validatable object for updating


I have a custom validation for my Users. It works fine but for creation but I face an error when I want to update a user. There are some fields that are not available in update section such as username, password and email and these fields shouldn't be null.

import grails.validation.Validateable

class UserCommand implements Validateable {
    Long id
    String firstName
    String lastName
    String username
    String password
    String email
    UserStatus status = UserStatus.CREATED


    static constraints = {
        firstName nullable: false, blank: false
        lastName nullable: false, blank: false
        username nullable: false, blank: false, validator: { val, obj ->
            if (obj.id) {
                if (User.countByUsernameAndIdNotEqual(val,obj.id)) {
                    return "user.already.exist"
                }
            } else {
                if (User.countByUsername(val)) {
                    return "user.already.exist"
                }
            }
        }
        password nullable: false, blank: false
        email nullable: false, blank: false, validator: { val, obj ->
            if (obj.id) {
                if (User.countByEmailAndIdNotEqual(val,obj.id)) {
                    return "user.already.exist"
                }
            } else {
                if (User.countByEmail(val)) {
                    return "user.already.exist"
                }
            }
        }
    }
}

In my controller, when I want to validate the inputs, it gives me a NullPointerException because mentioned fields shouldn't be null.

    def update(UserCommand command) {
        if(command.validate()) {
            try {
                user = userService.update(command)
            } catch (ValidationException e) {
                respond user.errors, view: 'edit'
                return
            }
        }
    }

and this is edit.gsp:


<!DOCTYPE html>
<html>
<head>
    <title>Edit information</title>
    <meta name="layout" content="main"/>
    <asset:stylesheet src="bootstrap.css" rel="stylesheet"/>
</head>
<body>
<g:form resource="${this.user}" controller="user" action="update" method="put">
    <div class="container">
        <div class="row">
            <div class="col-md-6">
                <div class="inputWithIcon">
                    <input type="text" class="custom-input" name="firstName" placeholder="First Name" value="${this.user.firstName}">
                    <i class="fa fa-id-badge fa-lg fa-fw" aria-hidden="true"></i>
                </div>
            </div>
            <div class="col-md-6">
                <div class="inputWithIcon">
                    <input type="text" class="custom-input" name="lastName" placeholder="Last Name" value="${this.user.lastName}">
                    <i class="fa fa-id-card fa-lg fa-fw" aria-hidden="true"></i>
                </div>
            </div>
        </div>

        <div class="row">
            <div class="col-md-4">
                <div class="inputWithIcon">
                    <input type="text" class="custom-input" name="country" placeholder="Country" value="${this.user.country}">
                    <i class="fa fa-globe fa-lg fa-fw" aria-hidden="true"></i>
                </div>
            </div>
            <div class="col-md-8">
                <div class="inputWithIcon">
                    <textarea  type="text" class="custom-input" name="address" placeholder="Address" rows="3">${this.user.address}</textarea>
                    <i class="fa fa-address-card fa-lg fa-fw" aria-hidden="true"></i>
                </div>
            </div>
        </div>

        <div class="row">
            <div class="col-md-12">
                <button type="submit" class="custom-button edit">Update</button>
            </div>
        </div>

    </div>
</g:form>
</div>
</body>
</html>

What is the best way to deal with Validations (maybe I don't get the concept entirely)? and how to fix this issue?

Thanks in advance!


Solution

  • I think what you may want to do is load the UserCommand in the update method, the update the fields based on parameters that come in.

    So instead of your update method, something like

    def update() {
        UserCommand command = UserCommand.get(params.id)
        command.firstName = params.firstName // and more based on whatever you can update
    
        if(command.validate()) {
            try {
                user = userService.update(command)
            } catch (ValidationException e) {
                respond user.errors, view: 'edit'
                return
            }
        }
    }
    

    You could also have all of the non-updateable fields on your edit.gsp in <input type='hidden'/> but my preference is to not send data like that across, as someone with malicious intent could still update those fields fairly easily by using DOM inspector or similar.