sparkyspider
sparkyspider

Reputation: 13509

Grails: GORM: Traversing Many To Many Relationships

I have 2 domain objects, User and SystemRights (It's a many to many, so 1 user can have many rights and 1 right can be owned by many users). I'm looking for a simple way to check if a user has the required rights.

User Domain

class User {

    static hasMany = [systemRights: SystemRight, enterpriseUsers: EnterpriseUser]

    String email;   
    String passwordHash;
}

SystemRight Domain

class SystemRight {

    public static final String LOGIN = "LOGIN"
    public static final String MODIFY_ALL_ENTERPRISES = "MODIFY_ALL_ENTERPRISES"
    public static final String ADMINISTER_SYSTEM = "ADMINISTER_SYSTEM"
    public static final String VALIDATE_SUPPLIER_SCORECARDS = "VALIDATE_SUPPLIER_SCORECARDS"

    static hasMany = [users:User]
    static belongsTo = User

    String name
}

The following did not work for me:

In User.class

public boolean hasRights(List<String> requiredRights) {

    def userHasRight = SystemRight.findByUserAndSystemRight (this, SystemRight.findByName(requiredRight));

    // Nor this

    def userHasRight = this.systemRights.contains(SystemRight.findByName(requiredRight));

}

Current Horrible Solution

public boolean hasRights(List<String> requiredRights) {

    for (String requiredRight : requiredRights) {

        def has = false

        for (SystemRight userRight : user.systemRights) {
            if (userRight.name == requiredRight) {
                has = true
                break;
            }
        }

        if (has == false) {
            return false;
        }            
    }

    return true        

}

Upvotes: 0

Views: 205

Answers (3)

Chris
Chris

Reputation: 8109

I would definitely try to solve this in the database.

def relevantUserRights = SystemRight.withCriteria {
    eq("user", this)
    "in"("name", requiredRights);
}

return relevantUserRights.size() == requiredRights.size()

Upvotes: 1

JB Nizet
JB Nizet

Reputation: 691635

How about the following?

public boolean hasRights(List<String> requiredRights) {
    return null != (this.systemRights.find { requiredRights.contains(it) });
}

(Not tested: Groovy newbie here)

Upvotes: 0

Gregg
Gregg

Reputation: 35864

If you're able/willing to change things up a bit, I'd highly recommend doing the following. It will make you're life so much easier.

First, remove the hasMany for SystemRight and User from both Domains and remove the belongsTo User from SystemRight.

Next, create the Domain to represent the join table.

class UserSystemRight {
   User user
   SystemRight systemRight

   boolean equals(other) {
      if (!(other instanceof UserSystemRight)) {
          return false
      }
      other.user?.id == user?.id && other.systemRight?.id == systemRight?.id
   }

   int hashCode() {
      def builder = new HashCodeBuilder()
      if (user) builder.append(user.id)
      if (systemRight) builder.append(systemRight.id)
      builder.toHashCode()
   }


   // add some more convenience methods here if you want like...
   static UserSystemRight get(long userId, long systemRightId, String systemRightName) {
       find 'from UserSystemRight where user.id=:userId and systemRight.id=:systemRightId and systemRight.name=:systemRightName',
            [userId: userId, systemRightId: systemRightId, systemRightName: systemRightName]
   }
}

Then, in your User class you can add this method:

Set<SystemRight> getSystemRights() {
    UserSystemRight.findAllByUser(this).collect { it.systemRight } as Set
}

Then, add this to your SystemRight Domain:

Set<User> getUsers() {
    UserSystemRight.findAllBySystemRight(this).collect { it.user } as Set
}

For a more detailed explenation of why this approach is full of win, aside from actually solving your problem, take a gander at this.

Upvotes: 2

Related Questions