简体   繁体   中英

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        

}

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 .

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()

How about the following?

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

(Not tested: Groovy newbie here)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM