Mauno Vähä
Mauno Vähä

Reputation: 9788

Play framework 2: one model approach

The problem:

When programming with play framework I feel that I'm stumbling with same problem than in many times at the past which is creating several models of the same type just because I want to add, update or use different data about certain model in different use cases.

Let me explain, lets consider of a example where I have 2 different views: register and login

I would have following User model:

/**
 * Example User class (simple annotations for demo purposes).
 * 
 */
@Entity
public class User {

    @Id
    public Long id;

    @Required
    public String email;

    @Required
    public String password;

    @Required
    public String firstName;

    @Required
    public String lastName;

}

In case of registering: I would have all corresponding fields in register.scala.html : email, password, firstName, lastName - because I will need them all when I register, right?

But also I would want to use repeatPassword field to confirm that user has typed password correctly, so I would add this into User model:

@Required
@Transient
public String repeatPassword;

Ok, then I would extend this model to have repeat password confirmation check in order to correct my "automatic" validations when form is submitted, like this:

public String validate() {
if(!password.equals(repeatPassword)) {
    return "Passwords doesn't match.";
}
    return null;
}
}

So even now I would have one extra attribute repeatPassword which is not persisted to database but used within registration.

Problem #1: Our model starts to go confusing piece by piece.

In case of login: I would want to use same model because its a User which is trying to sign in, right? But instead of all the fields I would only need email, password.

Problem #2: My User model cant be used in login because its already customized to be used within registration - I would need to move repeatPassword and validate() method to separate UserRegistation model, plus duplicate firstName lastName etc. fields or mix using both User and UserRegistration model within registration and to render two different forms to same registration view = confusing.

Problem #3: My login page cant use User model because it has annotations in place, if I dont add all the necessary fields like firstName, lastName etc. I will get errors. Again, I would need to create separate UserLogin model just because I want to login to work.? Example below:

public class UserLogin {

    @Required
    public String email;

    @Required
    public String password;

    public String validate() {
        if(User.authenticate(email, password) == null) {
            return "Invalid user or password";
        }
        return null;
    }

}

So very fast, I would have 3 different models just to represent User, one of them is persisted to database and two others is used to validate errors when we are completing login and registration functionality at template side.

So my question is: How on earth I should begin to solve this mess? code complexity is rising very fast :) Should I create separate models.template and models.database packages where template models are only ones within annotations and in case of no errors I start to fill real model before saving or updating its info to database? I need desperately answers from you guys/girls, Can we make one model approach? thnx in advance.

Upvotes: 1

Views: 547

Answers (1)

biesior
biesior

Reputation: 55798

I'll start from the end: you don't need to use whole model for changing password or loggin-in (also you don't need to create separate, 'non-persisted' sub-models), although Form<YourModel> is useful while filling large objects, you can just avoid them and rely on common DynamicForm.

In such case of course it won't use constraints added with annotations to the model's fields but you can validate them manually.

For an example: in registration form you can check if @Required fields like email, firstName, lastName exists (tip: also add MinLength and MaxLength constraints), but you should remove @Required annotation from password field.

Next after checking if the form hasn't any errors you can check if password and repeatedPassword are the same and it they are identical you can also add some individual (advised) strength check - most probably it wouldn't be possible with the annotations in the model.

In case of logging form the thing is ever easier: using DynamicForm data just try to find existing User with given password if result is null that means, the user doesn't exists or password is invalid.

Finally, tip: There is ready-to-use, full-stack authentication and authorisation module available for Play 2.0 by Joscha Feth (and I'm huge advocate of this solution.)

Upvotes: 1

Related Questions